Medusa Coverage Report GitHub

Files

    Files
    128
    Total Lines
    28014
    Coverage
    68.9%
    3447 / 5006 lines
    Actions
    90.0% lib/erc7540-reusable-properties/src/ERC7540Properties.sol
    Lines covered: 99 / 110 (90.0%)
    1
    // SPDX-License-Identifier: AGPL-3.0-only
    2
    pragma solidity ^0.8.21;
    3
    
                                                    
                                                
    4
    import {Asserts} from "@chimera/Asserts.sol";
    5
    
                                                    
                                                
    6
    interface IShareLike {
    7
        function balanceOf(address) external view returns (uint256);
    8
        function totalSupply() external view returns (uint256);
    9
        function decimals() external view returns (uint8);
    10
    }
    11
    
                                                    
                                                
    12
    interface IERC7540Like {
    13
        function share() external view returns (address shareTokenAddress);
    14
        function convertToShares(
    15
            uint256 assets
    16
        ) external view returns (uint256 shares);
    17
        function convertToAssets(
    18
            uint256 shares
    19
        ) external view returns (uint256 assets);
    20
        function totalAssets() external view returns (uint256 totalManagedAssets);
    21
        function maxDeposit(
    22
            address receiver
    23
        ) external view returns (uint256 maxAssets);
    24
        function previewDeposit(
    25
            uint256 assets
    26
        ) external view returns (uint256 shares);
    27
        function deposit(
    28
            uint256 assets,
    29
            address receiver
    30
        ) external returns (uint256 shares);
    31
        function maxMint(
    32
            address receiver
    33
        ) external view returns (uint256 maxShares);
    34
        function previewMint(uint256 shares) external view returns (uint256 assets);
    35
        function mint(
    36
            uint256 shares,
    37
            address receiver
    38
        ) external returns (uint256 assets);
    39
        function maxWithdraw(
    40
            address owner
    41
        ) external view returns (uint256 maxAssets);
    42
        function previewWithdraw(
    43
            uint256 assets
    44
        ) external view returns (uint256 shares);
    45
        function withdraw(
    46
            uint256 assets,
    47
            address receiver,
    48
            address owner
    49
        ) external returns (uint256 shares);
    50
        function maxRedeem(address owner) external view returns (uint256 maxShares);
    51
        function previewRedeem(
    52
            uint256 shares
    53
        ) external view returns (uint256 assets);
    54
        function redeem(
    55
            uint256 shares,
    56
            address receiver,
    57
            address owner
    58
        ) external returns (uint256 assets);
    59
    
                                                    
                                                
    60
        function requestRedeem(
    61
            uint256 shares,
    62
            address receiver,
    63
            address owner,
    64
            bytes calldata data
    65
        ) external returns (uint256 requestId);
    66
    }
    67
    
                                                    
                                                
    68
    /// @dev ERC-7540 Properties
    69
    /// @author The Recon Team
    70
    /// @notice A set of reuseable tests for your ERC7540 Vaults
    71
    ///     To get started, extend from this contract and make sure to add a way to set the actor (the current user)
    72
    ///     For more info: https://getrecon.xyz/
    73
    abstract contract ERC7540Properties is Asserts {
    74
    ✓ 3.8K
        uint256 public constant MAX_ROUNDING_ERROR = 10 ** 18;
    75
    
                                                    
                                                
    76
        address actor;
    77
    
                                                    
                                                
    78
        /// @audit TODO: You must add a way to change this!
    79
    
                                                    
                                                
    80
        /// @dev 7540-1	convertToAssets(totalSupply) == totalAssets unless price is 0.0
    81
    ✓ 112.8K
        function erc7540_1(address erc7540Target) public virtual returns (bool) {
    82
            // Doesn't hold on zero price
    83
            if (
    84
    ✓ 112.8K
                IERC7540Like(erc7540Target).convertToAssets(
    85
    ✓ 112.8K
                    10 ** IShareLike(IERC7540Like(erc7540Target).share()).decimals()
    86
    ✓ 2.3K
                ) == 0
    87
    ✓ 265
            ) return true;
    88
    
                                                    
                                                
    89
            return
    90
    ✓ 2.1K
                IERC7540Like(erc7540Target).convertToAssets(
    91
    ✓ 2.1K
                    IShareLike(IERC7540Like(erc7540Target).share()).totalSupply()
    92
    ✓ 2.1K
                ) == IERC7540Like(erc7540Target).totalAssets();
    93
        }
    94
    
                                                    
                                                
    95
        /// @dev 7540-2	convertToShares(totalAssets) == totalSupply unless price is 0.0
    96
    ✓ 150.7K
        function erc7540_2(address erc7540Target) public virtual returns (bool) {
    97
            if (
    98
    ✓ 150.7K
                IERC7540Like(erc7540Target).convertToAssets(
    99
    ✓ 150.7K
                    10 ** IShareLike(IERC7540Like(erc7540Target).share()).decimals()
    100
    ✓ 5.1K
                ) == 0
    101
    ✓ 1.3K
            ) return true;
    102
    
                                                    
                                                
    103
            // convertToShares(totalAssets) == totalSupply
    104
            return
    105
    ✓ 3.8K
                _diff(
    106
    ✓ 3.8K
                    IERC7540Like(erc7540Target).convertToShares(
    107
    ✓ 3.8K
                        IERC7540Like(erc7540Target).totalAssets()
    108
                    ),
    109
    ✓ 3.8K
                    IShareLike(IERC7540Like(erc7540Target).share()).totalSupply()
    110
                ) <= MAX_ROUNDING_ERROR;
    111
        }
    112
    
                                                    
                                                
    113
    ✓ 3.8K
        function _diff(uint256 a, uint256 b) internal pure returns (uint256) {
    114
    ✓ 3.8K
            return a > b ? a - b : b - a;
    115
        }
    116
    
                                                    
                                                
    117
        /// @dev 7540-3	max* never reverts
    118
        function erc7540_3(address erc7540Target) public virtual returns (bool) {
    119
            // max* never reverts
    120
    ✓ 111.0K
            try IERC7540Like(erc7540Target).maxDeposit(actor) {} catch {
    121
    ✓ 78.6K
                return false;
    122
            }
    123
    ✓ 3.9K
            try IERC7540Like(erc7540Target).maxMint(actor) {} catch {
    124
                return false;
    125
            }
    126
    ✓ 3.9K
            try IERC7540Like(erc7540Target).maxRedeem(actor) {} catch {
    127
    ✓ 863
                return false;
    128
            }
    129
    ✓ 3.0K
            try IERC7540Like(erc7540Target).maxWithdraw(actor) {} catch {
    130
                return false;
    131
            }
    132
    
                                                    
                                                
    133
    ✓ 3.0K
            return true;
    134
        }
    135
    
                                                    
                                                
    136
        /// == erc7540_4 == //
    137
    
                                                    
                                                
    138
        /// @dev 7540-4 claiming more than max always reverts
    139
        function erc7540_4_deposit(
    140
            address erc7540Target,
    141
            uint256 amt
    142
    ✓ 307.6K
        ) public virtual returns (bool) {
    143
            // Skip 0
    144
    ✓ 307.6K
            if (amt == 0) {
    145
    ✓ 3.8K
                return true; // Skip
    146
            }
    147
    
                                                    
                                                
    148
    ✓ 303.8K
            uint256 maxDep = IERC7540Like(erc7540Target).maxDeposit(actor);
    149
    
                                                    
                                                
    150
            /// @audit No Revert is proven by erc7540_5
    151
    
                                                    
                                                
    152
    ✓ 155.1K
            uint256 sum = maxDep + amt;
    153
    ✓ 12.6K
            if (sum == 0) {
    154
                return true; // Needs to be greater than 0, skip
    155
            }
    156
    
                                                    
                                                
    157
    ✓ 12.6K
            try IERC7540Like(erc7540Target).deposit(maxDep + amt, actor) {
    158
                return false;
    159
            } catch {
    160
                // We want this to be hit
    161
                return true; // So we explicitly return here, as a means to ensure that this is the code path
    162
            }
    163
    
                                                    
                                                
    164
            // NOTE: This code path is never hit per the above
    165
        }
    166
    
                                                    
                                                
    167
        function erc7540_4_mint(
    168
            address erc7540Target,
    169
            uint256 amt
    170
    ✓ 217.0K
        ) public virtual returns (bool) {
    171
            // Skip 0
    172
    ✓ 217.0K
            if (amt == 0) {
    173
    ✓ 3.3K
                return true;
    174
            }
    175
    
                                                    
                                                
    176
    ✓ 213.7K
            uint256 maxDep = IERC7540Like(erc7540Target).maxMint(actor);
    177
    
                                                    
                                                
    178
    ✓ 120.5K
            uint256 sum = maxDep + amt;
    179
    ✓ 4.8K
            if (sum == 0) {
    180
                return true; // Needs to be greater than 0, skip
    181
            }
    182
    
                                                    
                                                
    183
    ✓ 17.4K
            try IERC7540Like(erc7540Target).mint(maxDep + amt, actor) {
    184
                return false;
    185
            } catch {
    186
                // We want this to be hit
    187
                return true; // So we explicitly return here, as a means to ensure that this is the code path
    188
            }
    189
    
                                                    
                                                
    190
            // NOTE: This code path is never hit per the above
    191
        }
    192
    
                                                    
                                                
    193
        function erc7540_4_withdraw(
    194
            address erc7540Target,
    195
            uint256 amt
    196
    ✓ 261.8K
        ) public virtual returns (bool) {
    197
            // Skip 0
    198
    ✓ 261.8K
            if (amt == 0) {
    199
    ✓ 3.4K
                return true;
    200
            }
    201
    
                                                    
                                                
    202
    ✓ 258.4K
            uint256 maxDep = IERC7540Like(erc7540Target).maxWithdraw(actor);
    203
    
                                                    
                                                
    204
    ✓ 147.0K
            uint256 sum = maxDep + amt;
    205
    ✓ 146.9K
            if (sum == 0) {
    206
                return true; // Needs to be greater than 0
    207
            }
    208
    
                                                    
                                                
    209
    ✓ 146.9K
            try IERC7540Like(erc7540Target).withdraw(maxDep + amt, actor, actor) {
    210
                return false;
    211
            } catch {
    212
                // We want this to be hit
    213
                return true; // So we explicitly return here, as a means to ensure that this is the code path
    214
            }
    215
    
                                                    
                                                
    216
            // NOTE: This code path is never hit per the above
    217
        }
    218
    
                                                    
                                                
    219
        function erc7540_4_redeem(
    220
            address erc7540Target,
    221
            uint256 amt
    222
    ✓ 298.0K
        ) public virtual returns (bool) {
    223
            // Skip 0
    224
    ✓ 298.0K
            if (amt == 0) {
    225
    ✓ 4.9K
                return true;
    226
            }
    227
    
                                                    
                                                
    228
    ✓ 293.0K
            uint256 maxDep = IERC7540Like(erc7540Target).maxRedeem(actor);
    229
    
                                                    
                                                
    230
    ✓ 107.0K
            uint256 sum = maxDep + amt;
    231
    ✓ 107.0K
            if (sum == 0) {
    232
                return true; // Needs to be greater than 0
    233
            }
    234
    
                                                    
                                                
    235
    ✓ 253.9K
            try IERC7540Like(erc7540Target).redeem(maxDep + amt, actor, actor) {
    236
                return false;
    237
            } catch {
    238
                // We want this to be hit
    239
    ✓ 271.3K
                return true; // So we explicitly return here, as a means to ensure that this is the code path
    240
            }
    241
    
                                                    
                                                
    242
            // NOTE: This code path is never hit per the above
    243
        }
    244
    
                                                    
                                                
    245
        /// == END erc7540_4 == //
    246
    
                                                    
                                                
    247
        /// @dev 7540-5	requestRedeem reverts if the share balance is less than amount
    248
        function erc7540_5(
    249
            address erc7540Target,
    250
            address shareToken,
    251
            uint256 shares
    252
    ✓ 257.8K
        ) public virtual returns (bool) {
    253
    ✓ 257.8K
            if (shares == 0) {
    254
    ✓ 6.0K
                return true; // Skip
    255
            }
    256
    
                                                    
                                                
    257
    ✓ 251.8K
            uint256 actualBal = IShareLike(shareToken).balanceOf(actor);
    258
    ✓ 118.0K
            uint256 balWeWillUse = actualBal + shares;
    259
    
                                                    
                                                
    260
    ✓ 117.5K
            if (balWeWillUse == 0) {
    261
                return true; // Skip
    262
            }
    263
    
                                                    
                                                
    264
            try
    265
    ✓ 117.5K
                IERC7540Like(erc7540Target).requestRedeem(
    266
                    balWeWillUse,
    267
    ✓ 117.5K
                    actor,
    268
                    actor,
    269
                    ""
    270
                )
    271
            {
    272
                return false;
    273
            } catch {
    274
    ✓ 115.9K
                return true;
    275
            }
    276
    
                                                    
                                                
    277
            // NOTE: This code path is never hit per the above
    278
        }
    279
    
                                                    
                                                
    280
        /// @dev 7540-6	preview* always reverts
    281
    ✓ 99.9K
        function erc7540_6(address erc7540Target) public virtual returns (bool) {
    282
            // preview* always reverts
    283
    ✓ 99.9K
            try IERC7540Like(erc7540Target).previewDeposit(0) {
    284
    ✓ 3.1K
                return false;
    285
            } catch {}
    286
    ✓ 71.4K
            try IERC7540Like(erc7540Target).previewMint(0) {
    287
    ✓ 1.3K
                return false;
    288
            } catch {}
    289
    ✓ 70.1K
            try IERC7540Like(erc7540Target).previewRedeem(0) {
    290
                return false;
    291
            } catch {}
    292
    ✓ 70.1K
            try IERC7540Like(erc7540Target).previewWithdraw(0) {
    293
                return false;
    294
            } catch {}
    295
    
                                                    
                                                
    296
    ✓ 70.1K
            return true;
    297
        }
    298
    
                                                    
                                                
    299
        /// == erc7540_7 == //
    300
    
                                                    
                                                
    301
        /// @dev 7540-7 if max[method] > 0, then [method] (max) should not revert
    302
        function erc7540_7_deposit(
    303
            address erc7540Target,
    304
            uint256 amt
    305
    ✓ 116.7K
        ) public virtual returns (bool) {
    306
            // Per erc7540_4
    307
    ✓ 116.7K
            uint256 maxDeposit = IERC7540Like(erc7540Target).maxDeposit(actor);
    308
    ✓ 7.0K
            amt = between(amt, 0, maxDeposit);
    309
    
                                                    
                                                
    310
    ✓ 7.0K
            if (amt == 0) {
    311
    ✓ 1.4K
                return true; // Skip
    312
            }
    313
    
                                                    
                                                
    314
    ✓ 5.6K
            try IERC7540Like(erc7540Target).deposit(amt, actor) {
    315
                // Success here
    316
                return true;
    317
            } catch {
    318
                return false;
    319
            }
    320
    
                                                    
                                                
    321
            // NOTE: This code path is never hit per the above
    322
        }
    323
    
                                                    
                                                
    324
        function erc7540_7_mint(
    325
            address erc7540Target,
    326
            uint256 amt
    327
    ✓ 123.2K
        ) public virtual returns (bool) {
    328
    ✓ 123.2K
            uint256 maxMint = IERC7540Like(erc7540Target).maxMint(actor);
    329
    ✓ 7.9K
            amt = between(amt, 0, maxMint);
    330
    
                                                    
                                                
    331
    ✓ 7.9K
            if (amt == 0) {
    332
    ✓ 1.4K
                return true; // Skip
    333
            }
    334
    
                                                    
                                                
    335
    ✓ 6.5K
            try IERC7540Like(erc7540Target).mint(amt, actor) {
    336
                // Success here
    337
                return true;
    338
            } catch {
    339
                return false;
    340
            }
    341
    
                                                    
                                                
    342
            // NOTE: This code path is never hit per the above
    343
        }
    344
    
                                                    
                                                
    345
        function erc7540_7_withdraw(
    346
            address erc7540Target,
    347
            uint256 amt
    348
    ✓ 346.6K
        ) public virtual returns (bool) {
    349
    ✓ 346.6K
            uint256 maxWithdraw = IERC7540Like(erc7540Target).maxWithdraw(actor);
    350
    ✓ 168.4K
            amt = between(amt, 0, maxWithdraw);
    351
    
                                                    
                                                
    352
    ✓ 168.4K
            if (amt == 0) {
    353
    ✓ 152.2K
                return true; // Skip
    354
            }
    355
    
                                                    
                                                
    356
    ✓ 16.2K
            try IERC7540Like(erc7540Target).withdraw(amt, actor, actor) {
    357
                // Success here
    358
    ✓ 16.0K
                return true;
    359
            } catch {
    360
    ✓ 13.3K
                return false;
    361
            }
    362
    
                                                    
                                                
    363
            // NOTE: This code path is never hit per the above
    364
        }
    365
    
                                                    
                                                
    366
        function erc7540_7_redeem(
    367
            address erc7540Target,
    368
            uint256 amt
    369
    ✓ 115.5K
        ) public virtual returns (bool) {
    370
            // Per erc7540_4
    371
    ✓ 115.5K
            uint256 maxRedeem = IERC7540Like(erc7540Target).maxRedeem(actor);
    372
    ✓ 4.1K
            amt = between(amt, 0, maxRedeem);
    373
    
                                                    
                                                
    374
    ✓ 4.1K
            if (amt == 0) {
    375
    ✓ 3.2K
                return true; // Skip
    376
            }
    377
    
                                                    
                                                
    378
    ✓ 921
            try IERC7540Like(erc7540Target).redeem(amt, actor, actor) {
    379
                return true;
    380
            } catch {
    381
                return false;
    382
            }
    383
    
                                                    
                                                
    384
            // NOTE: This code path is never hit per the above
    385
        }
    386
    
                                                    
                                                
    387
        /// == END erc7540_7 == //
    388
    }
    389
    
                                                    
                                                
    88.5% lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol
    Lines covered: 23 / 26 (88.5%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
    8
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
    9
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
    10
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
    11
     *
    12
     * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
    13
     * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
    14
     * case an upgrade adds a module that needs to be initialized.
    15
     *
    16
     * For example:
    17
     *
    18
     * [.hljs-theme-light.nopadding]
    19
     * ```solidity
    20
     * contract MyToken is ERC20Upgradeable {
    21
     *     function initialize() initializer public {
    22
     *         __ERC20_init("MyToken", "MTK");
    23
     *     }
    24
     * }
    25
     *
    26
     * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
    27
     *     function initializeV2() reinitializer(2) public {
    28
     *         __ERC20Permit_init("MyToken");
    29
     *     }
    30
     * }
    31
     * ```
    32
     *
    33
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
    34
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
    35
     *
    36
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
    37
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
    38
     *
    39
     * [CAUTION]
    40
     * ====
    41
     * Avoid leaving a contract uninitialized.
    42
     *
    43
     * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
    44
     * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
    45
     * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
    46
     *
    47
     * [.hljs-theme-light.nopadding]
    48
     * ```
    49
     * /// @custom:oz-upgrades-unsafe-allow constructor
    50
     * constructor() {
    51
     *     _disableInitializers();
    52
     * }
    53
     * ```
    54
     * ====
    55
     */
    56
    abstract contract Initializable {
    57
        /**
    58
         * @dev Storage of the initializable contract.
    59
         *
    60
         * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
    61
         * when using with upgradeable contracts.
    62
         *
    63
         * @custom:storage-location erc7201:openzeppelin.storage.Initializable
    64
         */
    65
        struct InitializableStorage {
    66
            /**
    67
             * @dev Indicates that the contract has been initialized.
    68
             */
    69
            uint64 _initialized;
    70
            /**
    71
             * @dev Indicates that the contract is in the process of being initialized.
    72
             */
    73
            bool _initializing;
    74
        }
    75
    
                                                    
                                                
    76
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    77
    ✓ 1.6M
        bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
    78
    
                                                    
                                                
    79
        /**
    80
         * @dev The contract is already initialized.
    81
         */
    82
        error InvalidInitialization();
    83
    
                                                    
                                                
    84
        /**
    85
         * @dev The contract is not initializing.
    86
         */
    87
        error NotInitializing();
    88
    
                                                    
                                                
    89
        /**
    90
         * @dev Triggered when the contract has been initialized or reinitialized.
    91
         */
    92
        event Initialized(uint64 version);
    93
    
                                                    
                                                
    94
        /**
    95
         * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
    96
         * `onlyInitializing` functions can be used to initialize parent contracts.
    97
         *
    98
         * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
    99
         * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
    100
         * production.
    101
         *
    102
         * Emits an {Initialized} event.
    103
         */
    104
        modifier initializer() {
    105
            // solhint-disable-next-line var-name-mixedcase
    106
    ✓ 226.3K
            InitializableStorage storage $ = _getInitializableStorage();
    107
    
                                                    
                                                
    108
            // Cache values to avoid duplicated sloads
    109
    ✓ 226.3K
            bool isTopLevelCall = !$._initializing;
    110
    ✓ 226.3K
            uint64 initialized = $._initialized;
    111
    
                                                    
                                                
    112
            // Allowed calls:
    113
            // - initialSetup: the contract is not in the initializing state and no previous version was
    114
            //                 initialized
    115
            // - construction: the contract is initialized at version 1 (no reinitialization) and the
    116
            //                 current contract is just being deployed
    117
    ✓ 226.3K
            bool initialSetup = initialized == 0 && isTopLevelCall;
    118
    ✓ 226.3K
            bool construction = initialized == 1 && address(this).code.length == 0;
    119
    
                                                    
                                                
    120
    ✓ 226.3K
            if (!initialSetup && !construction) {
    121
                revert InvalidInitialization();
    122
            }
    123
    ✓ 226.3K
            $._initialized = 1;
    124
            if (isTopLevelCall) {
    125
    ✓ 226.3K
                $._initializing = true;
    126
            }
    127
            _;
    128
    ✓ 81.3K
            if (isTopLevelCall) {
    129
    ✓ 91.9K
                $._initializing = false;
    130
    ✓ 91.9K
                emit Initialized(1);
    131
            }
    132
        }
    133
    
                                                    
                                                
    134
        /**
    135
         * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
    136
         * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
    137
         * used to initialize parent contracts.
    138
         *
    139
         * A reinitializer may be used after the original initialization step. This is essential to configure modules that
    140
         * are added through upgrades and that require initialization.
    141
         *
    142
         * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
    143
         * cannot be nested. If one is invoked in the context of another, execution will revert.
    144
         *
    145
         * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
    146
         * a contract, executing them in the right order is up to the developer or operator.
    147
         *
    148
         * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
    149
         *
    150
         * Emits an {Initialized} event.
    151
         */
    152
        modifier reinitializer(uint64 version) {
    153
            // solhint-disable-next-line var-name-mixedcase
    154
            InitializableStorage storage $ = _getInitializableStorage();
    155
    
                                                    
                                                
    156
            if ($._initializing || $._initialized >= version) {
    157
                revert InvalidInitialization();
    158
            }
    159
            $._initialized = version;
    160
            $._initializing = true;
    161
            _;
    162
            $._initializing = false;
    163
            emit Initialized(version);
    164
        }
    165
    
                                                    
                                                
    166
        /**
    167
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
    168
         * {initializer} and {reinitializer} modifiers, directly or indirectly.
    169
         */
    170
        modifier onlyInitializing() {
    171
    ✓ 226.3K
            _checkInitializing();
    172
            _;
    173
        }
    174
    
                                                    
                                                
    175
        /**
    176
         * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
    177
         */
    178
        function _checkInitializing() internal view virtual {
    179
    ✓ 1.4M
            if (!_isInitializing()) {
    180
                revert NotInitializing();
    181
            }
    182
        }
    183
    
                                                    
                                                
    184
        /**
    185
         * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
    186
         * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
    187
         * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
    188
         * through proxies.
    189
         *
    190
         * Emits an {Initialized} event the first time it is successfully executed.
    191
         */
    192
        function _disableInitializers() internal virtual {
    193
            // solhint-disable-next-line var-name-mixedcase
    194
            InitializableStorage storage $ = _getInitializableStorage();
    195
    
                                                    
                                                
    196
    ✓ 1
            if ($._initializing) {
    197
                revert InvalidInitialization();
    198
            }
    199
    ✓ 1
            if ($._initialized != type(uint64).max) {
    200
    ✓ 1
                $._initialized = type(uint64).max;
    201
    ✓ 1
                emit Initialized(type(uint64).max);
    202
            }
    203
        }
    204
    
                                                    
                                                
    205
        /**
    206
         * @dev Returns the highest version that has been initialized. See {reinitializer}.
    207
         */
    208
        function _getInitializedVersion() internal view returns (uint64) {
    209
            return _getInitializableStorage()._initialized;
    210
        }
    211
    
                                                    
                                                
    212
        /**
    213
         * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
    214
         */
    215
    ✓ 1.4M
        function _isInitializing() internal view returns (bool) {
    216
    ✓ 1.4M
            return _getInitializableStorage()._initializing;
    217
        }
    218
    
                                                    
                                                
    219
        /**
    220
         * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
    221
         *
    222
         * NOTE: Consider following the ERC-7201 formula to derive storage locations.
    223
         */
    224
    ✓ 1.6M
        function _initializableStorageSlot() internal pure virtual returns (bytes32) {
    225
            return INITIALIZABLE_STORAGE;
    226
        }
    227
    
                                                    
                                                
    228
        /**
    229
         * @dev Returns a pointer to the storage namespace.
    230
         */
    231
        // solhint-disable-next-line var-name-mixedcase
    232
    ✓ 1.6M
        function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
    233
    ✓ 1.6M
            bytes32 slot = _initializableStorageSlot();
    234
            assembly {
    235
                $.slot := slot
    236
            }
    237
        }
    238
    }
    239
    
                                                    
                                                
    80.4% lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol
    Lines covered: 45 / 56 (80.4%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    7
    import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
    8
    import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
    9
    import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
    10
    import {Initializable} from "../../proxy/utils/Initializable.sol";
    11
    
                                                    
                                                
    12
    /**
    13
     * @dev Implementation of the {IERC20} interface.
    14
     *
    15
     * This implementation is agnostic to the way tokens are created. This means
    16
     * that a supply mechanism has to be added in a derived contract using {_mint}.
    17
     *
    18
     * TIP: For a detailed writeup see our guide
    19
     * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
    20
     * to implement supply mechanisms].
    21
     *
    22
     * The default value of {decimals} is 18. To change this, you should override
    23
     * this function so it returns a different value.
    24
     *
    25
     * We have followed general OpenZeppelin Contracts guidelines: functions revert
    26
     * instead returning `false` on failure. This behavior is nonetheless
    27
     * conventional and does not conflict with the expectations of ERC-20
    28
     * applications.
    29
     */
    30
    abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
    31
        /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
    32
        struct ERC20Storage {
    33
            mapping(address account => uint256) _balances;
    34
    
                                                    
                                                
    35
            mapping(address account => mapping(address spender => uint256)) _allowances;
    36
    
                                                    
                                                
    37
            uint256 _totalSupply;
    38
    
                                                    
                                                
    39
            string _name;
    40
            string _symbol;
    41
        }
    42
    
                                                    
                                                
    43
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
    44
        bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
    45
    
                                                    
                                                
    46
        function _getERC20Storage() private pure returns (ERC20Storage storage $) {
    47
            assembly {
    48
                $.slot := ERC20StorageLocation
    49
            }
    50
        }
    51
    
                                                    
                                                
    52
        /**
    53
         * @dev Sets the values for {name} and {symbol}.
    54
         *
    55
         * Both values are immutable: they can only be set once during construction.
    56
         */
    57
        function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
    58
    ✓ 226.3K
            __ERC20_init_unchained(name_, symbol_);
    59
        }
    60
    
                                                    
                                                
    61
        function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
    62
            ERC20Storage storage $ = _getERC20Storage();
    63
    ✓ 226.3K
            $._name = name_;
    64
    ✓ 226.3K
            $._symbol = symbol_;
    65
        }
    66
    
                                                    
                                                
    67
        /**
    68
         * @dev Returns the name of the token.
    69
         */
    70
        function name() public view virtual returns (string memory) {
    71
            ERC20Storage storage $ = _getERC20Storage();
    72
            return $._name;
    73
        }
    74
    
                                                    
                                                
    75
        /**
    76
         * @dev Returns the symbol of the token, usually a shorter version of the
    77
         * name.
    78
         */
    79
        function symbol() public view virtual returns (string memory) {
    80
            ERC20Storage storage $ = _getERC20Storage();
    81
            return $._symbol;
    82
        }
    83
    
                                                    
                                                
    84
        /**
    85
         * @dev Returns the number of decimals used to get its user representation.
    86
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
    87
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
    88
         *
    89
         * Tokens usually opt for a value of 18, imitating the relationship between
    90
         * Ether and Wei. This is the default value returned by this function, unless
    91
         * it's overridden.
    92
         *
    93
         * NOTE: This information is only used for _display_ purposes: it in
    94
         * no way affects any of the arithmetic of the contract, including
    95
         * {IERC20-balanceOf} and {IERC20-transfer}.
    96
         */
    97
        function decimals() public view virtual returns (uint8) {
    98
            return 18;
    99
        }
    100
    
                                                    
                                                
    101
        /// @inheritdoc IERC20
    102
        function totalSupply() public view virtual returns (uint256) {
    103
            ERC20Storage storage $ = _getERC20Storage();
    104
    ✓ 4.8M
            return $._totalSupply;
    105
        }
    106
    
                                                    
                                                
    107
        /// @inheritdoc IERC20
    108
    ✓ 25.7M
        function balanceOf(address account) public view virtual returns (uint256) {
    109
            ERC20Storage storage $ = _getERC20Storage();
    110
    ✓ 25.7M
            return $._balances[account];
    111
        }
    112
    
                                                    
                                                
    113
        /**
    114
         * @dev See {IERC20-transfer}.
    115
         *
    116
         * Requirements:
    117
         *
    118
         * - `to` cannot be the zero address.
    119
         * - the caller must have a balance of at least `value`.
    120
         */
    121
    ✓ 118.1K
        function transfer(address to, uint256 value) public virtual returns (bool) {
    122
            address owner = _msgSender();
    123
    ✓ 118.1K
            _transfer(owner, to, value);
    124
            return true;
    125
        }
    126
    
                                                    
                                                
    127
        /// @inheritdoc IERC20
    128
    ✓ 721.5K
        function allowance(address owner, address spender) public view virtual returns (uint256) {
    129
            ERC20Storage storage $ = _getERC20Storage();
    130
    ✓ 721.5K
            return $._allowances[owner][spender];
    131
        }
    132
    
                                                    
                                                
    133
        /**
    134
         * @dev See {IERC20-approve}.
    135
         *
    136
         * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
    137
         * `transferFrom`. This is semantically equivalent to an infinite approval.
    138
         *
    139
         * Requirements:
    140
         *
    141
         * - `spender` cannot be the zero address.
    142
         */
    143
    ✓ 140.7K
        function approve(address spender, uint256 value) public virtual returns (bool) {
    144
            address owner = _msgSender();
    145
    ✓ 140.7K
            _approve(owner, spender, value);
    146
    ✓ 146.4K
            return true;
    147
        }
    148
    
                                                    
                                                
    149
        /**
    150
         * @dev See {IERC20-transferFrom}.
    151
         *
    152
         * Skips emitting an {Approval} event indicating an allowance update. This is not
    153
         * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
    154
         *
    155
         * NOTE: Does not update the allowance if the current allowance
    156
         * is the maximum `uint256`.
    157
         *
    158
         * Requirements:
    159
         *
    160
         * - `from` and `to` cannot be the zero address.
    161
         * - `from` must have a balance of at least `value`.
    162
         * - the caller must have allowance for ``from``'s tokens of at least
    163
         * `value`.
    164
         */
    165
    ✓ 721.5K
        function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
    166
            address spender = _msgSender();
    167
    ✓ 721.5K
            _spendAllowance(from, spender, value);
    168
    ✓ 571.5K
            _transfer(from, to, value);
    169
    ✓ 571.4K
            return true;
    170
        }
    171
    
                                                    
                                                
    172
        /**
    173
         * @dev Moves a `value` amount of tokens from `from` to `to`.
    174
         *
    175
         * This internal function is equivalent to {transfer}, and can be used to
    176
         * e.g. implement automatic token fees, slashing mechanisms, etc.
    177
         *
    178
         * Emits a {Transfer} event.
    179
         *
    180
         * NOTE: This function is not virtual, {_update} should be overridden instead.
    181
         */
    182
        function _transfer(address from, address to, uint256 value) internal {
    183
    ✓ 689.5K
            if (from == address(0)) {
    184
                revert ERC20InvalidSender(address(0));
    185
            }
    186
    ✓ 689.5K
            if (to == address(0)) {
    187
                revert ERC20InvalidReceiver(address(0));
    188
            }
    189
    ✓ 689.5K
            _update(from, to, value);
    190
        }
    191
    
                                                    
                                                
    192
        /**
    193
         * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
    194
         * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
    195
         * this function.
    196
         *
    197
         * Emits a {Transfer} event.
    198
         */
    199
        function _update(address from, address to, uint256 value) internal virtual {
    200
            ERC20Storage storage $ = _getERC20Storage();
    201
    ✓ 1.6M
            if (from == address(0)) {
    202
                // Overflow check required: The rest of the code assumes that totalSupply never overflows
    203
    ✓ 532.6K
                $._totalSupply += value;
    204
            } else {
    205
    ✓ 1.1M
                uint256 fromBalance = $._balances[from];
    206
    ✓ 1.1M
                if (fromBalance < value) {
    207
    ✓ 111.2K
                    revert ERC20InsufficientBalance(from, fromBalance, value);
    208
                }
    209
                unchecked {
    210
                    // Overflow not possible: value <= fromBalance <= totalSupply.
    211
    ✓ 956.3K
                    $._balances[from] = fromBalance - value;
    212
                }
    213
            }
    214
    
                                                    
                                                
    215
    ✓ 1.5M
            if (to == address(0)) {
    216
                unchecked {
    217
                    // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
    218
    ✓ 378.0K
                    $._totalSupply -= value;
    219
                }
    220
            } else {
    221
                unchecked {
    222
                    // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
    223
    ✓ 1.1M
                    $._balances[to] += value;
    224
                }
    225
            }
    226
    
                                                    
                                                
    227
    ✓ 1.5M
            emit Transfer(from, to, value);
    228
        }
    229
    
                                                    
                                                
    230
        /**
    231
         * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
    232
         * Relies on the `_update` mechanism
    233
         *
    234
         * Emits a {Transfer} event with `from` set to the zero address.
    235
         *
    236
         * NOTE: This function is not virtual, {_update} should be overridden instead.
    237
         */
    238
        function _mint(address account, uint256 value) internal {
    239
    ✓ 532.6K
            if (account == address(0)) {
    240
                revert ERC20InvalidReceiver(address(0));
    241
            }
    242
    ✓ 532.6K
            _update(address(0), account, value);
    243
        }
    244
    
                                                    
                                                
    245
        /**
    246
         * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
    247
         * Relies on the `_update` mechanism.
    248
         *
    249
         * Emits a {Transfer} event with `to` set to the zero address.
    250
         *
    251
         * NOTE: This function is not virtual, {_update} should be overridden instead
    252
         */
    253
        function _burn(address account, uint256 value) internal {
    254
    ✓ 378.0K
            if (account == address(0)) {
    255
                revert ERC20InvalidSender(address(0));
    256
            }
    257
    ✓ 378.0K
            _update(account, address(0), value);
    258
        }
    259
    
                                                    
                                                
    260
        /**
    261
         * @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
    262
         *
    263
         * This internal function is equivalent to `approve`, and can be used to
    264
         * e.g. set automatic allowances for certain subsystems, etc.
    265
         *
    266
         * Emits an {Approval} event.
    267
         *
    268
         * Requirements:
    269
         *
    270
         * - `owner` cannot be the zero address.
    271
         * - `spender` cannot be the zero address.
    272
         *
    273
         * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
    274
         */
    275
        function _approve(address owner, address spender, uint256 value) internal {
    276
    ✓ 709.8K
            _approve(owner, spender, value, true);
    277
        }
    278
    
                                                    
                                                
    279
        /**
    280
         * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
    281
         *
    282
         * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
    283
         * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
    284
         * `Approval` event during `transferFrom` operations.
    285
         *
    286
         * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
    287
         * true using the following override:
    288
         *
    289
         * ```solidity
    290
         * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
    291
         *     super._approve(owner, spender, value, true);
    292
         * }
    293
         * ```
    294
         *
    295
         * Requirements are the same as {_approve}.
    296
         */
    297
        function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
    298
            ERC20Storage storage $ = _getERC20Storage();
    299
    ✓ 1.3M
            if (owner == address(0)) {
    300
                revert ERC20InvalidApprover(address(0));
    301
            }
    302
    ✓ 1.3M
            if (spender == address(0)) {
    303
    ✓ 1.3K
                revert ERC20InvalidSpender(address(0));
    304
            }
    305
    ✓ 1.3M
            $._allowances[owner][spender] = value;
    306
            if (emitEvent) {
    307
    ✓ 708.5K
                emit Approval(owner, spender, value);
    308
            }
    309
        }
    310
    
                                                    
                                                
    311
        /**
    312
         * @dev Updates `owner`'s allowance for `spender` based on spent `value`.
    313
         *
    314
         * Does not update the allowance value in case of infinite allowance.
    315
         * Revert if not enough allowance is available.
    316
         *
    317
         * Does not emit an {Approval} event.
    318
         */
    319
        function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
    320
    ✓ 721.5K
            uint256 currentAllowance = allowance(owner, spender);
    321
    ✓ 721.5K
            if (currentAllowance < type(uint256).max) {
    322
    ✓ 721.5K
                if (currentAllowance < value) {
    323
    ✓ 150.1K
                    revert ERC20InsufficientAllowance(spender, currentAllowance, value);
    324
                }
    325
                unchecked {
    326
    ✓ 571.5K
                    _approve(owner, spender, currentAllowance - value, false);
    327
                }
    328
            }
    329
        }
    330
    }
    331
    
                                                    
                                                
    100.0% lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    import {Initializable} from "../proxy/utils/Initializable.sol";
    6
    
                                                    
                                                
    7
    /**
    8
     * @dev Provides information about the current execution context, including the
    9
     * sender of the transaction and its data. While these are generally available
    10
     * via msg.sender and msg.data, they should not be accessed in such a direct
    11
     * manner, since when dealing with meta-transactions the account sending and
    12
     * paying for execution may not be the actual sender (as far as an application
    13
     * is concerned).
    14
     *
    15
     * This contract is only required for intermediate, library-like contracts.
    16
     */
    17
    abstract contract ContextUpgradeable is Initializable {
    18
        function __Context_init() internal onlyInitializing {
    19
        }
    20
    
                                                    
                                                
    21
        function __Context_init_unchained() internal onlyInitializing {
    22
        }
    23
        function _msgSender() internal view virtual returns (address) {
    24
    ✓ 721.5K
            return msg.sender;
    25
        }
    26
    
                                                    
                                                
    27
        function _msgData() internal view virtual returns (bytes calldata) {
    28
            return msg.data;
    29
        }
    30
    
                                                    
                                                
    31
        function _contextSuffixLength() internal view virtual returns (uint256) {
    32
            return 0;
    33
        }
    34
    }
    35
    
                                                    
                                                
    90.0% lib/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol
    Lines covered: 9 / 10 (90.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    import {Initializable} from "../proxy/utils/Initializable.sol";
    6
    
                                                    
                                                
    7
    /**
    8
     * @dev Contract module that helps prevent reentrant calls to a function.
    9
     *
    10
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
    11
     * available, which can be applied to functions to make sure there are no nested
    12
     * (reentrant) calls to them.
    13
     *
    14
     * Note that because there is a single `nonReentrant` guard, functions marked as
    15
     * `nonReentrant` may not call one another. This can be worked around by making
    16
     * those functions `private`, and then adding `external` `nonReentrant` entry
    17
     * points to them.
    18
     *
    19
     * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
    20
     * consider using {ReentrancyGuardTransient} instead.
    21
     *
    22
     * TIP: If you would like to learn more about reentrancy and alternative ways
    23
     * to protect against it, check out our blog post
    24
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
    25
     */
    26
    abstract contract ReentrancyGuardUpgradeable is Initializable {
    27
        // Booleans are more expensive than uint256 or any type that takes up a full
    28
        // word because each write operation emits an extra SLOAD to first read the
    29
        // slot's contents, replace the bits taken up by the boolean, and then write
    30
        // back. This is the compiler's defense against contract upgrades and
    31
        // pointer aliasing, and it cannot be disabled.
    32
    
                                                    
                                                
    33
        // The values being non-zero value makes deployment a bit more expensive,
    34
        // but in exchange the refund on every call to nonReentrant will be lower in
    35
        // amount. Since refunds are capped to a percentage of the total
    36
        // transaction's gas, it is best to keep them low in cases like this one, to
    37
        // increase the likelihood of the full refund coming into effect.
    38
    ✓ 378.0K
        uint256 private constant NOT_ENTERED = 1;
    39
    ✓ 2.6M
        uint256 private constant ENTERED = 2;
    40
    
                                                    
                                                
    41
        /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
    42
        struct ReentrancyGuardStorage {
    43
            uint256 _status;
    44
        }
    45
    
                                                    
                                                
    46
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    47
        bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
    48
    
                                                    
                                                
    49
        function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
    50
            assembly {
    51
                $.slot := ReentrancyGuardStorageLocation
    52
            }
    53
        }
    54
    
                                                    
                                                
    55
        /**
    56
         * @dev Unauthorized reentrant call.
    57
         */
    58
        error ReentrancyGuardReentrantCall();
    59
    
                                                    
                                                
    60
        function __ReentrancyGuard_init() internal onlyInitializing {
    61
    ✓ 226.3K
            __ReentrancyGuard_init_unchained();
    62
        }
    63
    
                                                    
                                                
    64
    ✓ 226.3K
        function __ReentrancyGuard_init_unchained() internal onlyInitializing {
    65
            ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
    66
            $._status = NOT_ENTERED;
    67
        }
    68
    
                                                    
                                                
    69
        /**
    70
         * @dev Prevents a contract from calling itself, directly or indirectly.
    71
         * Calling a `nonReentrant` function from another `nonReentrant`
    72
         * function is not supported. It is possible to prevent this from happening
    73
         * by making the `nonReentrant` function external, and making it call a
    74
         * `private` function that does the actual work.
    75
         */
    76
        modifier nonReentrant() {
    77
    ✓ 776.6K
            _nonReentrantBefore();
    78
            _;
    79
    ✓ 94.9M
            _nonReentrantAfter();
    80
        }
    81
    
                                                    
                                                
    82
        function _nonReentrantBefore() private {
    83
            ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
    84
            // On the first call to nonReentrant, _status will be NOT_ENTERED
    85
    ✓ 2.6M
            if ($._status == ENTERED) {
    86
                revert ReentrancyGuardReentrantCall();
    87
            }
    88
    
                                                    
                                                
    89
            // Any calls to nonReentrant after this point will fail
    90
    ✓ 2.6M
            $._status = ENTERED;
    91
        }
    92
    
                                                    
                                                
    93
        function _nonReentrantAfter() private {
    94
            ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
    95
            // By storing the original value once again, a refund is triggered (see
    96
            // https://eips.ethereum.org/EIPS/eip-2200)
    97
    ✓ 378.0K
            $._status = NOT_ENTERED;
    98
        }
    99
    
                                                    
                                                
    100
        /**
    101
         * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
    102
         * `nonReentrant` function in the call stack.
    103
         */
    104
        function _reentrancyGuardEntered() internal view returns (bool) {
    105
            ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
    106
            return $._status == ENTERED;
    107
        }
    108
    }
    109
    
                                                    
                                                
    11.9% lib/openzeppelin-contracts-upgradeable/contracts/utils/cryptography/EIP712Upgradeable.sol
    Lines covered: 5 / 42 (11.9%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/EIP712.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
    7
    import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";
    8
    import {Initializable} from "../../proxy/utils/Initializable.sol";
    9
    
                                                    
                                                
    10
    /**
    11
     * @dev https://eips.ethereum.org/EIPS/eip-712[EIP-712] is a standard for hashing and signing of typed structured data.
    12
     *
    13
     * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
    14
     * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
    15
     * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
    16
     * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
    17
     *
    18
     * This contract implements the EIP-712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
    19
     * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
    20
     * ({_hashTypedDataV4}).
    21
     *
    22
     * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
    23
     * the chain id to protect against replay attacks on an eventual fork of the chain.
    24
     *
    25
     * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
    26
     * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
    27
     *
    28
     * NOTE: The upgradeable version of this contract does not use an immutable cache and recomputes the domain separator
    29
     * each time {_domainSeparatorV4} is called. That is cheaper than accessing a cached version in cold storage.
    30
     */
    31
    abstract contract EIP712Upgradeable is Initializable, IERC5267 {
    32
        bytes32 private constant TYPE_HASH =
    33
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
    34
    
                                                    
                                                
    35
        /// @custom:storage-location erc7201:openzeppelin.storage.EIP712
    36
        struct EIP712Storage {
    37
            /// @custom:oz-renamed-from _HASHED_NAME
    38
            bytes32 _hashedName;
    39
            /// @custom:oz-renamed-from _HASHED_VERSION
    40
            bytes32 _hashedVersion;
    41
    
                                                    
                                                
    42
            string _name;
    43
            string _version;
    44
        }
    45
    
                                                    
                                                
    46
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.EIP712")) - 1)) & ~bytes32(uint256(0xff))
    47
        bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100;
    48
    
                                                    
                                                
    49
        function _getEIP712Storage() private pure returns (EIP712Storage storage $) {
    50
            assembly {
    51
                $.slot := EIP712StorageLocation
    52
            }
    53
        }
    54
    
                                                    
                                                
    55
        /**
    56
         * @dev Initializes the domain separator and parameter caches.
    57
         *
    58
         * The meaning of `name` and `version` is specified in
    59
         * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP-712]:
    60
         *
    61
         * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
    62
         * - `version`: the current major version of the signing domain.
    63
         *
    64
         * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
    65
         * contract upgrade].
    66
         */
    67
        function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
    68
    ✓ 226.3K
            __EIP712_init_unchained(name, version);
    69
        }
    70
    
                                                    
                                                
    71
        function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
    72
            EIP712Storage storage $ = _getEIP712Storage();
    73
    ✓ 226.3K
            $._name = name;
    74
    ✓ 226.3K
            $._version = version;
    75
    
                                                    
                                                
    76
            // Reset prior values in storage if upgrading
    77
    ✓ 226.3K
            $._hashedName = 0;
    78
    ✓ 226.3K
            $._hashedVersion = 0;
    79
        }
    80
    
                                                    
                                                
    81
        /**
    82
         * @dev Returns the domain separator for the current chain.
    83
         */
    84
        function _domainSeparatorV4() internal view returns (bytes32) {
    85
            return _buildDomainSeparator();
    86
        }
    87
    
                                                    
                                                
    88
        function _buildDomainSeparator() private view returns (bytes32) {
    89
            return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));
    90
        }
    91
    
                                                    
                                                
    92
        /**
    93
         * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
    94
         * function returns the hash of the fully encoded EIP712 message for this domain.
    95
         *
    96
         * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
    97
         *
    98
         * ```solidity
    99
         * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
    100
         *     keccak256("Mail(address to,string contents)"),
    101
         *     mailTo,
    102
         *     keccak256(bytes(mailContents))
    103
         * )));
    104
         * address signer = ECDSA.recover(digest, signature);
    105
         * ```
    106
         */
    107
        function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
    108
            return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
    109
        }
    110
    
                                                    
                                                
    111
        /// @inheritdoc IERC5267
    112
        function eip712Domain()
    113
            public
    114
            view
    115
            virtual
    116
            returns (
    117
                bytes1 fields,
    118
                string memory name,
    119
                string memory version,
    120
                uint256 chainId,
    121
                address verifyingContract,
    122
                bytes32 salt,
    123
                uint256[] memory extensions
    124
            )
    125
        {
    126
            EIP712Storage storage $ = _getEIP712Storage();
    127
            // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
    128
            // and the EIP712 domain is not reliable, as it will be missing name and version.
    129
            require($._hashedName == 0 && $._hashedVersion == 0, "EIP712: Uninitialized");
    130
    
                                                    
                                                
    131
            return (
    132
                hex"0f", // 01111
    133
                _EIP712Name(),
    134
                _EIP712Version(),
    135
                block.chainid,
    136
                address(this),
    137
                bytes32(0),
    138
                new uint256[](0)
    139
            );
    140
        }
    141
    
                                                    
                                                
    142
        /**
    143
         * @dev The name parameter for the EIP712 domain.
    144
         *
    145
         * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
    146
         * are a concern.
    147
         */
    148
        function _EIP712Name() internal view virtual returns (string memory) {
    149
            EIP712Storage storage $ = _getEIP712Storage();
    150
            return $._name;
    151
        }
    152
    
                                                    
                                                
    153
        /**
    154
         * @dev The version parameter for the EIP712 domain.
    155
         *
    156
         * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
    157
         * are a concern.
    158
         */
    159
        function _EIP712Version() internal view virtual returns (string memory) {
    160
            EIP712Storage storage $ = _getEIP712Storage();
    161
            return $._version;
    162
        }
    163
    
                                                    
                                                
    164
        /**
    165
         * @dev The hash of the name parameter for the EIP712 domain.
    166
         *
    167
         * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
    168
         */
    169
        function _EIP712NameHash() internal view returns (bytes32) {
    170
            EIP712Storage storage $ = _getEIP712Storage();
    171
            string memory name = _EIP712Name();
    172
            if (bytes(name).length > 0) {
    173
                return keccak256(bytes(name));
    174
            } else {
    175
                // If the name is empty, the contract may have been upgraded without initializing the new storage.
    176
                // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
    177
                bytes32 hashedName = $._hashedName;
    178
                if (hashedName != 0) {
    179
                    return hashedName;
    180
                } else {
    181
                    return keccak256("");
    182
                }
    183
            }
    184
        }
    185
    
                                                    
                                                
    186
        /**
    187
         * @dev The hash of the version parameter for the EIP712 domain.
    188
         *
    189
         * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
    190
         */
    191
        function _EIP712VersionHash() internal view returns (bytes32) {
    192
            EIP712Storage storage $ = _getEIP712Storage();
    193
            string memory version = _EIP712Version();
    194
            if (bytes(version).length > 0) {
    195
                return keccak256(bytes(version));
    196
            } else {
    197
                // If the version is empty, the contract may have been upgraded without initializing the new storage.
    198
                // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
    199
                bytes32 hashedVersion = $._hashedVersion;
    200
                if (hashedVersion != 0) {
    201
                    return hashedVersion;
    202
                } else {
    203
                    return keccak256("");
    204
                }
    205
            }
    206
        }
    207
    }
    208
    
                                                    
                                                
    0.0% lib/setup-helpers/lib/chimera/src/Asserts.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    abstract contract Asserts {
    5
        function gt(uint256 a, uint256 b, string memory reason) internal virtual;
    6
    
                                                    
                                                
    7
        function gte(uint256 a, uint256 b, string memory reason) internal virtual;
    8
    
                                                    
                                                
    9
        function lt(uint256 a, uint256 b, string memory reason) internal virtual;
    10
    
                                                    
                                                
    11
        function lte(uint256 a, uint256 b, string memory reason) internal virtual;
    12
    
                                                    
                                                
    13
        function eq(uint256 a, uint256 b, string memory reason) internal virtual;
    14
    
                                                    
                                                
    15
        function t(bool b, string memory reason) internal virtual;
    16
    
                                                    
                                                
    17
        function between(uint256 value, uint256 low, uint256 high) internal virtual returns (uint256);
    18
    
                                                    
                                                
    19
        function between(int256 value, int256 low, int256 high) internal virtual returns (int256);
    20
    
                                                    
                                                
    21
        function precondition(bool p) internal virtual;
    22
    }
    23
    
                                                    
                                                
    0.0% lib/setup-helpers/lib/chimera/src/BaseProperties.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseSetup} from "./BaseSetup.sol";
    5
    
                                                    
                                                
    6
    abstract contract BaseProperties is BaseSetup {}
    7
    
                                                    
                                                
    0.0% lib/setup-helpers/lib/chimera/src/BaseSetup.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    abstract contract BaseSetup {
    5
        function setup() internal virtual;
    6
    }
    7
    
                                                    
                                                
    0.0% lib/setup-helpers/lib/chimera/src/BaseTargetFunctions.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseProperties} from "./BaseProperties.sol";
    5
    import {Asserts} from "./Asserts.sol";
    6
    
                                                    
                                                
    7
    abstract contract BaseTargetFunctions is BaseProperties, Asserts {}
    8
    
                                                    
                                                
    83.3% lib/setup-helpers/lib/chimera/src/CryticAsserts.sol
    Lines covered: 15 / 18 (83.3%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {Asserts} from "./Asserts.sol";
    5
    
                                                    
                                                
    6
    contract CryticAsserts is Asserts {
    7
        event Log(string);
    8
    
                                                    
                                                
    9
        function gt(uint256 a, uint256 b, string memory reason) internal virtual override {
    10
            if (!(a > b)) {
    11
                emit Log(reason);
    12
                assert(false);
    13
            }
    14
        }
    15
    
                                                    
                                                
    16
        function gte(uint256 a, uint256 b, string memory reason) internal virtual override {
    17
    ✓ 740.1K
            if (!(a >= b)) {
    18
    ✓ 21.4K
                emit Log(reason);
    19
                assert(false);
    20
            }
    21
        }
    22
    
                                                    
                                                
    23
        function lt(uint256 a, uint256 b, string memory reason) internal virtual override {
    24
            if (!(a < b)) {
    25
                emit Log(reason);
    26
                assert(false);
    27
            }
    28
        }
    29
    
                                                    
                                                
    30
        function lte(uint256 a, uint256 b, string memory reason) internal virtual override {
    31
    ✓ 114.2K
            if (!(a <= b)) {
    32
    ✓ 20.2K
                emit Log(reason);
    33
                assert(false);
    34
            }
    35
        }
    36
    
                                                    
                                                
    37
        function eq(uint256 a, uint256 b, string memory reason) internal virtual override {
    38
    ✓ 440.4K
            if (!(a == b)) {
    39
    ✓ 14.4K
                emit Log(reason);
    40
    ✓ 56.0K
                assert(false);
    41
            }
    42
        }
    43
    
                                                    
                                                
    44
        function t(bool b, string memory reason) internal virtual override {
    45
    ✓ 1.2M
            if (!b) {
    46
    ✓ 81.2K
                emit Log(reason);
    47
    ✓ 81.2K
                assert(false);
    48
            }
    49
        }
    50
    
                                                    
                                                
    51
    ✓ 409.1K
        function between(uint256 value, uint256 low, uint256 high) internal virtual override returns (uint256) {
    52
    ✓ 409.1K
            if (value < low || value > high) {
    53
    ✓ 346.0K
                uint256 ans = low + (value % (high - low + 1));
    54
    ✓ 346.0K
                return ans;
    55
            }
    56
    ✓ 63.1K
            return value;
    57
        }
    58
    
                                                    
                                                
    59
        function between(int256 value, int256 low, int256 high) internal virtual override returns (int256) {
    60
            if (value < low || value > high) {
    61
                int256 range = high - low + 1;
    62
                int256 clamped = (value - low) % (range);
    63
                if (clamped < 0) clamped += range;
    64
                int256 ans = low + clamped;
    65
                return ans;
    66
            }
    67
            return value;
    68
        }
    69
    
                                                    
                                                
    70
        function precondition(bool p) internal virtual override {
    71
            require(p);
    72
        }
    73
    }
    74
    
                                                    
                                                
    100.0% lib/setup-helpers/lib/chimera/src/Hevm.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    // slither-disable-start shadowing-local
    5
    
                                                    
                                                
    6
    interface IHevm {
    7
        // Set block.timestamp to newTimestamp
    8
        function warp(uint256 newTimestamp) external;
    9
    
                                                    
                                                
    10
        // Set block.number to newNumber
    11
        function roll(uint256 newNumber) external;
    12
    
                                                    
                                                
    13
        // Add the condition b to the assumption base for the current branch
    14
        // This function is almost identical to require
    15
        function assume(bool b) external;
    16
    
                                                    
                                                
    17
        // Sets the eth balance of usr to amt
    18
        function deal(address usr, uint256 amt) external;
    19
    
                                                    
                                                
    20
        // Loads a storage slot from an address
    21
        function load(address where, bytes32 slot) external returns (bytes32);
    22
    
                                                    
                                                
    23
        // Stores a value to an address' storage slot
    24
        function store(address where, bytes32 slot, bytes32 value) external;
    25
    
                                                    
                                                
    26
        // Signs data (privateKey, digest) => (v, r, s)
    27
        function sign(uint256 privateKey, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);
    28
    
                                                    
                                                
    29
        // Gets address for a given private key
    30
        function addr(uint256 privateKey) external returns (address addr);
    31
    
                                                    
                                                
    32
        // Performs a foreign function call via terminal
    33
        function ffi(string[] calldata inputs) external returns (bytes memory result);
    34
    
                                                    
                                                
    35
        // Performs the next smart contract call with specified `msg.sender`
    36
        function prank(address newSender) external;
    37
    
                                                    
                                                
    38
        // Creates a new fork with the given endpoint and the latest block and returns the identifier of the fork
    39
        function createFork(string calldata urlOrAlias) external returns (uint256);
    40
    
                                                    
                                                
    41
        // Takes a fork identifier created by createFork and sets the corresponding forked state as active
    42
        function selectFork(uint256 forkId) external;
    43
    
                                                    
                                                
    44
        // Returns the identifier of the current fork
    45
        function activeFork() external returns (uint256);
    46
    
                                                    
                                                
    47
        // Labels the address in traces
    48
        function label(address addr, string calldata label) external;
    49
    
                                                    
                                                
    50
        /// Sets an address' code.
    51
        function etch(address target, bytes calldata newRuntimeBytecode) external;
    52
    }
    53
    
                                                    
                                                
    54
    ✓ 240.1K
    IHevm constant vm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
    55
    
                                                    
                                                
    56
    // slither-disable-end shadowing-local
    57
    
                                                    
                                                
    85.7% lib/setup-helpers/src/ActorManager.sol
    Lines covered: 12 / 14 (85.7%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseSetup} from "@chimera/BaseSetup.sol";
    5
    import {vm} from "@chimera/Hevm.sol";
    6
    import {EnumerableSet} from "./EnumerableSet.sol";
    7
    
                                                    
                                                
    8
    /// @dev This is the source of truth for the actors being used in the test
    9
    /// @notice No actors should be used in the suite without being added here first
    10
    abstract contract ActorManager {
    11
        using EnumerableSet for EnumerableSet.AddressSet;
    12
    
                                                    
                                                
    13
        ///@notice The current actor being used
    14
        address private _actor;
    15
    
                                                    
                                                
    16
        ///@notice The list of all actors being used
    17
        EnumerableSet.AddressSet private _actors;
    18
    
                                                    
                                                
    19
        // If the current target is address(0) then it has not been setup yet and should revert
    20
        error ActorNotSetup();
    21
        // Do not allow duplicates
    22
        error ActorExists();
    23
        // If the actor does not exist
    24
        error ActorNotAdded();
    25
        // Do not allow the default actor
    26
        error DefaultActor();
    27
    
                                                    
                                                
    28
        /// @notice address(this) is the default actor
    29
        constructor() {
    30
    ✓ 1
            _actors.add(address(this));
    31
    ✓ 1
            _actor = address(this);
    32
        }
    33
    
                                                    
                                                
    34
        /// @notice Returns the current active actor
    35
    ✓ 4.8M
        function _getActor() internal view returns (address) {
    36
    ✓ 4.8M
            return _actor;
    37
        }
    38
    
                                                    
                                                
    39
        /// @notice Returns all actors being used
    40
    ✓ 15.3M
        function _getActors() internal view returns (address[] memory) {
    41
    ✓ 15.3M
            return _actors.values();
    42
        }
    43
    
                                                    
                                                
    44
        /// @notice Adds an actor to the list of actors
    45
        function _addActor(address target) internal {
    46
    ✓ 2
            if (_actors.contains(target)) {
    47
                revert ActorExists();
    48
            }
    49
    
                                                    
                                                
    50
    ✓ 2
            if (target == address(this)) {
    51
                revert DefaultActor();
    52
            }
    53
    
                                                    
                                                
    54
    ✓ 2
            _actors.add(target);
    55
        }
    56
    
                                                    
                                                
    57
        /// @notice Removes an actor from the list of actors
    58
        function _removeActor(address target) internal {
    59
            if (!_actors.contains(target)) {
    60
                revert ActorNotAdded();
    61
            }
    62
    
                                                    
                                                
    63
            if (target == address(this)) {
    64
                revert DefaultActor();
    65
            }
    66
    
                                                    
                                                
    67
            _actors.remove(target);
    68
        }
    69
    
                                                    
                                                
    70
        /// @dev Expose this in the `TargetFunctions` contract to let the fuzzer switch actors
    71
        ///   NOTE: We revert if the entropy is greater than the number of actors, for Halmos compatibility
    72
        /// @dev This may reduce fuzzing performance if using multiple actors, if so add explicitly clamped handlers to ManagersTargets using the index of all added actors
    73
        /// @notice Switches the current actor based on the entropy
    74
        /// @param entropy The entropy to choose a random actor in the array for switching
    75
        /// @return target The new active actor
    76
    ✓ 117.0K
        function _switchActor(uint256 entropy) internal returns (address target) {
    77
    ✓ 117.0K
            target = _actors.at(entropy);
    78
    ✓ 7.6K
            _actor = target;
    79
        }
    80
    }
    81
    
                                                    
                                                
    92.9% lib/setup-helpers/src/AssetManager.sol
    Lines covered: 26 / 28 (92.9%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseSetup} from "@chimera/BaseSetup.sol";
    5
    import {vm} from "@chimera/Hevm.sol";
    6
    
                                                    
                                                
    7
    import {EnumerableSet} from "./EnumerableSet.sol";
    8
    import {MockERC20} from "./MockERC20.sol";
    9
    
                                                    
                                                
    10
    /// @dev Source of truth for the assets being used in the test
    11
    /// @notice No assets should be used in the suite without being added here first
    12
    abstract contract AssetManager {
    13
        using EnumerableSet for EnumerableSet.AddressSet;
    14
    
                                                    
                                                
    15
        /// @notice The current target for this set of variables
    16
        address private __asset;
    17
    
                                                    
                                                
    18
        /// @notice The list of all assets being used
    19
        EnumerableSet.AddressSet private _assets;
    20
    
                                                    
                                                
    21
        // If the current target is address(0) then it has not been setup yet and should revert
    22
        error NotSetup();
    23
        // Do not allow duplicates
    24
        error Exists();
    25
        // Enable only added assets
    26
        error NotAdded();
    27
    
                                                    
                                                
    28
        /// @notice Returns the current active asset
    29
    ✓ 95.1K
        function _getAsset() internal view returns (address) {
    30
    ✓ 95.1K
            if (__asset == address(0)) {
    31
                revert NotSetup();
    32
            }
    33
    
                                                    
                                                
    34
    ✓ 95.1K
            return __asset;
    35
        }
    36
    
                                                    
                                                
    37
        /// @notice Returns all assets being used
    38
    ✓ 8
        function _getAssets() internal view returns (address[] memory) {
    39
    ✓ 8
            return _assets.values();
    40
        }
    41
    
                                                    
                                                
    42
        /// @notice Creates a new asset and adds it to the list of assets
    43
        /// @param decimals The number of decimals for the asset
    44
        /// @return The address of the new asset
    45
    ✓ 115.4K
        function _newAsset(uint8 decimals) internal returns (address) {
    46
    ✓ 115.4K
            address asset_ = address(new MockERC20("Test Token", "TST", decimals)); // If names get confusing, concatenate the decimals to the name
    47
    ✓ 115.4K
            _addAsset(asset_);
    48
    ✓ 115.4K
            __asset = asset_; // sets the asset as the current asset
    49
            return asset_;
    50
        }
    51
    
                                                    
                                                
    52
        /// @notice Adds an asset to the list of assets
    53
        /// @param target The address of the asset to add
    54
        function _addAsset(address target) internal {
    55
    ✓ 115.4K
            if (_assets.contains(target)) {
    56
                revert Exists();
    57
            }
    58
    
                                                    
                                                
    59
    ✓ 115.4K
            _assets.add(target);
    60
        }
    61
    
                                                    
                                                
    62
        /// @notice Removes an asset from the list of assets
    63
        /// @param target The address of the asset to remove
    64
        function _removeAsset(address target) internal {
    65
            if (!_assets.contains(target)) {
    66
                revert NotAdded();
    67
            }
    68
    
                                                    
                                                
    69
            _assets.remove(target);
    70
        }
    71
    
                                                    
                                                
    72
        /// @notice Switches the current asset based on the entropy
    73
        ///   NOTE: We revert if the entropy is greater than the number of actors, for Halmos compatibility
    74
        /// @param entropy The entropy to choose a random asset in the array for switching
    75
        function _switchAsset(uint256 entropy) internal {
    76
    ✓ 144.4K
            address target = _assets.at(entropy);
    77
    ✓ 12.7K
            __asset = target;
    78
        }
    79
    
                                                    
                                                
    80
        /// === Approve & Mint Asset === ///
    81
    
                                                    
                                                
    82
        /// @notice Mint initial balance and approve allowances for the active asset
    83
        /// @param actorsArray The array of actors to mint the asset to
    84
        /// @param approvalArray The array of addresses to approve the asset to
    85
        /// @param amount The amount of the asset to mint
    86
        function _finalizeAssetDeployment(address[] memory actorsArray, address[] memory approvalArray, uint256 amount)
    87
            internal
    88
        {
    89
    ✓ 1
            _mintAssetToAllActors(actorsArray, amount);
    90
    ✓ 7
            for (uint256 i; i < approvalArray.length; i++) {
    91
    ✓ 6
                _approveAssetToAddressForAllActors(actorsArray, approvalArray[i]);
    92
            }
    93
        }
    94
    
                                                    
                                                
    95
        /// @notice Mint the asset to all actors
    96
        /// @param actorsArray The array of actors to mint the asset to
    97
        /// @param amount The amount of the asset to mint
    98
        function _mintAssetToAllActors(address[] memory actorsArray, uint256 amount) private {
    99
            // mint all actors
    100
    ✓ 1
            address[] memory assets = _getAssets();
    101
    ✓ 3
            for (uint256 i; i < assets.length; i++) {
    102
    ✓ 8
                for (uint256 j; j < actorsArray.length; j++) {
    103
    ✓ 6
                    vm.prank(actorsArray[j]);
    104
    ✓ 6
                    MockERC20(assets[i]).mint(actorsArray[j], amount);
    105
                }
    106
            }
    107
        }
    108
    
                                                    
                                                
    109
        /// @notice Approve the asset to all actors
    110
        /// @param actorsArray The array of actors to approve the asset from
    111
        /// @param addressToApprove The address to approve the asset to
    112
        function _approveAssetToAddressForAllActors(address[] memory actorsArray, address addressToApprove) private {
    113
            // approve to all actors
    114
    ✓ 6
            address[] memory assets = _getAssets();
    115
    ✓ 18
            for (uint256 i; i < assets.length; i++) {
    116
    ✓ 48
                for (uint256 j; j < actorsArray.length; j++) {
    117
    ✓ 36
                    vm.prank(actorsArray[j]);
    118
    ✓ 36
                    MockERC20(assets[i]).approve(addressToApprove, type(uint256).max);
    119
                }
    120
            }
    121
        }
    122
    }
    123
    
                                                    
                                                
    95.5% lib/setup-helpers/src/EnumerableSet.sol
    Lines covered: 21 / 22 (95.5%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
    3
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    4
    
                                                    
                                                
    5
    pragma solidity ^0.8.0;
    6
    
                                                    
                                                
    7
    /**
    8
     * @dev Library for managing
    9
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
    10
     * types.
    11
     *
    12
     * Sets have the following properties:
    13
     *
    14
     * - Elements are added, removed, and checked for existence in constant time
    15
     * (O(1)).
    16
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
    17
     *
    18
     * ```solidity
    19
     * contract Example {
    20
     *     // Add the library methods
    21
     *     using EnumerableSet for EnumerableSet.AddressSet;
    22
     *
    23
     *     // Declare a set state variable
    24
     *     EnumerableSet.AddressSet private mySet;
    25
     * }
    26
     * ```
    27
     *
    28
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
    29
     * and `uint256` (`UintSet`) are supported.
    30
     *
    31
     * [WARNING]
    32
     * ====
    33
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
    34
     * unusable.
    35
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
    36
     *
    37
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
    38
     * array of EnumerableSet.
    39
     * ====
    40
     */
    41
    ✓ 37.1K
    ⟲ 37.1K
    library EnumerableSet {
    42
        // To implement this library for multiple types with as little code
    43
        // repetition as possible, we write it in terms of a generic Set type with
    44
        // bytes32 values.
    45
        // The Set implementation uses private functions, and user-facing
    46
        // implementations (such as AddressSet) are just wrappers around the
    47
        // underlying Set.
    48
        // This means that we can only create new EnumerableSets for types that fit
    49
        // in bytes32.
    50
    
                                                    
                                                
    51
        struct Set {
    52
            // Storage of set values
    53
            bytes32[] _values;
    54
            // Position of the value in the `values` array, plus 1 because index 0
    55
            // means a value is not in the set.
    56
            mapping(bytes32 => uint256) _indexes;
    57
        }
    58
    
                                                    
                                                
    59
        /**
    60
         * @dev Add a value to a set. O(1).
    61
         *
    62
         * Returns true if the value was added to the set, that is if it was not
    63
         * already present.
    64
         */
    65
    ✓ 230.6K
        function _add(Set storage set, bytes32 value) private returns (bool) {
    66
            if (!_contains(set, value)) {
    67
    ✓ 230.6K
                set._values.push(value);
    68
                // The value is stored at length-1, but we add 1 to all indexes
    69
                // and use 0 as a sentinel value
    70
    ✓ 230.6K
                set._indexes[value] = set._values.length;
    71
    ✓ 230.6K
                return true;
    72
            } else {
    73
                return false;
    74
            }
    75
        }
    76
    
                                                    
                                                
    77
        /**
    78
         * @dev Removes a value from a set. O(1).
    79
         *
    80
         * Returns true if the value was removed from the set, that is if it was
    81
         * present.
    82
         */
    83
        function _remove(Set storage set, bytes32 value) private returns (bool) {
    84
            // We read and store the value's index to prevent multiple reads from the same storage slot
    85
            uint256 valueIndex = set._indexes[value];
    86
    
                                                    
                                                
    87
            if (valueIndex != 0) {
    88
                // Equivalent to contains(set, value)
    89
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
    90
                // the array, and then remove the last element (sometimes called as 'swap and pop').
    91
                // This modifies the order of the array, as noted in {at}.
    92
    
                                                    
                                                
    93
                uint256 toDeleteIndex = valueIndex - 1;
    94
                uint256 lastIndex = set._values.length - 1;
    95
    
                                                    
                                                
    96
                if (lastIndex != toDeleteIndex) {
    97
                    bytes32 lastValue = set._values[lastIndex];
    98
    
                                                    
                                                
    99
                    // Move the last value to the index where the value to delete is
    100
                    set._values[toDeleteIndex] = lastValue;
    101
                    // Update the index for the moved value
    102
                    set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
    103
                }
    104
    
                                                    
                                                
    105
                // Delete the slot where the moved value was stored
    106
                set._values.pop();
    107
    
                                                    
                                                
    108
                // Delete the index for the deleted slot
    109
                delete set._indexes[value];
    110
    
                                                    
                                                
    111
                return true;
    112
            } else {
    113
                return false;
    114
            }
    115
        }
    116
    
                                                    
                                                
    117
        /**
    118
         * @dev Returns true if the value is in the set. O(1).
    119
         */
    120
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
    121
    ✓ 230.6K
            return set._indexes[value] != 0;
    122
        }
    123
    
                                                    
                                                
    124
        /**
    125
         * @dev Returns the number of values on the set. O(1).
    126
         */
    127
        function _length(Set storage set) private view returns (uint256) {
    128
    ✓ 648.6K
            return set._values.length;
    129
        }
    130
    
                                                    
                                                
    131
        /**
    132
         * @dev Returns the value stored at position `index` in the set. O(1).
    133
         *
    134
         * Note that there are no guarantees on the ordering of values inside the
    135
         * array, and it may change when more values are added or removed.
    136
         *
    137
         * Requirements:
    138
         *
    139
         * - `index` must be strictly less than {length}.
    140
         */
    141
    ✓ 909.9K
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
    142
    ✓ 909.9K
            return set._values[index];
    143
        }
    144
    
                                                    
                                                
    145
        /**
    146
         * @dev Return the entire set in an array
    147
         *
    148
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    149
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    150
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    151
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    152
         */
    153
    ✓ 23.0M
        function _values(Set storage set) private view returns (bytes32[] memory) {
    154
    ✓ 23.0M
            return set._values;
    155
        }
    156
    
                                                    
                                                
    157
        // Bytes32Set
    158
    
                                                    
                                                
    159
        struct Bytes32Set {
    160
            Set _inner;
    161
        }
    162
    
                                                    
                                                
    163
        /**
    164
         * @dev Add a value to a set. O(1).
    165
         *
    166
         * Returns true if the value was added to the set, that is if it was not
    167
         * already present.
    168
         */
    169
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    170
            return _add(set._inner, value);
    171
        }
    172
    
                                                    
                                                
    173
        /**
    174
         * @dev Removes a value from a set. O(1).
    175
         *
    176
         * Returns true if the value was removed from the set, that is if it was
    177
         * present.
    178
         */
    179
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    180
            return _remove(set._inner, value);
    181
        }
    182
    
                                                    
                                                
    183
        /**
    184
         * @dev Returns true if the value is in the set. O(1).
    185
         */
    186
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
    187
            return _contains(set._inner, value);
    188
        }
    189
    
                                                    
                                                
    190
        /**
    191
         * @dev Returns the number of values in the set. O(1).
    192
         */
    193
        function length(Bytes32Set storage set) internal view returns (uint256) {
    194
            return _length(set._inner);
    195
        }
    196
    
                                                    
                                                
    197
        /**
    198
         * @dev Returns the value stored at position `index` in the set. O(1).
    199
         *
    200
         * Note that there are no guarantees on the ordering of values inside the
    201
         * array, and it may change when more values are added or removed.
    202
         *
    203
         * Requirements:
    204
         *
    205
         * - `index` must be strictly less than {length}.
    206
         */
    207
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
    208
            return _at(set._inner, index);
    209
        }
    210
    
                                                    
                                                
    211
        /**
    212
         * @dev Return the entire set in an array
    213
         *
    214
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    215
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    216
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    217
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    218
         */
    219
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
    220
            bytes32[] memory store = _values(set._inner);
    221
            bytes32[] memory result;
    222
    
                                                    
                                                
    223
            /// @solidity memory-safe-assembly
    224
            assembly {
    225
                result := store
    226
            }
    227
    
                                                    
                                                
    228
            return result;
    229
        }
    230
    
                                                    
                                                
    231
        // AddressSet
    232
    
                                                    
                                                
    233
        struct AddressSet {
    234
            Set _inner;
    235
        }
    236
    
                                                    
                                                
    237
        /**
    238
         * @dev Add a value to a set. O(1).
    239
         *
    240
         * Returns true if the value was added to the set, that is if it was not
    241
         * already present.
    242
         */
    243
    ✓ 230.6K
        function add(AddressSet storage set, address value) internal returns (bool) {
    244
    ✓ 230.6K
            return _add(set._inner, bytes32(uint256(uint160(value))));
    245
        }
    246
    
                                                    
                                                
    247
        /**
    248
         * @dev Removes a value from a set. O(1).
    249
         *
    250
         * Returns true if the value was removed from the set, that is if it was
    251
         * present.
    252
         */
    253
        function remove(AddressSet storage set, address value) internal returns (bool) {
    254
            return _remove(set._inner, bytes32(uint256(uint160(value))));
    255
        }
    256
    
                                                    
                                                
    257
        /**
    258
         * @dev Returns true if the value is in the set. O(1).
    259
         */
    260
    ✓ 230.6K
        function contains(AddressSet storage set, address value) internal view returns (bool) {
    261
    ✓ 230.6K
            return _contains(set._inner, bytes32(uint256(uint160(value))));
    262
        }
    263
    
                                                    
                                                
    264
        /**
    265
         * @dev Returns the number of values in the set. O(1).
    266
         */
    267
    ✓ 648.6K
        function length(AddressSet storage set) internal view returns (uint256) {
    268
    ✓ 648.6K
            return _length(set._inner);
    269
        }
    270
    
                                                    
                                                
    271
        /**
    272
         * @dev Returns the value stored at position `index` in the set. O(1).
    273
         *
    274
         * Note that there are no guarantees on the ordering of values inside the
    275
         * array, and it may change when more values are added or removed.
    276
         *
    277
         * Requirements:
    278
         *
    279
         * - `index` must be strictly less than {length}.
    280
         */
    281
    ✓ 909.9K
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
    282
    ✓ 909.9K
            return address(uint160(uint256(_at(set._inner, index))));
    283
        }
    284
    
                                                    
                                                
    285
        /**
    286
         * @dev Return the entire set in an array
    287
         *
    288
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    289
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    290
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    291
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    292
         */
    293
    ✓ 23.0M
        function values(AddressSet storage set) internal view returns (address[] memory) {
    294
    ✓ 23.0M
            bytes32[] memory store = _values(set._inner);
    295
            address[] memory result;
    296
    
                                                    
                                                
    297
            /// @solidity memory-safe-assembly
    298
            assembly {
    299
                result := store
    300
            }
    301
    
                                                    
                                                
    302
            return result;
    303
        }
    304
    
                                                    
                                                
    305
        // UintSet
    306
    
                                                    
                                                
    307
        struct UintSet {
    308
            Set _inner;
    309
        }
    310
    
                                                    
                                                
    311
        /**
    312
         * @dev Add a value to a set. O(1).
    313
         *
    314
         * Returns true if the value was added to the set, that is if it was not
    315
         * already present.
    316
         */
    317
        function add(UintSet storage set, uint256 value) internal returns (bool) {
    318
            return _add(set._inner, bytes32(value));
    319
        }
    320
    
                                                    
                                                
    321
        /**
    322
         * @dev Removes a value from a set. O(1).
    323
         *
    324
         * Returns true if the value was removed from the set, that is if it was
    325
         * present.
    326
         */
    327
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
    328
            return _remove(set._inner, bytes32(value));
    329
        }
    330
    
                                                    
                                                
    331
        /**
    332
         * @dev Returns true if the value is in the set. O(1).
    333
         */
    334
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
    335
            return _contains(set._inner, bytes32(value));
    336
        }
    337
    
                                                    
                                                
    338
        /**
    339
         * @dev Returns the number of values in the set. O(1).
    340
         */
    341
        function length(UintSet storage set) internal view returns (uint256) {
    342
            return _length(set._inner);
    343
        }
    344
    
                                                    
                                                
    345
        /**
    346
         * @dev Returns the value stored at position `index` in the set. O(1).
    347
         *
    348
         * Note that there are no guarantees on the ordering of values inside the
    349
         * array, and it may change when more values are added or removed.
    350
         *
    351
         * Requirements:
    352
         *
    353
         * - `index` must be strictly less than {length}.
    354
         */
    355
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
    356
            return uint256(_at(set._inner, index));
    357
        }
    358
    
                                                    
                                                
    359
        /**
    360
         * @dev Return the entire set in an array
    361
         *
    362
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    363
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    364
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    365
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    366
         */
    367
        function values(UintSet storage set) internal view returns (uint256[] memory) {
    368
            bytes32[] memory store = _values(set._inner);
    369
            uint256[] memory result;
    370
    
                                                    
                                                
    371
            /// @solidity memory-safe-assembly
    372
            assembly {
    373
                result := store
    374
            }
    375
    
                                                    
                                                
    376
            return result;
    377
        }
    378
    }
    379
    
                                                    
                                                
    77.8% lib/setup-helpers/src/MockERC20.sol
    Lines covered: 49 / 63 (77.8%)
    1
    // SPDX-License-Identifier: AGPL-3.0-only
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
    5
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
    6
    /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
    7
    /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
    8
    abstract contract ERC20 {
    9
        /*//////////////////////////////////////////////////////////////
    10
                                     ERRORS
    11
        //////////////////////////////////////////////////////////////*/
    12
    
                                                    
                                                
    13
        /// @notice Thrown when attempting to transfer more tokens than available balance
    14
        error InsufficientBalance(address from, uint256 balance, uint256 amount);
    15
    
                                                    
                                                
    16
        /// @notice Thrown when attempting to transfer more tokens than allowed
    17
        error InsufficientAllowance(address owner, address spender, uint256 allowance, uint256 amount);
    18
    
                                                    
                                                
    19
        /// @notice Thrown when minting would cause overflow
    20
        error MintOverflow(uint256 currentSupply, uint256 amount);
    21
    
                                                    
                                                
    22
        /*//////////////////////////////////////////////////////////////
    23
                                     EVENTS
    24
        //////////////////////////////////////////////////////////////*/
    25
    
                                                    
                                                
    26
        event Transfer(address indexed from, address indexed to, uint256 amount);
    27
    
                                                    
                                                
    28
        event Approval(address indexed owner, address indexed spender, uint256 amount);
    29
    
                                                    
                                                
    30
        /*//////////////////////////////////////////////////////////////
    31
                                METADATA STORAGE
    32
        //////////////////////////////////////////////////////////////*/
    33
    
                                                    
                                                
    34
    ✓ 40.5M
        string public name;
    35
    
                                                    
                                                
    36
        string public symbol;
    37
    
                                                    
                                                
    38
    ✓ 189.2K
        uint8 public immutable decimals;
    39
    
                                                    
                                                
    40
        /*//////////////////////////////////////////////////////////////
    41
                                  ERC20 STORAGE
    42
        //////////////////////////////////////////////////////////////*/
    43
    
                                                    
                                                
    44
    ✓ 37.5M
        uint256 public totalSupply;
    45
    
                                                    
                                                
    46
    ✓ 37.5M
        mapping(address => uint256) public balanceOf;
    47
    
                                                    
                                                
    48
        mapping(address => mapping(address => uint256)) public allowance;
    49
    
                                                    
                                                
    50
        /*//////////////////////////////////////////////////////////////
    51
                                EIP-2612 STORAGE
    52
        //////////////////////////////////////////////////////////////*/
    53
    
                                                    
                                                
    54
        uint256 internal immutable INITIAL_CHAIN_ID;
    55
    
                                                    
                                                
    56
        bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
    57
    
                                                    
                                                
    58
        mapping(address => uint256) public nonces;
    59
    
                                                    
                                                
    60
        /*//////////////////////////////////////////////////////////////
    61
                                   CONSTRUCTOR
    62
        //////////////////////////////////////////////////////////////*/
    63
    
                                                    
                                                
    64
        constructor(string memory _name, string memory _symbol, uint8 _decimals) {
    65
    ✓ 115.4K
            name = _name;
    66
    ✓ 115.4K
            symbol = _symbol;
    67
    ✓ 115.4K
            decimals = _decimals;
    68
    
                                                    
                                                
    69
    ✓ 115.4K
            INITIAL_CHAIN_ID = block.chainid;
    70
    ✓ 115.4K
            INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    71
        }
    72
    
                                                    
                                                
    73
        /*//////////////////////////////////////////////////////////////
    74
                                   ERC20 LOGIC
    75
        //////////////////////////////////////////////////////////////*/
    76
    
                                                    
                                                
    77
    ✓ 240.9K
        function approve(address spender, uint256 amount) public virtual returns (bool) {
    78
    ✓ 240.9K
            allowance[msg.sender][spender] = amount;
    79
    
                                                    
                                                
    80
    ✓ 240.9K
            emit Approval(msg.sender, spender, amount);
    81
    
                                                    
                                                
    82
    ✓ 240.9K
            return true;
    83
        }
    84
    
                                                    
                                                
    85
    ✓ 1.8M
        function transfer(address to, uint256 amount) public virtual returns (bool) {
    86
    ✓ 1.8M
            uint256 fromBalance = balanceOf[msg.sender];
    87
    ✓ 1.8M
            if (fromBalance < amount) revert InsufficientBalance(msg.sender, fromBalance, amount);
    88
    
                                                    
                                                
    89
    ✓ 1.6M
            balanceOf[msg.sender] = fromBalance - amount;
    90
    
                                                    
                                                
    91
            // Cannot overflow because the sum of all user
    92
            // balances can't exceed the max uint256 value.
    93
            unchecked {
    94
    ✓ 1.6M
                balanceOf[to] += amount;
    95
            }
    96
    
                                                    
                                                
    97
    ✓ 1.6M
            emit Transfer(msg.sender, to, amount);
    98
    
                                                    
                                                
    99
    ✓ 1.6M
            return true;
    100
        }
    101
    
                                                    
                                                
    102
    ✓ 2.1M
        function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
    103
    ✓ 2.1M
            uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
    104
    ✓ 2.1M
            uint256 fromBalance = balanceOf[from];
    105
    
                                                    
                                                
    106
    ✓ 2.1M
            if (allowed != type(uint256).max) {
    107
    ✓ 238.4K
                if (allowed < amount) revert InsufficientAllowance(from, msg.sender, allowed, amount);
    108
    ✓ 15.3K
                allowance[from][msg.sender] = allowed - amount;
    109
            }
    110
    
                                                    
                                                
    111
    ✓ 1.9M
            if (fromBalance < amount) revert InsufficientBalance(from, fromBalance, amount);
    112
    ✓ 951.2K
            balanceOf[from] = fromBalance - amount;
    113
    
                                                    
                                                
    114
            // Cannot overflow because the sum of all user
    115
            // balances can't exceed the max uint256 value.
    116
            unchecked {
    117
    ✓ 951.2K
                balanceOf[to] += amount;
    118
            }
    119
    
                                                    
                                                
    120
    ✓ 951.2K
            emit Transfer(from, to, amount);
    121
    
                                                    
                                                
    122
    ✓ 951.2K
            return true;
    123
        }
    124
    
                                                    
                                                
    125
        /*//////////////////////////////////////////////////////////////
    126
                                 EIP-2612 LOGIC
    127
        //////////////////////////////////////////////////////////////*/
    128
    
                                                    
                                                
    129
        function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
    130
            public
    131
            virtual
    132
        {
    133
            require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
    134
    
                                                    
                                                
    135
            // Unchecked because the only math done is incrementing
    136
            // the owner's nonce which cannot realistically overflow.
    137
            unchecked {
    138
                address recoveredAddress = ecrecover(
    139
                    keccak256(
    140
                        abi.encodePacked(
    141
                            "\x19\x01",
    142
                            DOMAIN_SEPARATOR(),
    143
                            keccak256(
    144
                                abi.encode(
    145
                                    keccak256(
    146
                                        "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
    147
                                    ),
    148
                                    owner,
    149
                                    spender,
    150
                                    value,
    151
                                    nonces[owner]++,
    152
                                    deadline
    153
                                )
    154
                            )
    155
                        )
    156
                    ),
    157
                    v,
    158
                    r,
    159
                    s
    160
                );
    161
    
                                                    
                                                
    162
                require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
    163
    
                                                    
                                                
    164
                allowance[recoveredAddress][spender] = value;
    165
            }
    166
    
                                                    
                                                
    167
            emit Approval(owner, spender, value);
    168
        }
    169
    
                                                    
                                                
    170
        function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
    171
            return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    172
        }
    173
    
                                                    
                                                
    174
    ✓ 115.4K
        function computeDomainSeparator() internal view virtual returns (bytes32) {
    175
            return keccak256(
    176
                abi.encode(
    177
    ✓ 115.4K
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
    178
    ✓ 115.4K
                    keccak256(bytes(name)),
    179
    ✓ 115.4K
                    keccak256("1"),
    180
    ✓ 115.4K
                    block.chainid,
    181
    ✓ 115.4K
                    address(this)
    182
                )
    183
            );
    184
        }
    185
    
                                                    
                                                
    186
        /*//////////////////////////////////////////////////////////////
    187
                            INTERNAL MINT/BURN LOGIC
    188
        //////////////////////////////////////////////////////////////*/
    189
    
                                                    
                                                
    190
        function _mint(address to, uint256 amount) internal virtual {
    191
    ✓ 290.4K
            uint256 newTotalSupply = totalSupply + amount;
    192
    ✓ 290.4K
            if (newTotalSupply < totalSupply) revert MintOverflow(totalSupply, amount);
    193
    ✓ 290.4K
            totalSupply = newTotalSupply;
    194
    
                                                    
                                                
    195
            // Cannot overflow because the sum of all user
    196
            // balances can't exceed the max uint256 value.
    197
            unchecked {
    198
    ✓ 290.4K
                balanceOf[to] += amount;
    199
            }
    200
    
                                                    
                                                
    201
    ✓ 678.9K
            emit Transfer(address(0), to, amount);
    202
        }
    203
    
                                                    
                                                
    204
        function _burn(address from, uint256 amount) internal virtual {
    205
    ✓ 407.5K
            uint256 fromBalance = balanceOf[from];
    206
    ✓ 407.5K
            if (fromBalance < amount) revert InsufficientBalance(from, fromBalance, amount);
    207
    
                                                    
                                                
    208
    ✓ 388.6K
            balanceOf[from] = fromBalance - amount;
    209
    
                                                    
                                                
    210
            // Cannot underflow because a user's balance
    211
            // will never be larger than the total supply.
    212
            unchecked {
    213
    ✓ 388.6K
                totalSupply -= amount;
    214
            }
    215
    
                                                    
                                                
    216
    ✓ 388.6K
            emit Transfer(from, address(0), amount);
    217
        }
    218
    }
    219
    
                                                    
                                                
    220
    contract MockERC20 is ERC20 {
    221
    ✓ 115.4K
        constructor(string memory _name, string memory _symbol, uint8 _decimals) ERC20(_name, _symbol, _decimals) {}
    222
    
                                                    
                                                
    223
        function mint(address to, uint256 value) public virtual {
    224
    ✓ 173.7K
            _mint(to, value);
    225
        }
    226
    
                                                    
                                                
    227
        function burn(address from, uint256 value) public virtual {
    228
            _burn(from, value);
    229
        }
    230
    }
    231
    
                                                    
                                                
    100.0% lib/setup-helpers/src/Panic.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    ✓ 18.4K
    ⟲ 18.4K
    library Panic {
    5
        // compiler panics
    6
        string constant assertionPanic = "Panic(1)";
    7
        string constant arithmeticPanic = "Panic(17)";
    8
        string constant divisionPanic = "Panic(18)";
    9
        string constant enumPanic = "Panic(33)";
    10
        string constant arrayPanic = "Panic(34)";
    11
        string constant emptyArrayPanic = "Panic(49)";
    12
        string constant outOfBoundsPanic = "Panic(50)";
    13
        string constant memoryPanic = "Panic(65)";
    14
        string constant functionPanic = "Panic(81)";
    15
    }
    16
    
                                                    
                                                
    62.3% lib/setup-helpers/src/Utils.sol
    Lines covered: 33 / 53 (62.3%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {Panic} from "./Panic.sol";
    5
    
                                                    
                                                
    6
    contract Utils {
    7
        /// @dev check if the error returned from a call is the same as the expected error
    8
        /// @param err the error returned from a call
    9
        /// @param expected the expected error
    10
        /// @return true if the error is the same as the expected error, false otherwise
    11
    ✓ 525.7K
        function checkError(bytes memory err, string memory expected) internal pure returns (bool) {
    12
    ✓ 525.7K
            (string memory revertMsg, bool customError) = _getRevertMsg(err);
    13
    
                                                    
                                                
    14
    ✓ 525.7K
            bytes32 errorBytes;
    15
    ✓ 525.7K
            bytes32 expectedBytes;
    16
    
                                                    
                                                
    17
    ✓ 525.7K
            if (customError) {
    18
                // Custom error returns the keccak256 hash of the error, so don't need to hash it again
    19
    ✓ 522.9K
                errorBytes = bytes32(abi.encodePacked(revertMsg, bytes28(0)));
    20
    ✓ 522.9K
                expectedBytes = bytes4(keccak256(abi.encodePacked(expected)));
    21
            } else {
    22
    ✓ 2.8K
                errorBytes = keccak256(abi.encodePacked(revertMsg));
    23
    ✓ 2.8K
                expectedBytes = keccak256(abi.encodePacked(expected));
    24
            }
    25
    
                                                    
                                                
    26
            // Check if error contains expected string
    27
    ✓ 525.7K
            return errorBytes == expectedBytes;
    28
        }
    29
    
                                                    
                                                
    30
        /// @dev get the revert message from a call
    31
        /// @notice based on https://ethereum.stackexchange.com/a/83577
    32
        /// @param returnData the return data from a call
    33
        /// @return the revert message and a boolean indicating if it's a custom error
    34
    ✓ 525.7K
        function _getRevertMsg(bytes memory returnData) internal pure returns (string memory, bool) {
    35
            // If the returnData length is 0, then the transaction failed silently (without a revert message)
    36
    ✓ 525.7K
            if (returnData.length == 0) return ("", false);
    37
    
                                                    
                                                
    38
            // 1. Panic(uint256)
    39
            // Check that the data has the right size: 4 bytes for signature + 32 bytes for panic code
    40
    ✓ 525.7K
            if (returnData.length == 4 + 32) {
    41
                // Check that the data starts with the Panic signature
    42
    ✓ 2.8K
                bool panic = _checkIfPanic(returnData);
    43
    
                                                    
                                                
    44
    ✓ 2.8K
                if (panic) {
    45
    ✓ 2.8K
                    return _getPanicCode(returnData);
    46
                }
    47
            }
    48
    
                                                    
                                                
    49
            // Get the error selector from returnData
    50
    ✓ 522.9K
            bytes4 errorSelector = _getErrorSelector(returnData);
    51
    
                                                    
                                                
    52
            // 2. Error(string) - If it's a standard revert string
    53
    ✓ 522.9K
            bytes4 errorStringSelector = bytes4(keccak256("Error(string)")); // Get the standard Error(string) selector
    54
    
                                                    
                                                
    55
    ✓ 522.9K
            if (errorSelector == errorStringSelector) {
    56
                assembly {
    57
                    // slice the sighash of the error so we can decode the string
    58
                    returnData := add(returnData, 0x04)
    59
                }
    60
                return (abi.decode(returnData, (string)), false);
    61
            }
    62
    
                                                    
                                                
    63
            // 3. Custom error - Return the custom error selector as a string
    64
    ✓ 522.9K
            return (string(abi.encodePacked(errorSelector)), true);
    65
        }
    66
    
                                                    
                                                
    67
    ✓ 2.8K
        function _checkIfPanic(bytes memory returnData) internal pure returns (bool) {
    68
    ✓ 2.8K
            bytes4 panicSignature = bytes4(keccak256(bytes("Panic(uint256)")));
    69
    
                                                    
                                                
    70
    ✓ 13.9K
            for (uint256 i = 0; i < 4; i++) {
    71
    ✓ 11.1K
                if (returnData[i] != panicSignature[i]) {
    72
                    return false;
    73
                }
    74
            }
    75
    
                                                    
                                                
    76
    ✓ 2.8K
            return true;
    77
        }
    78
    
                                                    
                                                
    79
    ✓ 2.8K
        function _getPanicCode(bytes memory returnData) internal pure returns (string memory, bool) {
    80
            uint256 panicCode;
    81
    ✓ 92.0K
            for (uint256 i = 4; i < 36; i++) {
    82
    ✓ 89.2K
                panicCode = panicCode << 8;
    83
    ✓ 89.2K
                panicCode |= uint8(returnData[i]);
    84
            }
    85
    
                                                    
                                                
    86
            // Convert the panic code into its string representation
    87
    ✓ 2.8K
            if (panicCode == 1) {
    88
                // call assert with an argument that evaluates to false
    89
                return (Panic.assertionPanic, false);
    90
    ✓ 2.8K
            } else if (panicCode == 17) {
    91
                // arithmetic operation results in underflow or overflow
    92
    ✓ 2.8K
                return (Panic.arithmeticPanic, false);
    93
            } else if (panicCode == 18) {
    94
                // division or modulo by zero
    95
                return (Panic.divisionPanic, false);
    96
            } else if (panicCode == 33) {
    97
                // converting a value that's too big or negative into an enum type
    98
                return (Panic.enumPanic, false);
    99
            } else if (panicCode == 34) {
    100
                // access a storage byte array that is incorrectly encoded
    101
                return (Panic.arrayPanic, false);
    102
            } else if (panicCode == 49) {
    103
                // call .pop() on an empty array
    104
                return (Panic.emptyArrayPanic, false);
    105
            } else if (panicCode == 50) {
    106
                // array access out of bounds
    107
                return (Panic.outOfBoundsPanic, false);
    108
            } else if (panicCode == 65) {
    109
                // allocate too much memory or create an array that is too large
    110
                return (Panic.memoryPanic, false);
    111
            } else if (panicCode == 81) {
    112
                // call a zero-initialized variable of internal function type
    113
                return (Panic.functionPanic, false);
    114
            }
    115
    
                                                    
                                                
    116
            return ("Undefined panic code", false);
    117
        }
    118
    
                                                    
                                                
    119
        function _getErrorSelector(bytes memory returnData) internal pure returns (bytes4 errorSelector) {
    120
            assembly {
    121
    ✓ 522.9K
                errorSelector := mload(add(returnData, 0x20))
    122
            }
    123
            return errorSelector;
    124
        }
    125
    }
    126
    
                                                    
                                                
    0.0% lib/v2-core/lib/modulekit/src/accounts/common/interfaces/IERC7579Account.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity >=0.8.23 <0.9.0;
    3
    
                                                    
                                                
    4
    /* solhint-disable no-unused-import */
    5
    
                                                    
                                                
    6
    // Types
    7
    import { CallType, ExecType, ModeCode } from "../lib/ModeLib.sol";
    8
    
                                                    
                                                
    9
    // Structs
    10
    struct Execution {
    11
        address target;
    12
        uint256 value;
    13
        bytes callData;
    14
    }
    15
    
                                                    
                                                
    16
    interface IERC7579Account {
    17
        event ModuleInstalled(uint256 moduleTypeId, address module);
    18
        event ModuleUninstalled(uint256 moduleTypeId, address module);
    19
    
                                                    
                                                
    20
        /**
    21
         * @dev Executes a transaction on behalf of the account.
    22
         *         This function is intended to be called by ERC-4337 EntryPoint.sol
    23
         * @dev Ensure adequate authorization control: i.e. onlyEntryPointOrSelf
    24
         *
    25
         * @dev MSA MUST implement this function signature.
    26
         * If a mode is requested that is not supported by the Account, it MUST revert
    27
         * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
    28
         * @param executionCalldata The encoded execution call data
    29
         */
    30
        function execute(ModeCode mode, bytes calldata executionCalldata) external payable;
    31
    
                                                    
                                                
    32
        /**
    33
         * @dev Executes a transaction on behalf of the account.
    34
         *         This function is intended to be called by Executor Modules
    35
         * @dev Ensure adequate authorization control: i.e. onlyExecutorModule
    36
         *
    37
         * @dev MSA MUST implement this function signature.
    38
         * If a mode is requested that is not supported by the Account, it MUST revert
    39
         * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
    40
         * @param executionCalldata The encoded execution call data
    41
         */
    42
        function executeFromExecutor(
    43
            ModeCode mode,
    44
            bytes calldata executionCalldata
    45
        )
    46
            external
    47
            payable
    48
            returns (bytes[] memory returnData);
    49
    
                                                    
                                                
    50
        /**
    51
         * @dev ERC-1271 isValidSignature
    52
         *         This function is intended to be used to validate a smart account signature
    53
         * and may forward the call to a validator module
    54
         *
    55
         * @param hash The hash of the data that is signed
    56
         * @param data The data that is signed
    57
         */
    58
        function isValidSignature(bytes32 hash, bytes calldata data) external view returns (bytes4);
    59
    
                                                    
                                                
    60
        /**
    61
         * @dev installs a Module of a certain type on the smart account
    62
         * @dev Implement Authorization control of your chosing
    63
         * @param moduleTypeId the module type ID according the ERC-7579 spec
    64
         * @param module the module address
    65
         * @param initData arbitrary data that may be required on the module during `onInstall`
    66
         * initialization.
    67
         */
    68
        function installModule(
    69
            uint256 moduleTypeId,
    70
            address module,
    71
            bytes calldata initData
    72
        )
    73
            external
    74
            payable;
    75
    
                                                    
                                                
    76
        /**
    77
         * @dev uninstalls a Module of a certain type on the smart account
    78
         * @dev Implement Authorization control of your chosing
    79
         * @param moduleTypeId the module type ID according the ERC-7579 spec
    80
         * @param module the module address
    81
         * @param deInitData arbitrary data that may be required on the module during `onUninstall`
    82
         * de-initialization.
    83
         */
    84
        function uninstallModule(
    85
            uint256 moduleTypeId,
    86
            address module,
    87
            bytes calldata deInitData
    88
        )
    89
            external
    90
            payable;
    91
    
                                                    
                                                
    92
        /**
    93
         * Function to check if the account supports a certain CallType or ExecType (see ModeLib.sol)
    94
         * @param encodedMode the encoded mode
    95
         */
    96
        function supportsExecutionMode(ModeCode encodedMode) external view returns (bool);
    97
    
                                                    
                                                
    98
        /**
    99
         * Function to check if the account supports installation of a certain module type Id
    100
         * @param moduleTypeId the module type ID according the ERC-7579 spec
    101
         */
    102
        function supportsModule(uint256 moduleTypeId) external view returns (bool);
    103
    
                                                    
                                                
    104
        /**
    105
         * Function to check if the account has a certain module installed
    106
         * @param moduleTypeId the module type ID according the ERC-7579 spec
    107
         *      Note: keep in mind that some contracts can be multiple module types at the same time. It
    108
         *            thus may be necessary to query multiple module types
    109
         * @param module the module address
    110
         * @param additionalContext additional context data that the smart account may interpret to
    111
         *                          identifiy conditions under which the module is installed.
    112
         *                          usually this is not necessary, but for some special hooks that
    113
         *                          are stored in mappings, this param might be needed
    114
         */
    115
        function isModuleInstalled(
    116
            uint256 moduleTypeId,
    117
            address module,
    118
            bytes calldata additionalContext
    119
        )
    120
            external
    121
            view
    122
            returns (bool);
    123
    
                                                    
                                                
    124
        /**
    125
         * @dev Returns the account id of the smart account
    126
         * @return accountImplementationId the account id of the smart account
    127
         * the accountId should be structured like so:
    128
         *        "vendorname.accountname.semver"
    129
         */
    130
        function accountId() external view returns (string memory accountImplementationId);
    131
    }
    132
    
                                                    
                                                
    100.0% lib/v2-core/lib/modulekit/src/accounts/common/lib/ModeLib.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: GPL-3.0
    2
    pragma solidity >=0.8.0 <0.9.0;
    3
    
                                                    
                                                
    4
    /**
    5
     * @title ModeLib
    6
     * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat)
    7
     * To allow smart accounts to be very simple, but allow for more complex execution, A custom mode
    8
     * encoding is used.
    9
     *    Function Signature of execute function:
    10
     *           function execute(ModeCode mode, bytes calldata executionCalldata) external payable;
    11
     * This allows for a single bytes32 to be used to encode the execution mode, calltype, execType and
    12
     * context.
    13
     * NOTE: Simple Account implementations only have to scope for the most significant byte. Account  that
    14
     * implement
    15
     * more complex execution modes may use the entire bytes32.
    16
     *
    17
     * |--------------------------------------------------------------------|
    18
     * | CALLTYPE  | EXECTYPE  |   UNUSED   | ModeSelector  |  ModePayload  |
    19
     * |--------------------------------------------------------------------|
    20
     * | 1 byte    | 1 byte    |   4 bytes  | 4 bytes       |   22 bytes    |
    21
     * |--------------------------------------------------------------------|
    22
     *
    23
     * CALLTYPE: 1 byte
    24
     * CallType is used to determine how the executeCalldata paramter of the execute function has to be
    25
     * decoded.
    26
     * It can be either single, batch or delegatecall. In the future different calls could be added.
    27
     * CALLTYPE can be used by a validation module to determine how to decode <userOp.callData[36:]>.
    28
     *
    29
     * EXECTYPE: 1 byte
    30
     * ExecType is used to determine how the account should handle the execution.
    31
     * It can indicate if the execution should revert on failure or continue execution.
    32
     * In the future more execution modes may be added.
    33
     * Default Behavior (EXECTYPE = 0x00) is to revert on a single failed execution. If one execution in
    34
     * a batch fails, the entire batch is reverted
    35
     *
    36
     * UNUSED: 4 bytes
    37
     * Unused bytes are reserved for future use.
    38
     *
    39
     * ModeSelector: bytes4
    40
     * The "optional" mode selector can be used by account vendors, to implement custom behavior in
    41
     * their accounts.
    42
     * the way a ModeSelector is to be calculated is bytes4(keccak256("vendorname.featurename"))
    43
     * this is to prevent collisions between different vendors, while allowing innovation and the
    44
     * development of new features without coordination between ERC-7579 implementing accounts
    45
     *
    46
     * ModePayload: 22 bytes
    47
     * Mode payload is used to pass additional data to the smart account execution, this may be
    48
     * interpreted depending on the ModeSelector
    49
     *
    50
     * ExecutionCallData: n bytes
    51
     * single, delegatecall or batch exec abi.encoded as bytes
    52
     */
    53
    
                                                    
                                                
    54
    // Custom type for improved developer experience
    55
    type ModeCode is bytes32;
    56
    
                                                    
                                                
    57
    type CallType is bytes1;
    58
    
                                                    
                                                
    59
    type ExecType is bytes1;
    60
    
                                                    
                                                
    61
    type ModeSelector is bytes4;
    62
    
                                                    
                                                
    63
    type ModePayload is bytes22;
    64
    
                                                    
                                                
    65
    // Default CallType
    66
    CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
    67
    // Batched CallType
    68
    CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
    69
    CallType constant CALLTYPE_STATIC = CallType.wrap(0xFE);
    70
    // @dev Implementing delegatecall is OPTIONAL!
    71
    // implement delegatecall with extreme care.
    72
    CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
    73
    
                                                    
                                                
    74
    // @dev default behavior is to revert on failure
    75
    // To allow very simple accounts to use mode encoding, the default behavior is to revert on failure
    76
    // Since this is value 0x00, no additional encoding is required for simple accounts
    77
    ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
    78
    // @dev account may elect to change execution behavior. For example "try exec" / "allow fail"
    79
    ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
    80
    
                                                    
                                                
    81
    ModeSelector constant MODE_DEFAULT = ModeSelector.wrap(bytes4(0x00000000));
    82
    // Example declaration of a custom mode selector
    83
    ModeSelector constant MODE_OFFSET = ModeSelector.wrap(bytes4(keccak256("default.mode.offset")));
    84
    
                                                    
                                                
    85
    /**
    86
     * @dev ModeLib is a helper library to encode/decode ModeCodes
    87
     */
    88
    ✓ 28.5K
    ⟲ 28.5K
    library ModeLib {
    89
        function decode(ModeCode mode)
    90
            internal
    91
            pure
    92
            returns (
    93
                CallType _calltype,
    94
                ExecType _execType,
    95
                ModeSelector _modeSelector,
    96
                ModePayload _modePayload
    97
            )
    98
        {
    99
            // solhint-disable-next-line no-inline-assembly
    100
            assembly {
    101
                _calltype := mode
    102
                _execType := shl(8, mode)
    103
                _modeSelector := shl(48, mode)
    104
                _modePayload := shl(80, mode)
    105
            }
    106
        }
    107
    
                                                    
                                                
    108
        function encode(
    109
            CallType callType,
    110
            ExecType execType,
    111
            ModeSelector mode,
    112
            ModePayload payload
    113
        )
    114
            internal
    115
            pure
    116
            returns (ModeCode)
    117
        {
    118
            return ModeCode.wrap(
    119
                bytes32(
    120
                    abi.encodePacked(callType, execType, bytes4(0), ModeSelector.unwrap(mode), payload)
    121
                )
    122
            );
    123
        }
    124
    
                                                    
                                                
    125
        function encodeSimpleBatch() internal pure returns (ModeCode mode) {
    126
            mode = encode(CALLTYPE_BATCH, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
    127
        }
    128
    
                                                    
                                                
    129
        function encodeSimpleSingle() internal pure returns (ModeCode mode) {
    130
            mode = encode(CALLTYPE_SINGLE, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
    131
        }
    132
    
                                                    
                                                
    133
        function getCallType(ModeCode mode) internal pure returns (CallType calltype) {
    134
            // solhint-disable-next-line no-inline-assembly
    135
            assembly {
    136
                calltype := mode
    137
            }
    138
        }
    139
    }
    140
    
                                                    
                                                
    141
    using { eqModeSelector as == } for ModeSelector global;
    142
    using { eqCallType as == } for CallType global;
    143
    using { neqCallType as != } for CallType global;
    144
    using { eqExecType as == } for ExecType global;
    145
    
                                                    
                                                
    146
    function eqCallType(CallType a, CallType b) pure returns (bool) {
    147
        return CallType.unwrap(a) == CallType.unwrap(b);
    148
    }
    149
    
                                                    
                                                
    150
    function neqCallType(CallType a, CallType b) pure returns (bool) {
    151
        return CallType.unwrap(a) == CallType.unwrap(b);
    152
    }
    153
    
                                                    
                                                
    154
    function eqExecType(ExecType a, ExecType b) pure returns (bool) {
    155
        return ExecType.unwrap(a) == ExecType.unwrap(b);
    156
    }
    157
    
                                                    
                                                
    158
    function eqModeSelector(ModeSelector a, ModeSelector b) pure returns (bool) {
    159
        return ModeSelector.unwrap(a) == ModeSelector.unwrap(b);
    160
    }
    161
    
                                                    
                                                
    100.0% lib/v2-core/lib/modulekit/src/accounts/erc7579/lib/ExecutionLib.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity >=0.8.23 <0.9.0;
    3
    
                                                    
                                                
    4
    // Types
    5
    import { Execution } from "../../common/interfaces/IERC7579Account.sol";
    6
    
                                                    
                                                
    7
    /**
    8
     * Helper Library for decoding Execution calldata
    9
     * malloc for memory allocation is bad for gas. use this assembly instead
    10
     */
    11
    ✓ 19.7K
    ⟲ 19.7K
    library ExecutionLib {
    12
        error ERC7579DecodingError();
    13
    
                                                    
                                                
    14
        /**
    15
         * @notice Decode a batch of `Execution` executionBatch from a `bytes` calldata.
    16
         * @dev code is copied from solady's LibERC7579.sol
    17
         * https://github.com/Vectorized/solady/blob/740812cedc9a1fc11e17cb3d4569744367dedf19/src/accounts/LibERC7579.sol#L146
    18
         *      Credits to Vectorized and the Solady Team
    19
         */
    20
        function decodeBatch(bytes calldata executionCalldata)
    21
            internal
    22
            pure
    23
            returns (Execution[] calldata executionBatch)
    24
        {
    25
            /// @solidity memory-safe-assembly
    26
            assembly {
    27
                let u := calldataload(executionCalldata.offset)
    28
                let s := add(executionCalldata.offset, u)
    29
                let e := sub(add(executionCalldata.offset, executionCalldata.length), 0x20)
    30
                executionBatch.offset := add(s, 0x20)
    31
                executionBatch.length := calldataload(s)
    32
                if or(shr(64, u), gt(add(s, shl(5, executionBatch.length)), e)) {
    33
                    mstore(0x00, 0xba597e7e) // `DecodingError()`.
    34
                    revert(0x1c, 0x04)
    35
                }
    36
                if executionBatch.length {
    37
                    // Perform bounds checks on the decoded `executionBatch`.
    38
                    // Loop runs out-of-gas if `executionBatch.length` is big enough to cause overflows.
    39
                    for { let i := executionBatch.length } 1 { } {
    40
                        i := sub(i, 1)
    41
                        let p := calldataload(add(executionBatch.offset, shl(5, i)))
    42
                        let c := add(executionBatch.offset, p)
    43
                        let q := calldataload(add(c, 0x40))
    44
                        let o := add(c, q)
    45
                        // forgefmt: disable-next-item
    46
                        if or(shr(64, or(calldataload(o), or(p, q))),
    47
                            or(gt(add(c, 0x40), e), gt(add(o, calldataload(o)), e))) {
    48
                            mstore(0x00, 0xba597e7e) // `DecodingError()`.
    49
                            revert(0x1c, 0x04)
    50
                        }
    51
                        if iszero(i) { break }
    52
                    }
    53
                }
    54
            }
    55
        }
    56
    
                                                    
                                                
    57
        function encodeBatch(Execution[] memory executions)
    58
            internal
    59
            pure
    60
            returns (bytes memory callData)
    61
        {
    62
            callData = abi.encode(executions);
    63
        }
    64
    
                                                    
                                                
    65
        function decodeSingle(bytes calldata executionCalldata)
    66
            internal
    67
            pure
    68
            returns (address target, uint256 value, bytes calldata callData)
    69
        {
    70
            target = address(bytes20(executionCalldata[0:20]));
    71
            value = uint256(bytes32(executionCalldata[20:52]));
    72
            callData = executionCalldata[52:];
    73
        }
    74
    
                                                    
                                                
    75
        function encodeSingle(
    76
            address target,
    77
            uint256 value,
    78
            bytes memory callData
    79
        )
    80
            internal
    81
            pure
    82
            returns (bytes memory userOpCalldata)
    83
        {
    84
            userOpCalldata = abi.encodePacked(target, value, callData);
    85
        }
    86
    }
    87
    
                                                    
                                                
    53.3% lib/v2-core/lib/openzeppelin-contracts/contracts/access/AccessControl.sol
    Lines covered: 16 / 30 (53.3%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (access/AccessControl.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {IAccessControl} from "./IAccessControl.sol";
    7
    import {Context} from "../utils/Context.sol";
    8
    import {IERC165, ERC165} from "../utils/introspection/ERC165.sol";
    9
    
                                                    
                                                
    10
    /**
    11
     * @dev Contract module that allows children to implement role-based access
    12
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
    13
     * members except through off-chain means by accessing the contract event logs. Some
    14
     * applications may benefit from on-chain enumerability, for those cases see
    15
     * {AccessControlEnumerable}.
    16
     *
    17
     * Roles are referred to by their `bytes32` identifier. These should be exposed
    18
     * in the external API and be unique. The best way to achieve this is by
    19
     * using `public constant` hash digests:
    20
     *
    21
     * ```solidity
    22
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
    23
     * ```
    24
     *
    25
     * Roles can be used to represent a set of permissions. To restrict access to a
    26
     * function call, use {hasRole}:
    27
     *
    28
     * ```solidity
    29
     * function foo() public {
    30
     *     require(hasRole(MY_ROLE, msg.sender));
    31
     *     ...
    32
     * }
    33
     * ```
    34
     *
    35
     * Roles can be granted and revoked dynamically via the {grantRole} and
    36
     * {revokeRole} functions. Each role has an associated admin role, and only
    37
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
    38
     *
    39
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
    40
     * that only accounts with this role will be able to grant or revoke other
    41
     * roles. More complex role relationships can be created by using
    42
     * {_setRoleAdmin}.
    43
     *
    44
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
    45
     * grant and revoke this role. Extra precautions should be taken to secure
    46
     * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
    47
     * to enforce additional security measures for this role.
    48
     */
    49
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
    50
        struct RoleData {
    51
            mapping(address account => bool) hasRole;
    52
            bytes32 adminRole;
    53
        }
    54
    
                                                    
                                                
    55
        mapping(bytes32 role => RoleData) private _roles;
    56
    
                                                    
                                                
    57
    ✓ 1
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
    58
    
                                                    
                                                
    59
        /**
    60
         * @dev Modifier that checks that an account has a specific role. Reverts
    61
         * with an {AccessControlUnauthorizedAccount} error including the required role.
    62
         */
    63
        modifier onlyRole(bytes32 role) {
    64
    ✓ 225.7K
            _checkRole(role);
    65
            _;
    66
        }
    67
    
                                                    
                                                
    68
        /// @inheritdoc IERC165
    69
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    70
    ✓ 28.3M
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    71
        }
    72
    
                                                    
                                                
    73
        /**
    74
         * @dev Returns `true` if `account` has been granted `role`.
    75
         */
    76
    ✓ 1.2M
        function hasRole(bytes32 role, address account) public view virtual returns (bool) {
    77
    ✓ 1.2M
            return _roles[role].hasRole[account];
    78
        }
    79
    
                                                    
                                                
    80
        /**
    81
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
    82
         * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
    83
         */
    84
        function _checkRole(bytes32 role) internal view virtual {
    85
    ✓ 1.2M
            _checkRole(role, _msgSender());
    86
        }
    87
    
                                                    
                                                
    88
        /**
    89
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
    90
         * is missing `role`.
    91
         */
    92
        function _checkRole(bytes32 role, address account) internal view virtual {
    93
    ✓ 1.2M
            if (!hasRole(role, account)) {
    94
                revert AccessControlUnauthorizedAccount(account, role);
    95
            }
    96
        }
    97
    
                                                    
                                                
    98
        /**
    99
         * @dev Returns the admin role that controls `role`. See {grantRole} and
    100
         * {revokeRole}.
    101
         *
    102
         * To change a role's admin, use {_setRoleAdmin}.
    103
         */
    104
        function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
    105
    ✓ 5
            return _roles[role].adminRole;
    106
        }
    107
    
                                                    
                                                
    108
        /**
    109
         * @dev Grants `role` to `account`.
    110
         *
    111
         * If `account` had not been already granted `role`, emits a {RoleGranted}
    112
         * event.
    113
         *
    114
         * Requirements:
    115
         *
    116
         * - the caller must have ``role``'s admin role.
    117
         *
    118
         * May emit a {RoleGranted} event.
    119
         */
    120
        function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
    121
            _grantRole(role, account);
    122
        }
    123
    
                                                    
                                                
    124
        /**
    125
         * @dev Revokes `role` from `account`.
    126
         *
    127
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
    128
         *
    129
         * Requirements:
    130
         *
    131
         * - the caller must have ``role``'s admin role.
    132
         *
    133
         * May emit a {RoleRevoked} event.
    134
         */
    135
        function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
    136
            _revokeRole(role, account);
    137
        }
    138
    
                                                    
                                                
    139
        /**
    140
         * @dev Revokes `role` from the calling account.
    141
         *
    142
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
    143
         * purpose is to provide a mechanism for accounts to lose their privileges
    144
         * if they are compromised (such as when a trusted device is misplaced).
    145
         *
    146
         * If the calling account had been revoked `role`, emits a {RoleRevoked}
    147
         * event.
    148
         *
    149
         * Requirements:
    150
         *
    151
         * - the caller must be `callerConfirmation`.
    152
         *
    153
         * May emit a {RoleRevoked} event.
    154
         */
    155
        function renounceRole(bytes32 role, address callerConfirmation) public virtual {
    156
            if (callerConfirmation != _msgSender()) {
    157
                revert AccessControlBadConfirmation();
    158
            }
    159
    
                                                    
                                                
    160
            _revokeRole(role, callerConfirmation);
    161
        }
    162
    
                                                    
                                                
    163
        /**
    164
         * @dev Sets `adminRole` as ``role``'s admin role.
    165
         *
    166
         * Emits a {RoleAdminChanged} event.
    167
         */
    168
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
    169
    ✓ 5
            bytes32 previousAdminRole = getRoleAdmin(role);
    170
    ✓ 5
            _roles[role].adminRole = adminRole;
    171
    ✓ 5
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
    172
        }
    173
    
                                                    
                                                
    174
        /**
    175
         * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
    176
         *
    177
         * Internal function without access restriction.
    178
         *
    179
         * May emit a {RoleGranted} event.
    180
         */
    181
    ✓ 5
        function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
    182
            if (!hasRole(role, account)) {
    183
    ✓ 5
                _roles[role].hasRole[account] = true;
    184
    ✓ 5
                emit RoleGranted(role, account, _msgSender());
    185
    ✓ 5
                return true;
    186
            } else {
    187
    ✓ 1.3M
                return false;
    188
            }
    189
        }
    190
    
                                                    
                                                
    191
        /**
    192
         * @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
    193
         *
    194
         * Internal function without access restriction.
    195
         *
    196
         * May emit a {RoleRevoked} event.
    197
         */
    198
        function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
    199
            if (hasRole(role, account)) {
    200
                _roles[role].hasRole[account] = false;
    201
                emit RoleRevoked(role, account, _msgSender());
    202
                return true;
    203
            } else {
    204
                return false;
    205
            }
    206
        }
    207
    }
    208
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/access/IAccessControl.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (access/IAccessControl.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.8.4;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev External interface of AccessControl declared to support ERC-165 detection.
    8
     */
    9
    interface IAccessControl {
    10
        /**
    11
         * @dev The `account` is missing a role.
    12
         */
    13
        error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
    14
    
                                                    
                                                
    15
        /**
    16
         * @dev The caller of a function is not the expected one.
    17
         *
    18
         * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
    19
         */
    20
        error AccessControlBadConfirmation();
    21
    
                                                    
                                                
    22
        /**
    23
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
    24
         *
    25
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
    26
         * {RoleAdminChanged} not being emitted to signal this.
    27
         */
    28
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
    29
    
                                                    
                                                
    30
        /**
    31
         * @dev Emitted when `account` is granted `role`.
    32
         *
    33
         * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
    34
         * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
    35
         */
    36
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
    37
    
                                                    
                                                
    38
        /**
    39
         * @dev Emitted when `account` is revoked `role`.
    40
         *
    41
         * `sender` is the account that originated the contract call:
    42
         *   - if using `revokeRole`, it is the admin role bearer
    43
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
    44
         */
    45
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
    46
    
                                                    
                                                
    47
        /**
    48
         * @dev Returns `true` if `account` has been granted `role`.
    49
         */
    50
        function hasRole(bytes32 role, address account) external view returns (bool);
    51
    
                                                    
                                                
    52
        /**
    53
         * @dev Returns the admin role that controls `role`. See {grantRole} and
    54
         * {revokeRole}.
    55
         *
    56
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
    57
         */
    58
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
    59
    
                                                    
                                                
    60
        /**
    61
         * @dev Grants `role` to `account`.
    62
         *
    63
         * If `account` had not been already granted `role`, emits a {RoleGranted}
    64
         * event.
    65
         *
    66
         * Requirements:
    67
         *
    68
         * - the caller must have ``role``'s admin role.
    69
         */
    70
        function grantRole(bytes32 role, address account) external;
    71
    
                                                    
                                                
    72
        /**
    73
         * @dev Revokes `role` from `account`.
    74
         *
    75
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
    76
         *
    77
         * Requirements:
    78
         *
    79
         * - the caller must have ``role``'s admin role.
    80
         */
    81
        function revokeRole(bytes32 role, address account) external;
    82
    
                                                    
                                                
    83
        /**
    84
         * @dev Revokes `role` from the calling account.
    85
         *
    86
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
    87
         * purpose is to provide a mechanism for accounts to lose their privileges
    88
         * if they are compromised (such as when a trusted device is misplaced).
    89
         *
    90
         * If the calling account had been granted `role`, emits a {RoleRevoked}
    91
         * event.
    92
         *
    93
         * Requirements:
    94
         *
    95
         * - the caller must be `callerConfirmation`.
    96
         */
    97
        function renounceRole(bytes32 role, address callerConfirmation) external;
    98
    }
    99
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.6.2;
    5
    
                                                    
                                                
    6
    import {IERC20} from "./IERC20.sol";
    7
    import {IERC165} from "./IERC165.sol";
    8
    
                                                    
                                                
    9
    /**
    10
     * @title IERC1363
    11
     * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
    12
     *
    13
     * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
    14
     * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
    15
     */
    16
    interface IERC1363 is IERC20, IERC165 {
    17
        /*
    18
         * Note: the ERC-165 identifier for this interface is 0xb0202a11.
    19
         * 0xb0202a11 ===
    20
         *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
    21
         *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
    22
         *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
    23
         *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
    24
         *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
    25
         *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
    26
         */
    27
    
                                                    
                                                
    28
        /**
    29
         * @dev Moves a `value` amount of tokens from the caller's account to `to`
    30
         * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
    31
         * @param to The address which you want to transfer to.
    32
         * @param value The amount of tokens to be transferred.
    33
         * @return A boolean value indicating whether the operation succeeded unless throwing.
    34
         */
    35
        function transferAndCall(address to, uint256 value) external returns (bool);
    36
    
                                                    
                                                
    37
        /**
    38
         * @dev Moves a `value` amount of tokens from the caller's account to `to`
    39
         * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
    40
         * @param to The address which you want to transfer to.
    41
         * @param value The amount of tokens to be transferred.
    42
         * @param data Additional data with no specified format, sent in call to `to`.
    43
         * @return A boolean value indicating whether the operation succeeded unless throwing.
    44
         */
    45
        function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
    46
    
                                                    
                                                
    47
        /**
    48
         * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
    49
         * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
    50
         * @param from The address which you want to send tokens from.
    51
         * @param to The address which you want to transfer to.
    52
         * @param value The amount of tokens to be transferred.
    53
         * @return A boolean value indicating whether the operation succeeded unless throwing.
    54
         */
    55
        function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
    56
    
                                                    
                                                
    57
        /**
    58
         * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
    59
         * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
    60
         * @param from The address which you want to send tokens from.
    61
         * @param to The address which you want to transfer to.
    62
         * @param value The amount of tokens to be transferred.
    63
         * @param data Additional data with no specified format, sent in call to `to`.
    64
         * @return A boolean value indicating whether the operation succeeded unless throwing.
    65
         */
    66
        function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
    67
    
                                                    
                                                
    68
        /**
    69
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
    70
         * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
    71
         * @param spender The address which will spend the funds.
    72
         * @param value The amount of tokens to be spent.
    73
         * @return A boolean value indicating whether the operation succeeded unless throwing.
    74
         */
    75
        function approveAndCall(address spender, uint256 value) external returns (bool);
    76
    
                                                    
                                                
    77
        /**
    78
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
    79
         * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
    80
         * @param spender The address which will spend the funds.
    81
         * @param value The amount of tokens to be spent.
    82
         * @param data Additional data with no specified format, sent in call to `spender`.
    83
         * @return A boolean value indicating whether the operation succeeded unless throwing.
    84
         */
    85
        function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
    86
    }
    87
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.4.16;
    5
    
                                                    
                                                
    6
    import {IERC165} from "../utils/introspection/IERC165.sol";
    7
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.4.16;
    5
    
                                                    
                                                
    6
    import {IERC20} from "../token/ERC20/IERC20.sol";
    7
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20Metadata.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.6.2;
    5
    
                                                    
                                                
    6
    import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
    7
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (interfaces/IERC4626.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.6.2;
    5
    
                                                    
                                                
    6
    import {IERC20} from "../token/ERC20/IERC20.sol";
    7
    import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
    8
    
                                                    
                                                
    9
    /**
    10
     * @dev Interface of the ERC-4626 "Tokenized Vault Standard", as defined in
    11
     * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
    12
     */
    13
    interface IERC4626 is IERC20, IERC20Metadata {
    14
        event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
    15
    
                                                    
                                                
    16
        event Withdraw(
    17
            address indexed sender,
    18
            address indexed receiver,
    19
            address indexed owner,
    20
            uint256 assets,
    21
            uint256 shares
    22
        );
    23
    
                                                    
                                                
    24
        /**
    25
         * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
    26
         *
    27
         * - MUST be an ERC-20 token contract.
    28
         * - MUST NOT revert.
    29
         */
    30
        function asset() external view returns (address assetTokenAddress);
    31
    
                                                    
                                                
    32
        /**
    33
         * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
    34
         *
    35
         * - SHOULD include any compounding that occurs from yield.
    36
         * - MUST be inclusive of any fees that are charged against assets in the Vault.
    37
         * - MUST NOT revert.
    38
         */
    39
        function totalAssets() external view returns (uint256 totalManagedAssets);
    40
    
                                                    
                                                
    41
        /**
    42
         * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
    43
         * scenario where all the conditions are met.
    44
         *
    45
         * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    46
         * - MUST NOT show any variations depending on the caller.
    47
         * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    48
         * - MUST NOT revert.
    49
         *
    50
         * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    51
         * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    52
         * from.
    53
         */
    54
        function convertToShares(uint256 assets) external view returns (uint256 shares);
    55
    
                                                    
                                                
    56
        /**
    57
         * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
    58
         * scenario where all the conditions are met.
    59
         *
    60
         * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    61
         * - MUST NOT show any variations depending on the caller.
    62
         * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    63
         * - MUST NOT revert.
    64
         *
    65
         * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    66
         * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    67
         * from.
    68
         */
    69
        function convertToAssets(uint256 shares) external view returns (uint256 assets);
    70
    
                                                    
                                                
    71
        /**
    72
         * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
    73
         * through a deposit call.
    74
         *
    75
         * - MUST return a limited value if receiver is subject to some deposit limit.
    76
         * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
    77
         * - MUST NOT revert.
    78
         */
    79
        function maxDeposit(address receiver) external view returns (uint256 maxAssets);
    80
    
                                                    
                                                
    81
        /**
    82
         * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
    83
         * current on-chain conditions.
    84
         *
    85
         * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
    86
         *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
    87
         *   in the same transaction.
    88
         * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
    89
         *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
    90
         * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    91
         * - MUST NOT revert.
    92
         *
    93
         * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
    94
         * share price or some other type of condition, meaning the depositor will lose assets by depositing.
    95
         */
    96
        function previewDeposit(uint256 assets) external view returns (uint256 shares);
    97
    
                                                    
                                                
    98
        /**
    99
         * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
    100
         *
    101
         * - MUST emit the Deposit event.
    102
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    103
         *   deposit execution, and are accounted for during deposit.
    104
         * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
    105
         *   approving enough underlying tokens to the Vault contract, etc).
    106
         *
    107
         * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    108
         */
    109
        function deposit(uint256 assets, address receiver) external returns (uint256 shares);
    110
    
                                                    
                                                
    111
        /**
    112
         * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
    113
         * - MUST return a limited value if receiver is subject to some mint limit.
    114
         * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
    115
         * - MUST NOT revert.
    116
         */
    117
        function maxMint(address receiver) external view returns (uint256 maxShares);
    118
    
                                                    
                                                
    119
        /**
    120
         * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
    121
         * current on-chain conditions.
    122
         *
    123
         * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
    124
         *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
    125
         *   same transaction.
    126
         * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
    127
         *   would be accepted, regardless if the user has enough tokens approved, etc.
    128
         * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    129
         * - MUST NOT revert.
    130
         *
    131
         * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
    132
         * share price or some other type of condition, meaning the depositor will lose assets by minting.
    133
         */
    134
        function previewMint(uint256 shares) external view returns (uint256 assets);
    135
    
                                                    
                                                
    136
        /**
    137
         * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
    138
         *
    139
         * - MUST emit the Deposit event.
    140
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
    141
         *   execution, and are accounted for during mint.
    142
         * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
    143
         *   approving enough underlying tokens to the Vault contract, etc).
    144
         *
    145
         * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    146
         */
    147
        function mint(uint256 shares, address receiver) external returns (uint256 assets);
    148
    
                                                    
                                                
    149
        /**
    150
         * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
    151
         * Vault, through a withdraw call.
    152
         *
    153
         * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    154
         * - MUST NOT revert.
    155
         */
    156
        function maxWithdraw(address owner) external view returns (uint256 maxAssets);
    157
    
                                                    
                                                
    158
        /**
    159
         * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
    160
         * given current on-chain conditions.
    161
         *
    162
         * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
    163
         *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
    164
         *   called
    165
         *   in the same transaction.
    166
         * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
    167
         *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
    168
         * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    169
         * - MUST NOT revert.
    170
         *
    171
         * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
    172
         * share price or some other type of condition, meaning the depositor will lose assets by depositing.
    173
         */
    174
        function previewWithdraw(uint256 assets) external view returns (uint256 shares);
    175
    
                                                    
                                                
    176
        /**
    177
         * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
    178
         *
    179
         * - MUST emit the Withdraw event.
    180
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    181
         *   withdraw execution, and are accounted for during withdraw.
    182
         * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
    183
         *   not having enough shares, etc).
    184
         *
    185
         * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    186
         * Those methods should be performed separately.
    187
         */
    188
        function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
    189
    
                                                    
                                                
    190
        /**
    191
         * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
    192
         * through a redeem call.
    193
         *
    194
         * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    195
         * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
    196
         * - MUST NOT revert.
    197
         */
    198
        function maxRedeem(address owner) external view returns (uint256 maxShares);
    199
    
                                                    
                                                
    200
        /**
    201
         * @dev Allows an on-chain or off-chain user to simulate the effects of their redemption at the current block,
    202
         * given current on-chain conditions.
    203
         *
    204
         * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
    205
         *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
    206
         *   same transaction.
    207
         * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
    208
         *   redemption would be accepted, regardless if the user has enough shares, etc.
    209
         * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    210
         * - MUST NOT revert.
    211
         *
    212
         * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
    213
         * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
    214
         */
    215
        function previewRedeem(uint256 shares) external view returns (uint256 assets);
    216
    
                                                    
                                                
    217
        /**
    218
         * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
    219
         *
    220
         * - MUST emit the Withdraw event.
    221
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    222
         *   redeem execution, and are accounted for during redeem.
    223
         * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
    224
         *   not having enough shares, etc).
    225
         *
    226
         * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    227
         * Those methods should be performed separately.
    228
         */
    229
        function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
    230
    }
    231
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/interfaces/IERC5267.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.4.16;
    5
    
                                                    
                                                
    6
    interface IERC5267 {
    7
        /**
    8
         * @dev MAY be emitted to signal that the domain could have changed.
    9
         */
    10
        event EIP712DomainChanged();
    11
    
                                                    
                                                
    12
        /**
    13
         * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
    14
         * signature.
    15
         */
    16
        function eip712Domain()
    17
            external
    18
            view
    19
            returns (
    20
                bytes1 fields,
    21
                string memory name,
    22
                string memory version,
    23
                uint256 chainId,
    24
                address verifyingContract,
    25
                bytes32 salt,
    26
                uint256[] memory extensions
    27
            );
    28
    }
    29
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
    3
    pragma solidity >=0.8.4;
    4
    
                                                    
                                                
    5
    /**
    6
     * @dev Standard ERC-20 Errors
    7
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
    8
     */
    9
    interface IERC20Errors {
    10
        /**
    11
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
    12
         * @param sender Address whose tokens are being transferred.
    13
         * @param balance Current balance for the interacting account.
    14
         * @param needed Minimum amount required to perform a transfer.
    15
         */
    16
        error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
    17
    
                                                    
                                                
    18
        /**
    19
         * @dev Indicates a failure with the token `sender`. Used in transfers.
    20
         * @param sender Address whose tokens are being transferred.
    21
         */
    22
        error ERC20InvalidSender(address sender);
    23
    
                                                    
                                                
    24
        /**
    25
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
    26
         * @param receiver Address to which tokens are being transferred.
    27
         */
    28
        error ERC20InvalidReceiver(address receiver);
    29
    
                                                    
                                                
    30
        /**
    31
         * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
    32
         * @param spender Address that may be allowed to operate on tokens without being their owner.
    33
         * @param allowance Amount of tokens a `spender` is allowed to operate with.
    34
         * @param needed Minimum amount required to perform a transfer.
    35
         */
    36
        error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
    37
    
                                                    
                                                
    38
        /**
    39
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
    40
         * @param approver Address initiating an approval operation.
    41
         */
    42
        error ERC20InvalidApprover(address approver);
    43
    
                                                    
                                                
    44
        /**
    45
         * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
    46
         * @param spender Address that may be allowed to operate on tokens without being their owner.
    47
         */
    48
        error ERC20InvalidSpender(address spender);
    49
    }
    50
    
                                                    
                                                
    51
    /**
    52
     * @dev Standard ERC-721 Errors
    53
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
    54
     */
    55
    interface IERC721Errors {
    56
        /**
    57
         * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
    58
         * Used in balance queries.
    59
         * @param owner Address of the current owner of a token.
    60
         */
    61
        error ERC721InvalidOwner(address owner);
    62
    
                                                    
                                                
    63
        /**
    64
         * @dev Indicates a `tokenId` whose `owner` is the zero address.
    65
         * @param tokenId Identifier number of a token.
    66
         */
    67
        error ERC721NonexistentToken(uint256 tokenId);
    68
    
                                                    
                                                
    69
        /**
    70
         * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
    71
         * @param sender Address whose tokens are being transferred.
    72
         * @param tokenId Identifier number of a token.
    73
         * @param owner Address of the current owner of a token.
    74
         */
    75
        error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
    76
    
                                                    
                                                
    77
        /**
    78
         * @dev Indicates a failure with the token `sender`. Used in transfers.
    79
         * @param sender Address whose tokens are being transferred.
    80
         */
    81
        error ERC721InvalidSender(address sender);
    82
    
                                                    
                                                
    83
        /**
    84
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
    85
         * @param receiver Address to which tokens are being transferred.
    86
         */
    87
        error ERC721InvalidReceiver(address receiver);
    88
    
                                                    
                                                
    89
        /**
    90
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
    91
         * @param operator Address that may be allowed to operate on tokens without being their owner.
    92
         * @param tokenId Identifier number of a token.
    93
         */
    94
        error ERC721InsufficientApproval(address operator, uint256 tokenId);
    95
    
                                                    
                                                
    96
        /**
    97
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
    98
         * @param approver Address initiating an approval operation.
    99
         */
    100
        error ERC721InvalidApprover(address approver);
    101
    
                                                    
                                                
    102
        /**
    103
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
    104
         * @param operator Address that may be allowed to operate on tokens without being their owner.
    105
         */
    106
        error ERC721InvalidOperator(address operator);
    107
    }
    108
    
                                                    
                                                
    109
    /**
    110
     * @dev Standard ERC-1155 Errors
    111
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
    112
     */
    113
    interface IERC1155Errors {
    114
        /**
    115
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
    116
         * @param sender Address whose tokens are being transferred.
    117
         * @param balance Current balance for the interacting account.
    118
         * @param needed Minimum amount required to perform a transfer.
    119
         * @param tokenId Identifier number of a token.
    120
         */
    121
        error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
    122
    
                                                    
                                                
    123
        /**
    124
         * @dev Indicates a failure with the token `sender`. Used in transfers.
    125
         * @param sender Address whose tokens are being transferred.
    126
         */
    127
        error ERC1155InvalidSender(address sender);
    128
    
                                                    
                                                
    129
        /**
    130
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
    131
         * @param receiver Address to which tokens are being transferred.
    132
         */
    133
        error ERC1155InvalidReceiver(address receiver);
    134
    
                                                    
                                                
    135
        /**
    136
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
    137
         * @param operator Address that may be allowed to operate on tokens without being their owner.
    138
         * @param owner Address of the current owner of a token.
    139
         */
    140
        error ERC1155MissingApprovalForAll(address operator, address owner);
    141
    
                                                    
                                                
    142
        /**
    143
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
    144
         * @param approver Address initiating an approval operation.
    145
         */
    146
        error ERC1155InvalidApprover(address approver);
    147
    
                                                    
                                                
    148
        /**
    149
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
    150
         * @param operator Address that may be allowed to operate on tokens without being their owner.
    151
         */
    152
        error ERC1155InvalidOperator(address operator);
    153
    
                                                    
                                                
    154
        /**
    155
         * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
    156
         * Used in batch transfers.
    157
         * @param idsLength Length of the array of token identifiers
    158
         * @param valuesLength Length of the array of token amounts
    159
         */
    160
        error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
    161
    }
    162
    
                                                    
                                                
    81.8% lib/v2-core/lib/openzeppelin-contracts/contracts/proxy/Clones.sol
    Lines covered: 9 / 11 (81.8%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (proxy/Clones.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {Create2} from "../utils/Create2.sol";
    7
    import {Errors} from "../utils/Errors.sol";
    8
    
                                                    
                                                
    9
    /**
    10
     * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
    11
     * deploying minimal proxy contracts, also known as "clones".
    12
     *
    13
     * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
    14
     * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
    15
     *
    16
     * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
    17
     * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
    18
     * deterministic method.
    19
     */
    20
    ✓ 16.2K
    ⟲ 16.2K
    library Clones {
    21
        error CloneArgumentsTooLong();
    22
    
                                                    
                                                
    23
        /**
    24
         * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`.
    25
         *
    26
         * This function uses the create opcode, which should never revert.
    27
         */
    28
        function clone(address implementation) internal returns (address instance) {
    29
            return clone(implementation, 0);
    30
        }
    31
    
                                                    
                                                
    32
        /**
    33
         * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
    34
         * to the new contract.
    35
         *
    36
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
    37
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
    38
         */
    39
        function clone(address implementation, uint256 value) internal returns (address instance) {
    40
            if (address(this).balance < value) {
    41
                revert Errors.InsufficientBalance(address(this).balance, value);
    42
            }
    43
            assembly ("memory-safe") {
    44
                // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
    45
                // of the `implementation` address with the bytecode before the address.
    46
                mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
    47
                // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
    48
                mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
    49
                instance := create(value, 0x09, 0x37)
    50
            }
    51
            if (instance == address(0)) {
    52
                revert Errors.FailedDeployment();
    53
            }
    54
        }
    55
    
                                                    
                                                
    56
        /**
    57
         * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`.
    58
         *
    59
         * This function uses the create2 opcode and a `salt` to deterministically deploy
    60
         * the clone. Using the same `implementation` and `salt` multiple times will revert, since
    61
         * the clones cannot be deployed twice at the same address.
    62
         */
    63
    ✓ 679.0K
        function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
    64
    ✓ 679.0K
            return cloneDeterministic(implementation, salt, 0);
    65
        }
    66
    
                                                    
                                                
    67
        /**
    68
         * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
    69
         * a `value` parameter to send native currency to the new contract.
    70
         *
    71
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
    72
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
    73
         */
    74
        function cloneDeterministic(
    75
            address implementation,
    76
            bytes32 salt,
    77
            uint256 value
    78
    ✓ 679.0K
        ) internal returns (address instance) {
    79
    ✓ 679.0K
            if (address(this).balance < value) {
    80
                revert Errors.InsufficientBalance(address(this).balance, value);
    81
            }
    82
            assembly ("memory-safe") {
    83
                // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
    84
                // of the `implementation` address with the bytecode before the address.
    85
    ✓ 679.0K
                mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
    86
                // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
    87
    ✓ 679.0K
                mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
    88
    ✓ 679.0K
                instance := create2(value, 0x09, 0x37, salt)
    89
            }
    90
    ✓ 679.0K
            if (instance == address(0)) {
    91
                revert Errors.FailedDeployment();
    92
            }
    93
        }
    94
    
                                                    
                                                
    95
        /**
    96
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
    97
         */
    98
        function predictDeterministicAddress(
    99
            address implementation,
    100
            bytes32 salt,
    101
            address deployer
    102
        ) internal pure returns (address predicted) {
    103
            assembly ("memory-safe") {
    104
                let ptr := mload(0x40)
    105
                mstore(add(ptr, 0x38), deployer)
    106
                mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
    107
                mstore(add(ptr, 0x14), implementation)
    108
                mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
    109
                mstore(add(ptr, 0x58), salt)
    110
                mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
    111
                predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
    112
            }
    113
        }
    114
    
                                                    
                                                
    115
        /**
    116
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
    117
         */
    118
        function predictDeterministicAddress(
    119
            address implementation,
    120
            bytes32 salt
    121
        ) internal view returns (address predicted) {
    122
            return predictDeterministicAddress(implementation, salt, address(this));
    123
        }
    124
    
                                                    
                                                
    125
        /**
    126
         * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
    127
         * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
    128
         * access the arguments within the implementation, use {fetchCloneArgs}.
    129
         *
    130
         * This function uses the create opcode, which should never revert.
    131
         */
    132
        function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) {
    133
            return cloneWithImmutableArgs(implementation, args, 0);
    134
        }
    135
    
                                                    
                                                
    136
        /**
    137
         * @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
    138
         * parameter to send native currency to the new contract.
    139
         *
    140
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
    141
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
    142
         */
    143
        function cloneWithImmutableArgs(
    144
            address implementation,
    145
            bytes memory args,
    146
            uint256 value
    147
        ) internal returns (address instance) {
    148
            if (address(this).balance < value) {
    149
                revert Errors.InsufficientBalance(address(this).balance, value);
    150
            }
    151
            bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
    152
            assembly ("memory-safe") {
    153
                instance := create(value, add(bytecode, 0x20), mload(bytecode))
    154
            }
    155
            if (instance == address(0)) {
    156
                revert Errors.FailedDeployment();
    157
            }
    158
        }
    159
    
                                                    
                                                
    160
        /**
    161
         * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
    162
         * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
    163
         * access the arguments within the implementation, use {fetchCloneArgs}.
    164
         *
    165
         * This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
    166
         * `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice
    167
         * at the same address.
    168
         */
    169
        function cloneDeterministicWithImmutableArgs(
    170
            address implementation,
    171
            bytes memory args,
    172
            bytes32 salt
    173
        ) internal returns (address instance) {
    174
            return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0);
    175
        }
    176
    
                                                    
                                                
    177
        /**
    178
         * @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
    179
         * but with a `value` parameter to send native currency to the new contract.
    180
         *
    181
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
    182
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
    183
         */
    184
        function cloneDeterministicWithImmutableArgs(
    185
            address implementation,
    186
            bytes memory args,
    187
            bytes32 salt,
    188
            uint256 value
    189
        ) internal returns (address instance) {
    190
            bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
    191
            return Create2.deploy(value, salt, bytecode);
    192
        }
    193
    
                                                    
                                                
    194
        /**
    195
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
    196
         */
    197
        function predictDeterministicAddressWithImmutableArgs(
    198
            address implementation,
    199
            bytes memory args,
    200
            bytes32 salt,
    201
            address deployer
    202
        ) internal pure returns (address predicted) {
    203
            bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
    204
            return Create2.computeAddress(salt, keccak256(bytecode), deployer);
    205
        }
    206
    
                                                    
                                                
    207
        /**
    208
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
    209
         */
    210
        function predictDeterministicAddressWithImmutableArgs(
    211
            address implementation,
    212
            bytes memory args,
    213
            bytes32 salt
    214
        ) internal view returns (address predicted) {
    215
            return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this));
    216
        }
    217
    
                                                    
                                                
    218
        /**
    219
         * @dev Get the immutable args attached to a clone.
    220
         *
    221
         * - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
    222
         *   function will return an empty array.
    223
         * - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
    224
         *   `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
    225
         *   creation.
    226
         * - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
    227
         *   function should only be used to check addresses that are known to be clones.
    228
         */
    229
        function fetchCloneArgs(address instance) internal view returns (bytes memory) {
    230
            bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
    231
            assembly ("memory-safe") {
    232
                extcodecopy(instance, add(result, 32), 45, mload(result))
    233
            }
    234
            return result;
    235
        }
    236
    
                                                    
                                                
    237
        /**
    238
         * @dev Helper that prepares the initcode of the proxy with immutable args.
    239
         *
    240
         * An assembly variant of this function requires copying the `args` array, which can be efficiently done using
    241
         * `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
    242
         * abi.encodePacked is more expensive but also more portable and easier to review.
    243
         *
    244
         * NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
    245
         * With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
    246
         */
    247
        function _cloneCodeWithImmutableArgs(
    248
            address implementation,
    249
            bytes memory args
    250
        ) private pure returns (bytes memory) {
    251
            if (args.length > 24531) revert CloneArgumentsTooLong();
    252
            return
    253
                abi.encodePacked(
    254
                    hex"61",
    255
                    uint16(args.length + 45),
    256
                    hex"3d81600a3d39f3363d3d373d3d3d363d73",
    257
                    implementation,
    258
                    hex"5af43d82803e903d91602b57fd5bf3",
    259
                    args
    260
                );
    261
        }
    262
    }
    263
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.4.16;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev Interface of the ERC-20 standard as defined in the ERC.
    8
     */
    9
    interface IERC20 {
    10
        /**
    11
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
    12
         * another (`to`).
    13
         *
    14
         * Note that `value` may be zero.
    15
         */
    16
        event Transfer(address indexed from, address indexed to, uint256 value);
    17
    
                                                    
                                                
    18
        /**
    19
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
    20
         * a call to {approve}. `value` is the new allowance.
    21
         */
    22
        event Approval(address indexed owner, address indexed spender, uint256 value);
    23
    
                                                    
                                                
    24
        /**
    25
         * @dev Returns the value of tokens in existence.
    26
         */
    27
        function totalSupply() external view returns (uint256);
    28
    
                                                    
                                                
    29
        /**
    30
         * @dev Returns the value of tokens owned by `account`.
    31
         */
    32
        function balanceOf(address account) external view returns (uint256);
    33
    
                                                    
                                                
    34
        /**
    35
         * @dev Moves a `value` amount of tokens from the caller's account to `to`.
    36
         *
    37
         * Returns a boolean value indicating whether the operation succeeded.
    38
         *
    39
         * Emits a {Transfer} event.
    40
         */
    41
        function transfer(address to, uint256 value) external returns (bool);
    42
    
                                                    
                                                
    43
        /**
    44
         * @dev Returns the remaining number of tokens that `spender` will be
    45
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
    46
         * zero by default.
    47
         *
    48
         * This value changes when {approve} or {transferFrom} are called.
    49
         */
    50
        function allowance(address owner, address spender) external view returns (uint256);
    51
    
                                                    
                                                
    52
        /**
    53
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
    54
         * caller's tokens.
    55
         *
    56
         * Returns a boolean value indicating whether the operation succeeded.
    57
         *
    58
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
    59
         * that someone may use both the old and the new allowance by unfortunate
    60
         * transaction ordering. One possible solution to mitigate this race
    61
         * condition is to first reduce the spender's allowance to 0 and set the
    62
         * desired value afterwards:
    63
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    64
         *
    65
         * Emits an {Approval} event.
    66
         */
    67
        function approve(address spender, uint256 value) external returns (bool);
    68
    
                                                    
                                                
    69
        /**
    70
         * @dev Moves a `value` amount of tokens from `from` to `to` using the
    71
         * allowance mechanism. `value` is then deducted from the caller's
    72
         * allowance.
    73
         *
    74
         * Returns a boolean value indicating whether the operation succeeded.
    75
         *
    76
         * Emits a {Transfer} event.
    77
         */
    78
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    79
    }
    80
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.6.2;
    5
    
                                                    
                                                
    6
    import {IERC20} from "../IERC20.sol";
    7
    
                                                    
                                                
    8
    /**
    9
     * @dev Interface for the optional metadata functions from the ERC-20 standard.
    10
     */
    11
    interface IERC20Metadata is IERC20 {
    12
        /**
    13
         * @dev Returns the name of the token.
    14
         */
    15
        function name() external view returns (string memory);
    16
    
                                                    
                                                
    17
        /**
    18
         * @dev Returns the symbol of the token.
    19
         */
    20
        function symbol() external view returns (string memory);
    21
    
                                                    
                                                
    22
        /**
    23
         * @dev Returns the decimals places of the token.
    24
         */
    25
        function decimals() external view returns (uint8);
    26
    }
    27
    
                                                    
                                                
    92.9% lib/v2-core/lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol
    Lines covered: 13 / 14 (92.9%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {IERC20} from "../IERC20.sol";
    7
    import {IERC1363} from "../../../interfaces/IERC1363.sol";
    8
    
                                                    
                                                
    9
    /**
    10
     * @title SafeERC20
    11
     * @dev Wrappers around ERC-20 operations that throw on failure (when the token
    12
     * contract returns false). Tokens that return no value (and instead revert or
    13
     * throw on failure) are also supported, non-reverting calls are assumed to be
    14
     * successful.
    15
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
    16
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
    17
     */
    18
    ✓ 25.9K
    ⟲ 25.9K
    library SafeERC20 {
    19
        /**
    20
         * @dev An operation with an ERC-20 token failed.
    21
         */
    22
        error SafeERC20FailedOperation(address token);
    23
    
                                                    
                                                
    24
        /**
    25
         * @dev Indicates a failed `decreaseAllowance` request.
    26
         */
    27
        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
    28
    
                                                    
                                                
    29
        /**
    30
         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
    31
         * non-reverting calls are assumed to be successful.
    32
         */
    33
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
    34
    ✓ 1.3M
            _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    35
        }
    36
    
                                                    
                                                
    37
        /**
    38
         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
    39
         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
    40
         */
    41
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
    42
    ✓ 1.3M
            _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    43
        }
    44
    
                                                    
                                                
    45
        /**
    46
         * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
    47
         */
    48
        function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
    49
            return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    50
        }
    51
    
                                                    
                                                
    52
        /**
    53
         * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
    54
         */
    55
        function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
    56
            return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    57
        }
    58
    
                                                    
                                                
    59
        /**
    60
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
    61
         * non-reverting calls are assumed to be successful.
    62
         *
    63
         * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
    64
         * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
    65
         * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
    66
         * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
    67
         */
    68
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
    69
            uint256 oldAllowance = token.allowance(address(this), spender);
    70
            forceApprove(token, spender, oldAllowance + value);
    71
        }
    72
    
                                                    
                                                
    73
        /**
    74
         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
    75
         * value, non-reverting calls are assumed to be successful.
    76
         *
    77
         * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
    78
         * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
    79
         * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
    80
         * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
    81
         */
    82
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
    83
            unchecked {
    84
                uint256 currentAllowance = token.allowance(address(this), spender);
    85
                if (currentAllowance < requestedDecrease) {
    86
                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
    87
                }
    88
                forceApprove(token, spender, currentAllowance - requestedDecrease);
    89
            }
    90
        }
    91
    
                                                    
                                                
    92
        /**
    93
         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
    94
         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
    95
         * to be set to zero before setting it to a non-zero value, such as USDT.
    96
         *
    97
         * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
    98
         * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
    99
         * set here.
    100
         */
    101
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
    102
            bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
    103
    
                                                    
                                                
    104
            if (!_callOptionalReturnBool(token, approvalCall)) {
    105
                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
    106
                _callOptionalReturn(token, approvalCall);
    107
            }
    108
        }
    109
    
                                                    
                                                
    110
        /**
    111
         * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
    112
         * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
    113
         * targeting contracts.
    114
         *
    115
         * Reverts if the returned value is other than `true`.
    116
         */
    117
        function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
    118
            if (to.code.length == 0) {
    119
                safeTransfer(token, to, value);
    120
            } else if (!token.transferAndCall(to, value, data)) {
    121
                revert SafeERC20FailedOperation(address(token));
    122
            }
    123
        }
    124
    
                                                    
                                                
    125
        /**
    126
         * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
    127
         * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
    128
         * targeting contracts.
    129
         *
    130
         * Reverts if the returned value is other than `true`.
    131
         */
    132
        function transferFromAndCallRelaxed(
    133
            IERC1363 token,
    134
            address from,
    135
            address to,
    136
            uint256 value,
    137
            bytes memory data
    138
        ) internal {
    139
            if (to.code.length == 0) {
    140
                safeTransferFrom(token, from, to, value);
    141
            } else if (!token.transferFromAndCall(from, to, value, data)) {
    142
                revert SafeERC20FailedOperation(address(token));
    143
            }
    144
        }
    145
    
                                                    
                                                
    146
        /**
    147
         * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
    148
         * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
    149
         * targeting contracts.
    150
         *
    151
         * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
    152
         * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
    153
         * once without retrying, and relies on the returned value to be true.
    154
         *
    155
         * Reverts if the returned value is other than `true`.
    156
         */
    157
        function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
    158
            if (to.code.length == 0) {
    159
                forceApprove(token, to, value);
    160
            } else if (!token.approveAndCall(to, value, data)) {
    161
                revert SafeERC20FailedOperation(address(token));
    162
            }
    163
        }
    164
    
                                                    
                                                
    165
        /**
    166
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
    167
         * on the return value: the return value is optional (but if data is returned, it must not be false).
    168
         * @param token The token targeted by the call.
    169
         * @param data The call data (encoded using abi.encode or one of its variants).
    170
         *
    171
         * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
    172
         */
    173
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
    174
    ✓ 1.3M
            uint256 returnSize;
    175
    ✓ 1.3M
            uint256 returnValue;
    176
            assembly ("memory-safe") {
    177
    ✓ 1.3M
                let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
    178
                // bubble errors
    179
    ✓ 1.3M
                if iszero(success) {
    180
    ✓ 682.5K
                    let ptr := mload(0x40)
    181
    ✓ 682.5K
                    returndatacopy(ptr, 0, returndatasize())
    182
    ✓ 682.5K
                    revert(ptr, returndatasize())
    183
                }
    184
    ✓ 1.2M
                returnSize := returndatasize()
    185
    ✓ 1.2M
                returnValue := mload(0)
    186
            }
    187
    
                                                    
                                                
    188
    ✓ 1.2M
            if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
    189
                revert SafeERC20FailedOperation(address(token));
    190
            }
    191
        }
    192
    
                                                    
                                                
    193
        /**
    194
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
    195
         * on the return value: the return value is optional (but if data is returned, it must not be false).
    196
         * @param token The token targeted by the call.
    197
         * @param data The call data (encoded using abi.encode or one of its variants).
    198
         *
    199
         * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
    200
         */
    201
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
    202
            bool success;
    203
            uint256 returnSize;
    204
            uint256 returnValue;
    205
            assembly ("memory-safe") {
    206
                success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
    207
                returnSize := returndatasize()
    208
                returnValue := mload(0)
    209
            }
    210
            return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    211
        }
    212
    }
    213
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/Arrays.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (utils/Arrays.sol)
    3
    // This file was procedurally generated from scripts/generate/templates/Arrays.js.
    4
    
                                                    
                                                
    5
    pragma solidity ^0.8.20;
    6
    
                                                    
                                                
    7
    import {Comparators} from "./Comparators.sol";
    8
    import {SlotDerivation} from "./SlotDerivation.sol";
    9
    import {StorageSlot} from "./StorageSlot.sol";
    10
    import {Math} from "./math/Math.sol";
    11
    
                                                    
                                                
    12
    /**
    13
     * @dev Collection of functions related to array types.
    14
     */
    15
    ✓ 31.7K
    ⟲ 31.7K
    library Arrays {
    16
        using SlotDerivation for bytes32;
    17
        using StorageSlot for bytes32;
    18
    
                                                    
                                                
    19
        /**
    20
         * @dev Sort an array of uint256 (in memory) following the provided comparator function.
    21
         *
    22
         * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
    23
         * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
    24
         *
    25
         * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
    26
         * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
    27
         * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
    28
         * consume more gas than is available in a block, leading to potential DoS.
    29
         *
    30
         * IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
    31
         */
    32
        function sort(
    33
            uint256[] memory array,
    34
            function(uint256, uint256) pure returns (bool) comp
    35
        ) internal pure returns (uint256[] memory) {
    36
            _quickSort(_begin(array), _end(array), comp);
    37
            return array;
    38
        }
    39
    
                                                    
                                                
    40
        /**
    41
         * @dev Variant of {sort} that sorts an array of uint256 in increasing order.
    42
         */
    43
        function sort(uint256[] memory array) internal pure returns (uint256[] memory) {
    44
            sort(array, Comparators.lt);
    45
            return array;
    46
        }
    47
    
                                                    
                                                
    48
        /**
    49
         * @dev Sort an array of address (in memory) following the provided comparator function.
    50
         *
    51
         * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
    52
         * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
    53
         *
    54
         * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
    55
         * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
    56
         * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
    57
         * consume more gas than is available in a block, leading to potential DoS.
    58
         *
    59
         * IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
    60
         */
    61
        function sort(
    62
            address[] memory array,
    63
            function(address, address) pure returns (bool) comp
    64
        ) internal pure returns (address[] memory) {
    65
            sort(_castToUint256Array(array), _castToUint256Comp(comp));
    66
            return array;
    67
        }
    68
    
                                                    
                                                
    69
        /**
    70
         * @dev Variant of {sort} that sorts an array of address in increasing order.
    71
         */
    72
        function sort(address[] memory array) internal pure returns (address[] memory) {
    73
            sort(_castToUint256Array(array), Comparators.lt);
    74
            return array;
    75
        }
    76
    
                                                    
                                                
    77
        /**
    78
         * @dev Sort an array of bytes32 (in memory) following the provided comparator function.
    79
         *
    80
         * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
    81
         * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
    82
         *
    83
         * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
    84
         * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
    85
         * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
    86
         * consume more gas than is available in a block, leading to potential DoS.
    87
         *
    88
         * IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
    89
         */
    90
        function sort(
    91
            bytes32[] memory array,
    92
            function(bytes32, bytes32) pure returns (bool) comp
    93
        ) internal pure returns (bytes32[] memory) {
    94
            sort(_castToUint256Array(array), _castToUint256Comp(comp));
    95
            return array;
    96
        }
    97
    
                                                    
                                                
    98
        /**
    99
         * @dev Variant of {sort} that sorts an array of bytes32 in increasing order.
    100
         */
    101
        function sort(bytes32[] memory array) internal pure returns (bytes32[] memory) {
    102
            sort(_castToUint256Array(array), Comparators.lt);
    103
            return array;
    104
        }
    105
    
                                                    
                                                
    106
        /**
    107
         * @dev Performs a quick sort of a segment of memory. The segment sorted starts at `begin` (inclusive), and stops
    108
         * at end (exclusive). Sorting follows the `comp` comparator.
    109
         *
    110
         * Invariant: `begin <= end`. This is the case when initially called by {sort} and is preserved in subcalls.
    111
         *
    112
         * IMPORTANT: Memory locations between `begin` and `end` are not validated/zeroed. This function should
    113
         * be used only if the limits are within a memory array.
    114
         */
    115
        function _quickSort(uint256 begin, uint256 end, function(uint256, uint256) pure returns (bool) comp) private pure {
    116
            unchecked {
    117
                if (end - begin < 0x40) return;
    118
    
                                                    
                                                
    119
                // Use first element as pivot
    120
                uint256 pivot = _mload(begin);
    121
                // Position where the pivot should be at the end of the loop
    122
                uint256 pos = begin;
    123
    
                                                    
                                                
    124
                for (uint256 it = begin + 0x20; it < end; it += 0x20) {
    125
                    if (comp(_mload(it), pivot)) {
    126
                        // If the value stored at the iterator's position comes before the pivot, we increment the
    127
                        // position of the pivot and move the value there.
    128
                        pos += 0x20;
    129
                        _swap(pos, it);
    130
                    }
    131
                }
    132
    
                                                    
                                                
    133
                _swap(begin, pos); // Swap pivot into place
    134
                _quickSort(begin, pos, comp); // Sort the left side of the pivot
    135
                _quickSort(pos + 0x20, end, comp); // Sort the right side of the pivot
    136
            }
    137
        }
    138
    
                                                    
                                                
    139
        /**
    140
         * @dev Pointer to the memory location of the first element of `array`.
    141
         */
    142
        function _begin(uint256[] memory array) private pure returns (uint256 ptr) {
    143
            assembly ("memory-safe") {
    144
                ptr := add(array, 0x20)
    145
            }
    146
        }
    147
    
                                                    
                                                
    148
        /**
    149
         * @dev Pointer to the memory location of the first memory word (32bytes) after `array`. This is the memory word
    150
         * that comes just after the last element of the array.
    151
         */
    152
        function _end(uint256[] memory array) private pure returns (uint256 ptr) {
    153
            unchecked {
    154
                return _begin(array) + array.length * 0x20;
    155
            }
    156
        }
    157
    
                                                    
                                                
    158
        /**
    159
         * @dev Load memory word (as a uint256) at location `ptr`.
    160
         */
    161
        function _mload(uint256 ptr) private pure returns (uint256 value) {
    162
            assembly {
    163
                value := mload(ptr)
    164
            }
    165
        }
    166
    
                                                    
                                                
    167
        /**
    168
         * @dev Swaps the elements memory location `ptr1` and `ptr2`.
    169
         */
    170
        function _swap(uint256 ptr1, uint256 ptr2) private pure {
    171
            assembly {
    172
                let value1 := mload(ptr1)
    173
                let value2 := mload(ptr2)
    174
                mstore(ptr1, value2)
    175
                mstore(ptr2, value1)
    176
            }
    177
        }
    178
    
                                                    
                                                
    179
        /// @dev Helper: low level cast address memory array to uint256 memory array
    180
        function _castToUint256Array(address[] memory input) private pure returns (uint256[] memory output) {
    181
            assembly {
    182
                output := input
    183
            }
    184
        }
    185
    
                                                    
                                                
    186
        /// @dev Helper: low level cast bytes32 memory array to uint256 memory array
    187
        function _castToUint256Array(bytes32[] memory input) private pure returns (uint256[] memory output) {
    188
            assembly {
    189
                output := input
    190
            }
    191
        }
    192
    
                                                    
                                                
    193
        /// @dev Helper: low level cast address comp function to uint256 comp function
    194
        function _castToUint256Comp(
    195
            function(address, address) pure returns (bool) input
    196
        ) private pure returns (function(uint256, uint256) pure returns (bool) output) {
    197
            assembly {
    198
                output := input
    199
            }
    200
        }
    201
    
                                                    
                                                
    202
        /// @dev Helper: low level cast bytes32 comp function to uint256 comp function
    203
        function _castToUint256Comp(
    204
            function(bytes32, bytes32) pure returns (bool) input
    205
        ) private pure returns (function(uint256, uint256) pure returns (bool) output) {
    206
            assembly {
    207
                output := input
    208
            }
    209
        }
    210
    
                                                    
                                                
    211
        /**
    212
         * @dev Searches a sorted `array` and returns the first index that contains
    213
         * a value greater or equal to `element`. If no such index exists (i.e. all
    214
         * values in the array are strictly less than `element`), the array length is
    215
         * returned. Time complexity O(log n).
    216
         *
    217
         * NOTE: The `array` is expected to be sorted in ascending order, and to
    218
         * contain no repeated elements.
    219
         *
    220
         * IMPORTANT: Deprecated. This implementation behaves as {lowerBound} but lacks
    221
         * support for repeated elements in the array. The {lowerBound} function should
    222
         * be used instead.
    223
         */
    224
        function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
    225
            uint256 low = 0;
    226
            uint256 high = array.length;
    227
    
                                                    
                                                
    228
            if (high == 0) {
    229
                return 0;
    230
            }
    231
    
                                                    
                                                
    232
            while (low < high) {
    233
                uint256 mid = Math.average(low, high);
    234
    
                                                    
                                                
    235
                // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
    236
                // because Math.average rounds towards zero (it does integer division with truncation).
    237
                if (unsafeAccess(array, mid).value > element) {
    238
                    high = mid;
    239
                } else {
    240
                    low = mid + 1;
    241
                }
    242
            }
    243
    
                                                    
                                                
    244
            // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
    245
            if (low > 0 && unsafeAccess(array, low - 1).value == element) {
    246
                return low - 1;
    247
            } else {
    248
                return low;
    249
            }
    250
        }
    251
    
                                                    
                                                
    252
        /**
    253
         * @dev Searches an `array` sorted in ascending order and returns the first
    254
         * index that contains a value greater or equal than `element`. If no such index
    255
         * exists (i.e. all values in the array are strictly less than `element`), the array
    256
         * length is returned. Time complexity O(log n).
    257
         *
    258
         * See C++'s https://en.cppreference.com/w/cpp/algorithm/lower_bound[lower_bound].
    259
         */
    260
        function lowerBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
    261
            uint256 low = 0;
    262
            uint256 high = array.length;
    263
    
                                                    
                                                
    264
            if (high == 0) {
    265
                return 0;
    266
            }
    267
    
                                                    
                                                
    268
            while (low < high) {
    269
                uint256 mid = Math.average(low, high);
    270
    
                                                    
                                                
    271
                // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
    272
                // because Math.average rounds towards zero (it does integer division with truncation).
    273
                if (unsafeAccess(array, mid).value < element) {
    274
                    // this cannot overflow because mid < high
    275
                    unchecked {
    276
                        low = mid + 1;
    277
                    }
    278
                } else {
    279
                    high = mid;
    280
                }
    281
            }
    282
    
                                                    
                                                
    283
            return low;
    284
        }
    285
    
                                                    
                                                
    286
        /**
    287
         * @dev Searches an `array` sorted in ascending order and returns the first
    288
         * index that contains a value strictly greater than `element`. If no such index
    289
         * exists (i.e. all values in the array are strictly less than `element`), the array
    290
         * length is returned. Time complexity O(log n).
    291
         *
    292
         * See C++'s https://en.cppreference.com/w/cpp/algorithm/upper_bound[upper_bound].
    293
         */
    294
        function upperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
    295
            uint256 low = 0;
    296
            uint256 high = array.length;
    297
    
                                                    
                                                
    298
            if (high == 0) {
    299
                return 0;
    300
            }
    301
    
                                                    
                                                
    302
            while (low < high) {
    303
                uint256 mid = Math.average(low, high);
    304
    
                                                    
                                                
    305
                // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
    306
                // because Math.average rounds towards zero (it does integer division with truncation).
    307
                if (unsafeAccess(array, mid).value > element) {
    308
                    high = mid;
    309
                } else {
    310
                    // this cannot overflow because mid < high
    311
                    unchecked {
    312
                        low = mid + 1;
    313
                    }
    314
                }
    315
            }
    316
    
                                                    
                                                
    317
            return low;
    318
        }
    319
    
                                                    
                                                
    320
        /**
    321
         * @dev Same as {lowerBound}, but with an array in memory.
    322
         */
    323
        function lowerBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
    324
            uint256 low = 0;
    325
            uint256 high = array.length;
    326
    
                                                    
                                                
    327
            if (high == 0) {
    328
                return 0;
    329
            }
    330
    
                                                    
                                                
    331
            while (low < high) {
    332
                uint256 mid = Math.average(low, high);
    333
    
                                                    
                                                
    334
                // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
    335
                // because Math.average rounds towards zero (it does integer division with truncation).
    336
                if (unsafeMemoryAccess(array, mid) < element) {
    337
                    // this cannot overflow because mid < high
    338
                    unchecked {
    339
                        low = mid + 1;
    340
                    }
    341
                } else {
    342
                    high = mid;
    343
                }
    344
            }
    345
    
                                                    
                                                
    346
            return low;
    347
        }
    348
    
                                                    
                                                
    349
        /**
    350
         * @dev Same as {upperBound}, but with an array in memory.
    351
         */
    352
        function upperBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
    353
            uint256 low = 0;
    354
            uint256 high = array.length;
    355
    
                                                    
                                                
    356
            if (high == 0) {
    357
                return 0;
    358
            }
    359
    
                                                    
                                                
    360
            while (low < high) {
    361
                uint256 mid = Math.average(low, high);
    362
    
                                                    
                                                
    363
                // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
    364
                // because Math.average rounds towards zero (it does integer division with truncation).
    365
                if (unsafeMemoryAccess(array, mid) > element) {
    366
                    high = mid;
    367
                } else {
    368
                    // this cannot overflow because mid < high
    369
                    unchecked {
    370
                        low = mid + 1;
    371
                    }
    372
                }
    373
            }
    374
    
                                                    
                                                
    375
            return low;
    376
        }
    377
    
                                                    
                                                
    378
        /**
    379
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    380
         *
    381
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    382
         */
    383
        function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
    384
            bytes32 slot;
    385
            assembly ("memory-safe") {
    386
                slot := arr.slot
    387
            }
    388
            return slot.deriveArray().offset(pos).getAddressSlot();
    389
        }
    390
    
                                                    
                                                
    391
        /**
    392
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    393
         *
    394
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    395
         */
    396
        function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
    397
            bytes32 slot;
    398
            assembly ("memory-safe") {
    399
                slot := arr.slot
    400
            }
    401
            return slot.deriveArray().offset(pos).getBytes32Slot();
    402
        }
    403
    
                                                    
                                                
    404
        /**
    405
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    406
         *
    407
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    408
         */
    409
        function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
    410
            bytes32 slot;
    411
            assembly ("memory-safe") {
    412
                slot := arr.slot
    413
            }
    414
            return slot.deriveArray().offset(pos).getUint256Slot();
    415
        }
    416
    
                                                    
                                                
    417
        /**
    418
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    419
         *
    420
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    421
         */
    422
        function unsafeAccess(bytes[] storage arr, uint256 pos) internal pure returns (StorageSlot.BytesSlot storage) {
    423
            bytes32 slot;
    424
            assembly ("memory-safe") {
    425
                slot := arr.slot
    426
            }
    427
            return slot.deriveArray().offset(pos).getBytesSlot();
    428
        }
    429
    
                                                    
                                                
    430
        /**
    431
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    432
         *
    433
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    434
         */
    435
        function unsafeAccess(string[] storage arr, uint256 pos) internal pure returns (StorageSlot.StringSlot storage) {
    436
            bytes32 slot;
    437
            assembly ("memory-safe") {
    438
                slot := arr.slot
    439
            }
    440
            return slot.deriveArray().offset(pos).getStringSlot();
    441
        }
    442
    
                                                    
                                                
    443
        /**
    444
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    445
         *
    446
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    447
         */
    448
        function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
    449
            assembly {
    450
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
    451
            }
    452
        }
    453
    
                                                    
                                                
    454
        /**
    455
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    456
         *
    457
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    458
         */
    459
        function unsafeMemoryAccess(bytes32[] memory arr, uint256 pos) internal pure returns (bytes32 res) {
    460
            assembly {
    461
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
    462
            }
    463
        }
    464
    
                                                    
                                                
    465
        /**
    466
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    467
         *
    468
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    469
         */
    470
        function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
    471
            assembly {
    472
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
    473
            }
    474
        }
    475
    
                                                    
                                                
    476
        /**
    477
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    478
         *
    479
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    480
         */
    481
        function unsafeMemoryAccess(bytes[] memory arr, uint256 pos) internal pure returns (bytes memory res) {
    482
            assembly {
    483
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
    484
            }
    485
        }
    486
    
                                                    
                                                
    487
        /**
    488
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
    489
         *
    490
         * WARNING: Only use if you are certain `pos` is lower than the array length.
    491
         */
    492
        function unsafeMemoryAccess(string[] memory arr, uint256 pos) internal pure returns (string memory res) {
    493
            assembly {
    494
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
    495
            }
    496
        }
    497
    
                                                    
                                                
    498
        /**
    499
         * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
    500
         *
    501
         * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
    502
         */
    503
        function unsafeSetLength(address[] storage array, uint256 len) internal {
    504
            assembly ("memory-safe") {
    505
                sstore(array.slot, len)
    506
            }
    507
        }
    508
    
                                                    
                                                
    509
        /**
    510
         * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
    511
         *
    512
         * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
    513
         */
    514
        function unsafeSetLength(bytes32[] storage array, uint256 len) internal {
    515
            assembly ("memory-safe") {
    516
                sstore(array.slot, len)
    517
            }
    518
        }
    519
    
                                                    
                                                
    520
        /**
    521
         * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
    522
         *
    523
         * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
    524
         */
    525
        function unsafeSetLength(uint256[] storage array, uint256 len) internal {
    526
            assembly ("memory-safe") {
    527
                sstore(array.slot, len)
    528
            }
    529
        }
    530
    
                                                    
                                                
    531
        /**
    532
         * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
    533
         *
    534
         * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
    535
         */
    536
        function unsafeSetLength(bytes[] storage array, uint256 len) internal {
    537
            assembly ("memory-safe") {
    538
                sstore(array.slot, len)
    539
            }
    540
        }
    541
    
                                                    
                                                
    542
        /**
    543
         * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
    544
         *
    545
         * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
    546
         */
    547
        function unsafeSetLength(string[] storage array, uint256 len) internal {
    548
            assembly ("memory-safe") {
    549
                sstore(array.slot, len)
    550
            }
    551
        }
    552
    }
    553
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/Comparators.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/Comparators.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev Provides a set of functions to compare values.
    8
     *
    9
     * _Available since v5.1._
    10
     */
    11
    ✓ 25.1K
    ⟲ 25.1K
    library Comparators {
    12
        function lt(uint256 a, uint256 b) internal pure returns (bool) {
    13
            return a < b;
    14
        }
    15
    
                                                    
                                                
    16
        function gt(uint256 a, uint256 b) internal pure returns (bool) {
    17
            return a > b;
    18
        }
    19
    }
    20
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/Context.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev Provides information about the current execution context, including the
    8
     * sender of the transaction and its data. While these are generally available
    9
     * via msg.sender and msg.data, they should not be accessed in such a direct
    10
     * manner, since when dealing with meta-transactions the account sending and
    11
     * paying for execution may not be the actual sender (as far as an application
    12
     * is concerned).
    13
     *
    14
     * This contract is only required for intermediate, library-like contracts.
    15
     */
    16
    abstract contract Context {
    17
        function _msgSender() internal view virtual returns (address) {
    18
    ✓ 1.2M
            return msg.sender;
    19
        }
    20
    
                                                    
                                                
    21
        function _msgData() internal view virtual returns (bytes calldata) {
    22
            return msg.data;
    23
        }
    24
    
                                                    
                                                
    25
        function _contextSuffixLength() internal view virtual returns (uint256) {
    26
            return 0;
    27
        }
    28
    }
    29
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/Create2.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {Errors} from "./Errors.sol";
    7
    
                                                    
                                                
    8
    /**
    9
     * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
    10
     * `CREATE2` can be used to compute in advance the address where a smart
    11
     * contract will be deployed, which allows for interesting new mechanisms known
    12
     * as 'counterfactual interactions'.
    13
     *
    14
     * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
    15
     * information.
    16
     */
    17
    ✓ 25.8K
    ⟲ 25.8K
    library Create2 {
    18
        /**
    19
         * @dev There's no code to deploy.
    20
         */
    21
        error Create2EmptyBytecode();
    22
    
                                                    
                                                
    23
        /**
    24
         * @dev Deploys a contract using `CREATE2`. The address where the contract
    25
         * will be deployed can be known in advance via {computeAddress}.
    26
         *
    27
         * The bytecode for a contract can be obtained from Solidity with
    28
         * `type(contractName).creationCode`.
    29
         *
    30
         * Requirements:
    31
         *
    32
         * - `bytecode` must not be empty.
    33
         * - `salt` must have not been used for `bytecode` already.
    34
         * - the factory must have a balance of at least `amount`.
    35
         * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
    36
         */
    37
        function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
    38
            if (address(this).balance < amount) {
    39
                revert Errors.InsufficientBalance(address(this).balance, amount);
    40
            }
    41
            if (bytecode.length == 0) {
    42
                revert Create2EmptyBytecode();
    43
            }
    44
            assembly ("memory-safe") {
    45
                addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
    46
                // if no address was created, and returndata is not empty, bubble revert
    47
                if and(iszero(addr), not(iszero(returndatasize()))) {
    48
                    let p := mload(0x40)
    49
                    returndatacopy(p, 0, returndatasize())
    50
                    revert(p, returndatasize())
    51
                }
    52
            }
    53
            if (addr == address(0)) {
    54
                revert Errors.FailedDeployment();
    55
            }
    56
        }
    57
    
                                                    
                                                
    58
        /**
    59
         * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
    60
         * `bytecodeHash` or `salt` will result in a new destination address.
    61
         */
    62
        function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
    63
            return computeAddress(salt, bytecodeHash, address(this));
    64
        }
    65
    
                                                    
                                                
    66
        /**
    67
         * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
    68
         * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
    69
         */
    70
        function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
    71
            assembly ("memory-safe") {
    72
                let ptr := mload(0x40) // Get free memory pointer
    73
    
                                                    
                                                
    74
                // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
    75
                // |-------------------|---------------------------------------------------------------------------|
    76
                // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
    77
                // | salt              |                                      BBBBBBBBBBBBB...BB                   |
    78
                // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
    79
                // | 0xFF              |            FF                                                             |
    80
                // |-------------------|---------------------------------------------------------------------------|
    81
                // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
    82
                // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
    83
    
                                                    
                                                
    84
                mstore(add(ptr, 0x40), bytecodeHash)
    85
                mstore(add(ptr, 0x20), salt)
    86
                mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
    87
                let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
    88
                mstore8(start, 0xff)
    89
                addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff)
    90
            }
    91
        }
    92
    }
    93
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/Errors.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev Collection of common custom errors used in multiple contracts
    8
     *
    9
     * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
    10
     * It is recommended to avoid relying on the error API for critical functionality.
    11
     *
    12
     * _Available since v5.1._
    13
     */
    14
    ✓ 19.4K
    ⟲ 19.4K
    library Errors {
    15
        /**
    16
         * @dev The ETH balance of the account is not enough to perform the operation.
    17
         */
    18
        error InsufficientBalance(uint256 balance, uint256 needed);
    19
    
                                                    
                                                
    20
        /**
    21
         * @dev A call to an address target failed. The target may have reverted.
    22
         */
    23
        error FailedCall();
    24
    
                                                    
                                                
    25
        /**
    26
         * @dev The deployment failed.
    27
         */
    28
        error FailedDeployment();
    29
    
                                                    
                                                
    30
        /**
    31
         * @dev A necessary precompile is missing.
    32
         */
    33
        error MissingPrecompile(address);
    34
    }
    35
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/Panic.sol
    Lines covered: 5 / 5 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev Helper library for emitting standardized panic codes.
    8
     *
    9
     * ```solidity
    10
     * contract Example {
    11
     *      using Panic for uint256;
    12
     *
    13
     *      // Use any of the declared internal constants
    14
     *      function foo() { Panic.GENERIC.panic(); }
    15
     *
    16
     *      // Alternatively
    17
     *      function foo() { Panic.panic(Panic.GENERIC); }
    18
     * }
    19
     * ```
    20
     *
    21
     * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
    22
     *
    23
     * _Available since v5.1._
    24
     */
    25
    // slither-disable-next-line unused-state
    26
    ✓ 23
    library Panic {
    27
        /// @dev generic / unspecified error
    28
        uint256 internal constant GENERIC = 0x00;
    29
        /// @dev used by the assert() builtin
    30
        uint256 internal constant ASSERT = 0x01;
    31
        /// @dev arithmetic underflow or overflow
    32
    ✓ 189.0K
        uint256 internal constant UNDER_OVERFLOW = 0x11;
    33
        /// @dev division or modulo by zero
    34
        uint256 internal constant DIVISION_BY_ZERO = 0x12;
    35
        /// @dev enum conversion error
    36
        uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
    37
        /// @dev invalid encoding in storage
    38
        uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
    39
        /// @dev empty array pop
    40
        uint256 internal constant EMPTY_ARRAY_POP = 0x31;
    41
        /// @dev array out of bounds access
    42
        uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    43
        /// @dev resource error (too large allocation or too large array)
    44
        uint256 internal constant RESOURCE_ERROR = 0x41;
    45
        /// @dev calling invalid internal function
    46
        uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
    47
    
                                                    
                                                
    48
        /// @dev Reverts with a panic code. Recommended to use with
    49
        /// the internal constants with predefined codes.
    50
        function panic(uint256 code) internal pure {
    51
            assembly ("memory-safe") {
    52
    ✓ 189.0K
                mstore(0x00, 0x4e487b71)
    53
    ✓ 189.0K
                mstore(0x20, code)
    54
    ✓ 189.0K
                revert(0x1c, 0x24)
    55
            }
    56
        }
    57
    }
    58
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/SlotDerivation.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (utils/SlotDerivation.sol)
    3
    // This file was procedurally generated from scripts/generate/templates/SlotDerivation.js.
    4
    
                                                    
                                                
    5
    pragma solidity ^0.8.20;
    6
    
                                                    
                                                
    7
    /**
    8
     * @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots
    9
     * corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by
    10
     * the solidity language / compiler.
    11
     *
    12
     * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.].
    13
     *
    14
     * Example usage:
    15
     * ```solidity
    16
     * contract Example {
    17
     *     // Add the library methods
    18
     *     using StorageSlot for bytes32;
    19
     *     using SlotDerivation for bytes32;
    20
     *
    21
     *     // Declare a namespace
    22
     *     string private constant _NAMESPACE = "<namespace>"; // eg. OpenZeppelin.Slot
    23
     *
    24
     *     function setValueInNamespace(uint256 key, address newValue) internal {
    25
     *         _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value = newValue;
    26
     *     }
    27
     *
    28
     *     function getValueInNamespace(uint256 key) internal view returns (address) {
    29
     *         return _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value;
    30
     *     }
    31
     * }
    32
     * ```
    33
     *
    34
     * TIP: Consider using this library along with {StorageSlot}.
    35
     *
    36
     * NOTE: This library provides a way to manipulate storage locations in a non-standard way. Tooling for checking
    37
     * upgrade safety will ignore the slots accessed through this library.
    38
     *
    39
     * _Available since v5.1._
    40
     */
    41
    ✓ 22.2K
    ⟲ 22.2K
    library SlotDerivation {
    42
        /**
    43
         * @dev Derive an ERC-7201 slot from a string (namespace).
    44
         */
    45
        function erc7201Slot(string memory namespace) internal pure returns (bytes32 slot) {
    46
            assembly ("memory-safe") {
    47
                mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
    48
                slot := and(keccak256(0x00, 0x20), not(0xff))
    49
            }
    50
        }
    51
    
                                                    
                                                
    52
        /**
    53
         * @dev Add an offset to a slot to get the n-th element of a structure or an array.
    54
         */
    55
        function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) {
    56
            unchecked {
    57
                return bytes32(uint256(slot) + pos);
    58
            }
    59
        }
    60
    
                                                    
                                                
    61
        /**
    62
         * @dev Derive the location of the first element in an array from the slot where the length is stored.
    63
         */
    64
        function deriveArray(bytes32 slot) internal pure returns (bytes32 result) {
    65
            assembly ("memory-safe") {
    66
                mstore(0x00, slot)
    67
                result := keccak256(0x00, 0x20)
    68
            }
    69
        }
    70
    
                                                    
                                                
    71
        /**
    72
         * @dev Derive the location of a mapping element from the key.
    73
         */
    74
        function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) {
    75
            assembly ("memory-safe") {
    76
                mstore(0x00, and(key, shr(96, not(0))))
    77
                mstore(0x20, slot)
    78
                result := keccak256(0x00, 0x40)
    79
            }
    80
        }
    81
    
                                                    
                                                
    82
        /**
    83
         * @dev Derive the location of a mapping element from the key.
    84
         */
    85
        function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) {
    86
            assembly ("memory-safe") {
    87
                mstore(0x00, iszero(iszero(key)))
    88
                mstore(0x20, slot)
    89
                result := keccak256(0x00, 0x40)
    90
            }
    91
        }
    92
    
                                                    
                                                
    93
        /**
    94
         * @dev Derive the location of a mapping element from the key.
    95
         */
    96
        function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) {
    97
            assembly ("memory-safe") {
    98
                mstore(0x00, key)
    99
                mstore(0x20, slot)
    100
                result := keccak256(0x00, 0x40)
    101
            }
    102
        }
    103
    
                                                    
                                                
    104
        /**
    105
         * @dev Derive the location of a mapping element from the key.
    106
         */
    107
        function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) {
    108
            assembly ("memory-safe") {
    109
                mstore(0x00, key)
    110
                mstore(0x20, slot)
    111
                result := keccak256(0x00, 0x40)
    112
            }
    113
        }
    114
    
                                                    
                                                
    115
        /**
    116
         * @dev Derive the location of a mapping element from the key.
    117
         */
    118
        function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) {
    119
            assembly ("memory-safe") {
    120
                mstore(0x00, key)
    121
                mstore(0x20, slot)
    122
                result := keccak256(0x00, 0x40)
    123
            }
    124
        }
    125
    
                                                    
                                                
    126
        /**
    127
         * @dev Derive the location of a mapping element from the key.
    128
         */
    129
        function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) {
    130
            assembly ("memory-safe") {
    131
                let length := mload(key)
    132
                let begin := add(key, 0x20)
    133
                let end := add(begin, length)
    134
                let cache := mload(end)
    135
                mstore(end, slot)
    136
                result := keccak256(begin, add(length, 0x20))
    137
                mstore(end, cache)
    138
            }
    139
        }
    140
    
                                                    
                                                
    141
        /**
    142
         * @dev Derive the location of a mapping element from the key.
    143
         */
    144
        function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) {
    145
            assembly ("memory-safe") {
    146
                let length := mload(key)
    147
                let begin := add(key, 0x20)
    148
                let end := add(begin, length)
    149
                let cache := mload(end)
    150
                mstore(end, slot)
    151
                result := keccak256(begin, add(length, 0x20))
    152
                mstore(end, cache)
    153
            }
    154
        }
    155
    }
    156
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
    3
    // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
    4
    
                                                    
                                                
    5
    pragma solidity ^0.8.20;
    6
    
                                                    
                                                
    7
    /**
    8
     * @dev Library for reading and writing primitive types to specific storage slots.
    9
     *
    10
     * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
    11
     * This library helps with reading and writing to such slots without the need for inline assembly.
    12
     *
    13
     * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
    14
     *
    15
     * Example usage to set ERC-1967 implementation slot:
    16
     * ```solidity
    17
     * contract ERC1967 {
    18
     *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
    19
     *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    20
     *
    21
     *     function _getImplementation() internal view returns (address) {
    22
     *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    23
     *     }
    24
     *
    25
     *     function _setImplementation(address newImplementation) internal {
    26
     *         require(newImplementation.code.length > 0);
    27
     *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    28
     *     }
    29
     * }
    30
     * ```
    31
     *
    32
     * TIP: Consider using this library along with {SlotDerivation}.
    33
     */
    34
    ✓ 34.7K
    ⟲ 34.7K
    library StorageSlot {
    35
        struct AddressSlot {
    36
            address value;
    37
        }
    38
    
                                                    
                                                
    39
        struct BooleanSlot {
    40
            bool value;
    41
        }
    42
    
                                                    
                                                
    43
        struct Bytes32Slot {
    44
            bytes32 value;
    45
        }
    46
    
                                                    
                                                
    47
        struct Uint256Slot {
    48
            uint256 value;
    49
        }
    50
    
                                                    
                                                
    51
        struct Int256Slot {
    52
            int256 value;
    53
        }
    54
    
                                                    
                                                
    55
        struct StringSlot {
    56
            string value;
    57
        }
    58
    
                                                    
                                                
    59
        struct BytesSlot {
    60
            bytes value;
    61
        }
    62
    
                                                    
                                                
    63
        /**
    64
         * @dev Returns an `AddressSlot` with member `value` located at `slot`.
    65
         */
    66
        function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
    67
            assembly ("memory-safe") {
    68
                r.slot := slot
    69
            }
    70
        }
    71
    
                                                    
                                                
    72
        /**
    73
         * @dev Returns a `BooleanSlot` with member `value` located at `slot`.
    74
         */
    75
        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
    76
            assembly ("memory-safe") {
    77
                r.slot := slot
    78
            }
    79
        }
    80
    
                                                    
                                                
    81
        /**
    82
         * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
    83
         */
    84
        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
    85
            assembly ("memory-safe") {
    86
                r.slot := slot
    87
            }
    88
        }
    89
    
                                                    
                                                
    90
        /**
    91
         * @dev Returns a `Uint256Slot` with member `value` located at `slot`.
    92
         */
    93
        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
    94
            assembly ("memory-safe") {
    95
                r.slot := slot
    96
            }
    97
        }
    98
    
                                                    
                                                
    99
        /**
    100
         * @dev Returns a `Int256Slot` with member `value` located at `slot`.
    101
         */
    102
        function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
    103
            assembly ("memory-safe") {
    104
                r.slot := slot
    105
            }
    106
        }
    107
    
                                                    
                                                
    108
        /**
    109
         * @dev Returns a `StringSlot` with member `value` located at `slot`.
    110
         */
    111
        function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
    112
            assembly ("memory-safe") {
    113
                r.slot := slot
    114
            }
    115
        }
    116
    
                                                    
                                                
    117
        /**
    118
         * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
    119
         */
    120
        function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
    121
            assembly ("memory-safe") {
    122
                r.slot := store.slot
    123
            }
    124
        }
    125
    
                                                    
                                                
    126
        /**
    127
         * @dev Returns a `BytesSlot` with member `value` located at `slot`.
    128
         */
    129
        function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
    130
            assembly ("memory-safe") {
    131
                r.slot := slot
    132
            }
    133
        }
    134
    
                                                    
                                                
    135
        /**
    136
         * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
    137
         */
    138
        function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
    139
            assembly ("memory-safe") {
    140
                r.slot := store.slot
    141
            }
    142
        }
    143
    }
    144
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/Strings.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (utils/Strings.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {Math} from "./math/Math.sol";
    7
    import {SafeCast} from "./math/SafeCast.sol";
    8
    import {SignedMath} from "./math/SignedMath.sol";
    9
    
                                                    
                                                
    10
    /**
    11
     * @dev String operations.
    12
     */
    13
    ✓ 18.1K
    ⟲ 18.1K
    library Strings {
    14
        using SafeCast for *;
    15
    
                                                    
                                                
    16
        bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    17
        uint8 private constant ADDRESS_LENGTH = 20;
    18
        uint256 private constant SPECIAL_CHARS_LOOKUP =
    19
            (1 << 0x08) | // backspace
    20
                (1 << 0x09) | // tab
    21
                (1 << 0x0a) | // newline
    22
                (1 << 0x0c) | // form feed
    23
                (1 << 0x0d) | // carriage return
    24
                (1 << 0x22) | // double quote
    25
                (1 << 0x5c); // backslash
    26
    
                                                    
                                                
    27
        /**
    28
         * @dev The `value` string doesn't fit in the specified `length`.
    29
         */
    30
        error StringsInsufficientHexLength(uint256 value, uint256 length);
    31
    
                                                    
                                                
    32
        /**
    33
         * @dev The string being parsed contains characters that are not in scope of the given base.
    34
         */
    35
        error StringsInvalidChar();
    36
    
                                                    
                                                
    37
        /**
    38
         * @dev The string being parsed is not a properly formatted address.
    39
         */
    40
        error StringsInvalidAddressFormat();
    41
    
                                                    
                                                
    42
        /**
    43
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
    44
         */
    45
        function toString(uint256 value) internal pure returns (string memory) {
    46
            unchecked {
    47
                uint256 length = Math.log10(value) + 1;
    48
                string memory buffer = new string(length);
    49
                uint256 ptr;
    50
                assembly ("memory-safe") {
    51
                    ptr := add(add(buffer, 0x20), length)
    52
                }
    53
                while (true) {
    54
                    ptr--;
    55
                    assembly ("memory-safe") {
    56
                        mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
    57
                    }
    58
                    value /= 10;
    59
                    if (value == 0) break;
    60
                }
    61
                return buffer;
    62
            }
    63
        }
    64
    
                                                    
                                                
    65
        /**
    66
         * @dev Converts a `int256` to its ASCII `string` decimal representation.
    67
         */
    68
        function toStringSigned(int256 value) internal pure returns (string memory) {
    69
            return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    70
        }
    71
    
                                                    
                                                
    72
        /**
    73
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
    74
         */
    75
        function toHexString(uint256 value) internal pure returns (string memory) {
    76
            unchecked {
    77
                return toHexString(value, Math.log256(value) + 1);
    78
            }
    79
        }
    80
    
                                                    
                                                
    81
        /**
    82
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
    83
         */
    84
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
    85
            uint256 localValue = value;
    86
            bytes memory buffer = new bytes(2 * length + 2);
    87
            buffer[0] = "0";
    88
            buffer[1] = "x";
    89
            for (uint256 i = 2 * length + 1; i > 1; --i) {
    90
                buffer[i] = HEX_DIGITS[localValue & 0xf];
    91
                localValue >>= 4;
    92
            }
    93
            if (localValue != 0) {
    94
                revert StringsInsufficientHexLength(value, length);
    95
            }
    96
            return string(buffer);
    97
        }
    98
    
                                                    
                                                
    99
        /**
    100
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
    101
         * representation.
    102
         */
    103
        function toHexString(address addr) internal pure returns (string memory) {
    104
            return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    105
        }
    106
    
                                                    
                                                
    107
        /**
    108
         * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
    109
         * representation, according to EIP-55.
    110
         */
    111
        function toChecksumHexString(address addr) internal pure returns (string memory) {
    112
            bytes memory buffer = bytes(toHexString(addr));
    113
    
                                                    
                                                
    114
            // hash the hex part of buffer (skip length + 2 bytes, length 40)
    115
            uint256 hashValue;
    116
            assembly ("memory-safe") {
    117
                hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
    118
            }
    119
    
                                                    
                                                
    120
            for (uint256 i = 41; i > 1; --i) {
    121
                // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
    122
                if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
    123
                    // case shift by xoring with 0x20
    124
                    buffer[i] ^= 0x20;
    125
                }
    126
                hashValue >>= 4;
    127
            }
    128
            return string(buffer);
    129
        }
    130
    
                                                    
                                                
    131
        /**
    132
         * @dev Converts a `bytes` buffer to its ASCII `string` hexadecimal representation.
    133
         */
    134
        function toHexString(bytes memory input) internal pure returns (string memory) {
    135
            unchecked {
    136
                bytes memory buffer = new bytes(2 * input.length + 2);
    137
                buffer[0] = "0";
    138
                buffer[1] = "x";
    139
                for (uint256 i = 0; i < input.length; ++i) {
    140
                    uint8 v = uint8(input[i]);
    141
                    buffer[2 * i + 2] = HEX_DIGITS[v >> 4];
    142
                    buffer[2 * i + 3] = HEX_DIGITS[v & 0xf];
    143
                }
    144
                return string(buffer);
    145
            }
    146
        }
    147
    
                                                    
                                                
    148
        /**
    149
         * @dev Returns true if the two strings are equal.
    150
         */
    151
        function equal(string memory a, string memory b) internal pure returns (bool) {
    152
            return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    153
        }
    154
    
                                                    
                                                
    155
        /**
    156
         * @dev Parse a decimal string and returns the value as a `uint256`.
    157
         *
    158
         * Requirements:
    159
         * - The string must be formatted as `[0-9]*`
    160
         * - The result must fit into an `uint256` type
    161
         */
    162
        function parseUint(string memory input) internal pure returns (uint256) {
    163
            return parseUint(input, 0, bytes(input).length);
    164
        }
    165
    
                                                    
                                                
    166
        /**
    167
         * @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
    168
         * `end` (excluded).
    169
         *
    170
         * Requirements:
    171
         * - The substring must be formatted as `[0-9]*`
    172
         * - The result must fit into an `uint256` type
    173
         */
    174
        function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
    175
            (bool success, uint256 value) = tryParseUint(input, begin, end);
    176
            if (!success) revert StringsInvalidChar();
    177
            return value;
    178
        }
    179
    
                                                    
                                                
    180
        /**
    181
         * @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
    182
         *
    183
         * NOTE: This function will revert if the result does not fit in a `uint256`.
    184
         */
    185
        function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
    186
            return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
    187
        }
    188
    
                                                    
                                                
    189
        /**
    190
         * @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
    191
         * character.
    192
         *
    193
         * NOTE: This function will revert if the result does not fit in a `uint256`.
    194
         */
    195
        function tryParseUint(
    196
            string memory input,
    197
            uint256 begin,
    198
            uint256 end
    199
        ) internal pure returns (bool success, uint256 value) {
    200
            if (end > bytes(input).length || begin > end) return (false, 0);
    201
            return _tryParseUintUncheckedBounds(input, begin, end);
    202
        }
    203
    
                                                    
                                                
    204
        /**
    205
         * @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
    206
         * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
    207
         */
    208
        function _tryParseUintUncheckedBounds(
    209
            string memory input,
    210
            uint256 begin,
    211
            uint256 end
    212
        ) private pure returns (bool success, uint256 value) {
    213
            bytes memory buffer = bytes(input);
    214
    
                                                    
                                                
    215
            uint256 result = 0;
    216
            for (uint256 i = begin; i < end; ++i) {
    217
                uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
    218
                if (chr > 9) return (false, 0);
    219
                result *= 10;
    220
                result += chr;
    221
            }
    222
            return (true, result);
    223
        }
    224
    
                                                    
                                                
    225
        /**
    226
         * @dev Parse a decimal string and returns the value as a `int256`.
    227
         *
    228
         * Requirements:
    229
         * - The string must be formatted as `[-+]?[0-9]*`
    230
         * - The result must fit in an `int256` type.
    231
         */
    232
        function parseInt(string memory input) internal pure returns (int256) {
    233
            return parseInt(input, 0, bytes(input).length);
    234
        }
    235
    
                                                    
                                                
    236
        /**
    237
         * @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
    238
         * `end` (excluded).
    239
         *
    240
         * Requirements:
    241
         * - The substring must be formatted as `[-+]?[0-9]*`
    242
         * - The result must fit in an `int256` type.
    243
         */
    244
        function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
    245
            (bool success, int256 value) = tryParseInt(input, begin, end);
    246
            if (!success) revert StringsInvalidChar();
    247
            return value;
    248
        }
    249
    
                                                    
                                                
    250
        /**
    251
         * @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
    252
         * the result does not fit in a `int256`.
    253
         *
    254
         * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
    255
         */
    256
        function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
    257
            return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
    258
        }
    259
    
                                                    
                                                
    260
        uint256 private constant ABS_MIN_INT256 = 2 ** 255;
    261
    
                                                    
                                                
    262
        /**
    263
         * @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
    264
         * character or if the result does not fit in a `int256`.
    265
         *
    266
         * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
    267
         */
    268
        function tryParseInt(
    269
            string memory input,
    270
            uint256 begin,
    271
            uint256 end
    272
        ) internal pure returns (bool success, int256 value) {
    273
            if (end > bytes(input).length || begin > end) return (false, 0);
    274
            return _tryParseIntUncheckedBounds(input, begin, end);
    275
        }
    276
    
                                                    
                                                
    277
        /**
    278
         * @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
    279
         * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
    280
         */
    281
        function _tryParseIntUncheckedBounds(
    282
            string memory input,
    283
            uint256 begin,
    284
            uint256 end
    285
        ) private pure returns (bool success, int256 value) {
    286
            bytes memory buffer = bytes(input);
    287
    
                                                    
                                                
    288
            // Check presence of a negative sign.
    289
            bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
    290
            bool positiveSign = sign == bytes1("+");
    291
            bool negativeSign = sign == bytes1("-");
    292
            uint256 offset = (positiveSign || negativeSign).toUint();
    293
    
                                                    
                                                
    294
            (bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);
    295
    
                                                    
                                                
    296
            if (absSuccess && absValue < ABS_MIN_INT256) {
    297
                return (true, negativeSign ? -int256(absValue) : int256(absValue));
    298
            } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
    299
                return (true, type(int256).min);
    300
            } else return (false, 0);
    301
        }
    302
    
                                                    
                                                
    303
        /**
    304
         * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
    305
         *
    306
         * Requirements:
    307
         * - The string must be formatted as `(0x)?[0-9a-fA-F]*`
    308
         * - The result must fit in an `uint256` type.
    309
         */
    310
        function parseHexUint(string memory input) internal pure returns (uint256) {
    311
            return parseHexUint(input, 0, bytes(input).length);
    312
        }
    313
    
                                                    
                                                
    314
        /**
    315
         * @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
    316
         * `end` (excluded).
    317
         *
    318
         * Requirements:
    319
         * - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
    320
         * - The result must fit in an `uint256` type.
    321
         */
    322
        function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
    323
            (bool success, uint256 value) = tryParseHexUint(input, begin, end);
    324
            if (!success) revert StringsInvalidChar();
    325
            return value;
    326
        }
    327
    
                                                    
                                                
    328
        /**
    329
         * @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
    330
         *
    331
         * NOTE: This function will revert if the result does not fit in a `uint256`.
    332
         */
    333
        function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
    334
            return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
    335
        }
    336
    
                                                    
                                                
    337
        /**
    338
         * @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
    339
         * invalid character.
    340
         *
    341
         * NOTE: This function will revert if the result does not fit in a `uint256`.
    342
         */
    343
        function tryParseHexUint(
    344
            string memory input,
    345
            uint256 begin,
    346
            uint256 end
    347
        ) internal pure returns (bool success, uint256 value) {
    348
            if (end > bytes(input).length || begin > end) return (false, 0);
    349
            return _tryParseHexUintUncheckedBounds(input, begin, end);
    350
        }
    351
    
                                                    
                                                
    352
        /**
    353
         * @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
    354
         * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
    355
         */
    356
        function _tryParseHexUintUncheckedBounds(
    357
            string memory input,
    358
            uint256 begin,
    359
            uint256 end
    360
        ) private pure returns (bool success, uint256 value) {
    361
            bytes memory buffer = bytes(input);
    362
    
                                                    
                                                
    363
            // skip 0x prefix if present
    364
            bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
    365
            uint256 offset = hasPrefix.toUint() * 2;
    366
    
                                                    
                                                
    367
            uint256 result = 0;
    368
            for (uint256 i = begin + offset; i < end; ++i) {
    369
                uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
    370
                if (chr > 15) return (false, 0);
    371
                result *= 16;
    372
                unchecked {
    373
                    // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
    374
                    // This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
    375
                    result += chr;
    376
                }
    377
            }
    378
            return (true, result);
    379
        }
    380
    
                                                    
                                                
    381
        /**
    382
         * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
    383
         *
    384
         * Requirements:
    385
         * - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
    386
         */
    387
        function parseAddress(string memory input) internal pure returns (address) {
    388
            return parseAddress(input, 0, bytes(input).length);
    389
        }
    390
    
                                                    
                                                
    391
        /**
    392
         * @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
    393
         * `end` (excluded).
    394
         *
    395
         * Requirements:
    396
         * - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
    397
         */
    398
        function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
    399
            (bool success, address value) = tryParseAddress(input, begin, end);
    400
            if (!success) revert StringsInvalidAddressFormat();
    401
            return value;
    402
        }
    403
    
                                                    
                                                
    404
        /**
    405
         * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
    406
         * formatted address. See {parseAddress-string} requirements.
    407
         */
    408
        function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
    409
            return tryParseAddress(input, 0, bytes(input).length);
    410
        }
    411
    
                                                    
                                                
    412
        /**
    413
         * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
    414
         * formatted address. See {parseAddress-string-uint256-uint256} requirements.
    415
         */
    416
        function tryParseAddress(
    417
            string memory input,
    418
            uint256 begin,
    419
            uint256 end
    420
        ) internal pure returns (bool success, address value) {
    421
            if (end > bytes(input).length || begin > end) return (false, address(0));
    422
    
                                                    
                                                
    423
            bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
    424
            uint256 expectedLength = 40 + hasPrefix.toUint() * 2;
    425
    
                                                    
                                                
    426
            // check that input is the correct length
    427
            if (end - begin == expectedLength) {
    428
                // length guarantees that this does not overflow, and value is at most type(uint160).max
    429
                (bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
    430
                return (s, address(uint160(v)));
    431
            } else {
    432
                return (false, address(0));
    433
            }
    434
        }
    435
    
                                                    
                                                
    436
        function _tryParseChr(bytes1 chr) private pure returns (uint8) {
    437
            uint8 value = uint8(chr);
    438
    
                                                    
                                                
    439
            // Try to parse `chr`:
    440
            // - Case 1: [0-9]
    441
            // - Case 2: [a-f]
    442
            // - Case 3: [A-F]
    443
            // - otherwise not supported
    444
            unchecked {
    445
                if (value > 47 && value < 58) value -= 48;
    446
                else if (value > 96 && value < 103) value -= 87;
    447
                else if (value > 64 && value < 71) value -= 55;
    448
                else return type(uint8).max;
    449
            }
    450
    
                                                    
                                                
    451
            return value;
    452
        }
    453
    
                                                    
                                                
    454
        /**
    455
         * @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
    456
         *
    457
         * WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
    458
         *
    459
         * NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
    460
         * RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
    461
         * characters that are not in this range, but other tooling may provide different results.
    462
         */
    463
        function escapeJSON(string memory input) internal pure returns (string memory) {
    464
            bytes memory buffer = bytes(input);
    465
            bytes memory output = new bytes(2 * buffer.length); // worst case scenario
    466
            uint256 outputLength = 0;
    467
    
                                                    
                                                
    468
            for (uint256 i; i < buffer.length; ++i) {
    469
                bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
    470
                if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
    471
                    output[outputLength++] = "\\";
    472
                    if (char == 0x08) output[outputLength++] = "b";
    473
                    else if (char == 0x09) output[outputLength++] = "t";
    474
                    else if (char == 0x0a) output[outputLength++] = "n";
    475
                    else if (char == 0x0c) output[outputLength++] = "f";
    476
                    else if (char == 0x0d) output[outputLength++] = "r";
    477
                    else if (char == 0x5c) output[outputLength++] = "\\";
    478
                    else if (char == 0x22) {
    479
                        // solhint-disable-next-line quotes
    480
                        output[outputLength++] = '"';
    481
                    }
    482
                } else {
    483
                    output[outputLength++] = char;
    484
                }
    485
            }
    486
            // write the actual length and deallocate unused memory
    487
            assembly ("memory-safe") {
    488
                mstore(output, outputLength)
    489
                mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
    490
            }
    491
    
                                                    
                                                
    492
            return string(output);
    493
        }
    494
    
                                                    
                                                
    495
        /**
    496
         * @dev Reads a bytes32 from a bytes array without bounds checking.
    497
         *
    498
         * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
    499
         * assembly block as such would prevent some optimizations.
    500
         */
    501
        function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
    502
            // This is not memory safe in the general case, but all calls to this private function are within bounds.
    503
            assembly ("memory-safe") {
    504
                value := mload(add(add(buffer, 0x20), offset))
    505
            }
    506
        }
    507
    }
    508
    
                                                    
                                                
    3.7% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol
    Lines covered: 1 / 27 (3.7%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
    8
     *
    9
     * These functions can be used to verify that a message was signed by the holder
    10
     * of the private keys of a given address.
    11
     */
    12
    ✓ 19.7K
    ⟲ 19.7K
    library ECDSA {
    13
        enum RecoverError {
    14
            NoError,
    15
            InvalidSignature,
    16
            InvalidSignatureLength,
    17
            InvalidSignatureS
    18
        }
    19
    
                                                    
                                                
    20
        /**
    21
         * @dev The signature derives the `address(0)`.
    22
         */
    23
        error ECDSAInvalidSignature();
    24
    
                                                    
                                                
    25
        /**
    26
         * @dev The signature has an invalid length.
    27
         */
    28
        error ECDSAInvalidSignatureLength(uint256 length);
    29
    
                                                    
                                                
    30
        /**
    31
         * @dev The signature has an S value that is in the upper half order.
    32
         */
    33
        error ECDSAInvalidSignatureS(bytes32 s);
    34
    
                                                    
                                                
    35
        /**
    36
         * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
    37
         * return address(0) without also returning an error description. Errors are documented using an enum (error type)
    38
         * and a bytes32 providing additional information about the error.
    39
         *
    40
         * If no error is returned, then the address can be used for verification purposes.
    41
         *
    42
         * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
    43
         * this function rejects them by requiring the `s` value to be in the lower
    44
         * half order, and the `v` value to be either 27 or 28.
    45
         *
    46
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
    47
         * verification to be secure: it is possible to craft signatures that
    48
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
    49
         * this is by receiving a hash of the original message (which may otherwise
    50
         * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
    51
         *
    52
         * Documentation for signature generation:
    53
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
    54
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
    55
         */
    56
        function tryRecover(
    57
            bytes32 hash,
    58
            bytes memory signature
    59
        ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
    60
            if (signature.length == 65) {
    61
                bytes32 r;
    62
                bytes32 s;
    63
                uint8 v;
    64
                // ecrecover takes the signature parameters, and the only way to get them
    65
                // currently is to use assembly.
    66
                assembly ("memory-safe") {
    67
                    r := mload(add(signature, 0x20))
    68
                    s := mload(add(signature, 0x40))
    69
                    v := byte(0, mload(add(signature, 0x60)))
    70
                }
    71
                return tryRecover(hash, v, r, s);
    72
            } else {
    73
                return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
    74
            }
    75
        }
    76
    
                                                    
                                                
    77
        /**
    78
         * @dev Returns the address that signed a hashed message (`hash`) with
    79
         * `signature`. This address can then be used for verification purposes.
    80
         *
    81
         * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
    82
         * this function rejects them by requiring the `s` value to be in the lower
    83
         * half order, and the `v` value to be either 27 or 28.
    84
         *
    85
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
    86
         * verification to be secure: it is possible to craft signatures that
    87
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
    88
         * this is by receiving a hash of the original message (which may otherwise
    89
         * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
    90
         */
    91
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
    92
            (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
    93
            _throwError(error, errorArg);
    94
            return recovered;
    95
        }
    96
    
                                                    
                                                
    97
        /**
    98
         * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
    99
         *
    100
         * See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
    101
         */
    102
        function tryRecover(
    103
            bytes32 hash,
    104
            bytes32 r,
    105
            bytes32 vs
    106
        ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
    107
            unchecked {
    108
                bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
    109
                // We do not check for an overflow here since the shift operation results in 0 or 1.
    110
                uint8 v = uint8((uint256(vs) >> 255) + 27);
    111
                return tryRecover(hash, v, r, s);
    112
            }
    113
        }
    114
    
                                                    
                                                
    115
        /**
    116
         * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
    117
         */
    118
        function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
    119
            (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
    120
            _throwError(error, errorArg);
    121
            return recovered;
    122
        }
    123
    
                                                    
                                                
    124
        /**
    125
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
    126
         * `r` and `s` signature fields separately.
    127
         */
    128
        function tryRecover(
    129
            bytes32 hash,
    130
            uint8 v,
    131
            bytes32 r,
    132
            bytes32 s
    133
        ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
    134
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
    135
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
    136
            // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
    137
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
    138
            //
    139
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
    140
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
    141
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
    142
            // these malleable signatures as well.
    143
            if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
    144
                return (address(0), RecoverError.InvalidSignatureS, s);
    145
            }
    146
    
                                                    
                                                
    147
            // If the signature is valid (and not malleable), return the signer address
    148
            address signer = ecrecover(hash, v, r, s);
    149
            if (signer == address(0)) {
    150
                return (address(0), RecoverError.InvalidSignature, bytes32(0));
    151
            }
    152
    
                                                    
                                                
    153
            return (signer, RecoverError.NoError, bytes32(0));
    154
        }
    155
    
                                                    
                                                
    156
        /**
    157
         * @dev Overload of {ECDSA-recover} that receives the `v`,
    158
         * `r` and `s` signature fields separately.
    159
         */
    160
        function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
    161
            (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
    162
            _throwError(error, errorArg);
    163
            return recovered;
    164
        }
    165
    
                                                    
                                                
    166
        /**
    167
         * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
    168
         */
    169
        function _throwError(RecoverError error, bytes32 errorArg) private pure {
    170
            if (error == RecoverError.NoError) {
    171
                return; // no error: do nothing
    172
            } else if (error == RecoverError.InvalidSignature) {
    173
                revert ECDSAInvalidSignature();
    174
            } else if (error == RecoverError.InvalidSignatureLength) {
    175
                revert ECDSAInvalidSignatureLength(uint256(errorArg));
    176
            } else if (error == RecoverError.InvalidSignatureS) {
    177
                revert ECDSAInvalidSignatureS(errorArg);
    178
            }
    179
        }
    180
    }
    181
    
                                                    
                                                
    14.3% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/cryptography/Hashes.sol
    Lines covered: 1 / 7 (14.3%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/Hashes.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev Library of standard hash functions.
    8
     *
    9
     * _Available since v5.1._
    10
     */
    11
    ✓ 15.1K
    ⟲ 15.1K
    library Hashes {
    12
        /**
    13
         * @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs.
    14
         *
    15
         * NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
    16
         */
    17
        function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) {
    18
            return a < b ? efficientKeccak256(a, b) : efficientKeccak256(b, a);
    19
        }
    20
    
                                                    
                                                
    21
        /**
    22
         * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
    23
         */
    24
        function efficientKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32 value) {
    25
            assembly ("memory-safe") {
    26
                mstore(0x00, a)
    27
                mstore(0x20, b)
    28
                value := keccak256(0x00, 0x40)
    29
            }
    30
        }
    31
    }
    32
    
                                                    
                                                
    12.5% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol
    Lines covered: 1 / 8 (12.5%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/MerkleProof.sol)
    3
    // This file was procedurally generated from scripts/generate/templates/MerkleProof.js.
    4
    
                                                    
                                                
    5
    pragma solidity ^0.8.20;
    6
    
                                                    
                                                
    7
    import {Hashes} from "./Hashes.sol";
    8
    
                                                    
                                                
    9
    /**
    10
     * @dev These functions deal with verification of Merkle Tree proofs.
    11
     *
    12
     * The tree and the proofs can be generated using our
    13
     * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
    14
     * You will find a quickstart guide in the readme.
    15
     *
    16
     * WARNING: You should avoid using leaf values that are 64 bytes long prior to
    17
     * hashing, or use a hash function other than keccak256 for hashing leaves.
    18
     * This is because the concatenation of a sorted pair of internal nodes in
    19
     * the Merkle tree could be reinterpreted as a leaf value.
    20
     * OpenZeppelin's JavaScript library generates Merkle trees that are safe
    21
     * against this attack out of the box.
    22
     *
    23
     * IMPORTANT: Consider memory side-effects when using custom hashing functions
    24
     * that access memory in an unsafe way.
    25
     *
    26
     * NOTE: This library supports proof verification for merkle trees built using
    27
     * custom _commutative_ hashing functions (i.e. `H(a, b) == H(b, a)`). Proving
    28
     * leaf inclusion in trees built using non-commutative hashing functions requires
    29
     * additional logic that is not supported by this library.
    30
     */
    31
    ✓ 28.0K
    ⟲ 28.0K
    library MerkleProof {
    32
        /**
    33
         *@dev The multiproof provided is not valid.
    34
         */
    35
        error MerkleProofInvalidMultiproof();
    36
    
                                                    
                                                
    37
        /**
    38
         * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
    39
         * defined by `root`. For this, a `proof` must be provided, containing
    40
         * sibling hashes on the branch from the leaf to the root of the tree. Each
    41
         * pair of leaves and each pair of pre-images are assumed to be sorted.
    42
         *
    43
         * This version handles proofs in memory with the default hashing function.
    44
         */
    45
        function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
    46
            return processProof(proof, leaf) == root;
    47
        }
    48
    
                                                    
                                                
    49
        /**
    50
         * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
    51
         * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
    52
         * hash matches the root of the tree. When processing the proof, the pairs
    53
         * of leaves & pre-images are assumed to be sorted.
    54
         *
    55
         * This version handles proofs in memory with the default hashing function.
    56
         */
    57
        function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
    58
            bytes32 computedHash = leaf;
    59
            for (uint256 i = 0; i < proof.length; i++) {
    60
                computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
    61
            }
    62
            return computedHash;
    63
        }
    64
    
                                                    
                                                
    65
        /**
    66
         * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
    67
         * defined by `root`. For this, a `proof` must be provided, containing
    68
         * sibling hashes on the branch from the leaf to the root of the tree. Each
    69
         * pair of leaves and each pair of pre-images are assumed to be sorted.
    70
         *
    71
         * This version handles proofs in memory with a custom hashing function.
    72
         */
    73
        function verify(
    74
            bytes32[] memory proof,
    75
            bytes32 root,
    76
            bytes32 leaf,
    77
            function(bytes32, bytes32) view returns (bytes32) hasher
    78
        ) internal view returns (bool) {
    79
            return processProof(proof, leaf, hasher) == root;
    80
        }
    81
    
                                                    
                                                
    82
        /**
    83
         * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
    84
         * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
    85
         * hash matches the root of the tree. When processing the proof, the pairs
    86
         * of leaves & pre-images are assumed to be sorted.
    87
         *
    88
         * This version handles proofs in memory with a custom hashing function.
    89
         */
    90
        function processProof(
    91
            bytes32[] memory proof,
    92
            bytes32 leaf,
    93
            function(bytes32, bytes32) view returns (bytes32) hasher
    94
        ) internal view returns (bytes32) {
    95
            bytes32 computedHash = leaf;
    96
            for (uint256 i = 0; i < proof.length; i++) {
    97
                computedHash = hasher(computedHash, proof[i]);
    98
            }
    99
            return computedHash;
    100
        }
    101
    
                                                    
                                                
    102
        /**
    103
         * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
    104
         * defined by `root`. For this, a `proof` must be provided, containing
    105
         * sibling hashes on the branch from the leaf to the root of the tree. Each
    106
         * pair of leaves and each pair of pre-images are assumed to be sorted.
    107
         *
    108
         * This version handles proofs in calldata with the default hashing function.
    109
         */
    110
        function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
    111
            return processProofCalldata(proof, leaf) == root;
    112
        }
    113
    
                                                    
                                                
    114
        /**
    115
         * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
    116
         * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
    117
         * hash matches the root of the tree. When processing the proof, the pairs
    118
         * of leaves & pre-images are assumed to be sorted.
    119
         *
    120
         * This version handles proofs in calldata with the default hashing function.
    121
         */
    122
        function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
    123
            bytes32 computedHash = leaf;
    124
            for (uint256 i = 0; i < proof.length; i++) {
    125
                computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
    126
            }
    127
            return computedHash;
    128
        }
    129
    
                                                    
                                                
    130
        /**
    131
         * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
    132
         * defined by `root`. For this, a `proof` must be provided, containing
    133
         * sibling hashes on the branch from the leaf to the root of the tree. Each
    134
         * pair of leaves and each pair of pre-images are assumed to be sorted.
    135
         *
    136
         * This version handles proofs in calldata with a custom hashing function.
    137
         */
    138
        function verifyCalldata(
    139
            bytes32[] calldata proof,
    140
            bytes32 root,
    141
            bytes32 leaf,
    142
            function(bytes32, bytes32) view returns (bytes32) hasher
    143
        ) internal view returns (bool) {
    144
            return processProofCalldata(proof, leaf, hasher) == root;
    145
        }
    146
    
                                                    
                                                
    147
        /**
    148
         * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
    149
         * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
    150
         * hash matches the root of the tree. When processing the proof, the pairs
    151
         * of leaves & pre-images are assumed to be sorted.
    152
         *
    153
         * This version handles proofs in calldata with a custom hashing function.
    154
         */
    155
        function processProofCalldata(
    156
            bytes32[] calldata proof,
    157
            bytes32 leaf,
    158
            function(bytes32, bytes32) view returns (bytes32) hasher
    159
        ) internal view returns (bytes32) {
    160
            bytes32 computedHash = leaf;
    161
            for (uint256 i = 0; i < proof.length; i++) {
    162
                computedHash = hasher(computedHash, proof[i]);
    163
            }
    164
            return computedHash;
    165
        }
    166
    
                                                    
                                                
    167
        /**
    168
         * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
    169
         * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
    170
         *
    171
         * This version handles multiproofs in memory with the default hashing function.
    172
         *
    173
         * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
    174
         *
    175
         * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
    176
         * The `leaves` must be validated independently. See {processMultiProof}.
    177
         */
    178
        function multiProofVerify(
    179
            bytes32[] memory proof,
    180
            bool[] memory proofFlags,
    181
            bytes32 root,
    182
            bytes32[] memory leaves
    183
        ) internal pure returns (bool) {
    184
            return processMultiProof(proof, proofFlags, leaves) == root;
    185
        }
    186
    
                                                    
                                                
    187
        /**
    188
         * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
    189
         * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
    190
         * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
    191
         * respectively.
    192
         *
    193
         * This version handles multiproofs in memory with the default hashing function.
    194
         *
    195
         * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
    196
         * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
    197
         * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
    198
         *
    199
         * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
    200
         * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
    201
         * validating the leaves elsewhere.
    202
         */
    203
        function processMultiProof(
    204
            bytes32[] memory proof,
    205
            bool[] memory proofFlags,
    206
            bytes32[] memory leaves
    207
        ) internal pure returns (bytes32 merkleRoot) {
    208
            // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
    209
            // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
    210
            // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
    211
            // the Merkle tree.
    212
            uint256 leavesLen = leaves.length;
    213
            uint256 proofFlagsLen = proofFlags.length;
    214
    
                                                    
                                                
    215
            // Check proof validity.
    216
            if (leavesLen + proof.length != proofFlagsLen + 1) {
    217
                revert MerkleProofInvalidMultiproof();
    218
            }
    219
    
                                                    
                                                
    220
            // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
    221
            // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
    222
            bytes32[] memory hashes = new bytes32[](proofFlagsLen);
    223
            uint256 leafPos = 0;
    224
            uint256 hashPos = 0;
    225
            uint256 proofPos = 0;
    226
            // At each step, we compute the next hash using two values:
    227
            // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
    228
            //   get the next hash.
    229
            // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
    230
            //   `proof` array.
    231
            for (uint256 i = 0; i < proofFlagsLen; i++) {
    232
                bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
    233
                bytes32 b = proofFlags[i]
    234
                    ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
    235
                    : proof[proofPos++];
    236
                hashes[i] = Hashes.commutativeKeccak256(a, b);
    237
            }
    238
    
                                                    
                                                
    239
            if (proofFlagsLen > 0) {
    240
                if (proofPos != proof.length) {
    241
                    revert MerkleProofInvalidMultiproof();
    242
                }
    243
                unchecked {
    244
                    return hashes[proofFlagsLen - 1];
    245
                }
    246
            } else if (leavesLen > 0) {
    247
                return leaves[0];
    248
            } else {
    249
                return proof[0];
    250
            }
    251
        }
    252
    
                                                    
                                                
    253
        /**
    254
         * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
    255
         * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
    256
         *
    257
         * This version handles multiproofs in memory with a custom hashing function.
    258
         *
    259
         * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
    260
         *
    261
         * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
    262
         * The `leaves` must be validated independently. See {processMultiProof}.
    263
         */
    264
        function multiProofVerify(
    265
            bytes32[] memory proof,
    266
            bool[] memory proofFlags,
    267
            bytes32 root,
    268
            bytes32[] memory leaves,
    269
            function(bytes32, bytes32) view returns (bytes32) hasher
    270
        ) internal view returns (bool) {
    271
            return processMultiProof(proof, proofFlags, leaves, hasher) == root;
    272
        }
    273
    
                                                    
                                                
    274
        /**
    275
         * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
    276
         * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
    277
         * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
    278
         * respectively.
    279
         *
    280
         * This version handles multiproofs in memory with a custom hashing function.
    281
         *
    282
         * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
    283
         * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
    284
         * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
    285
         *
    286
         * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
    287
         * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
    288
         * validating the leaves elsewhere.
    289
         */
    290
        function processMultiProof(
    291
            bytes32[] memory proof,
    292
            bool[] memory proofFlags,
    293
            bytes32[] memory leaves,
    294
            function(bytes32, bytes32) view returns (bytes32) hasher
    295
        ) internal view returns (bytes32 merkleRoot) {
    296
            // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
    297
            // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
    298
            // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
    299
            // the Merkle tree.
    300
            uint256 leavesLen = leaves.length;
    301
            uint256 proofFlagsLen = proofFlags.length;
    302
    
                                                    
                                                
    303
            // Check proof validity.
    304
            if (leavesLen + proof.length != proofFlagsLen + 1) {
    305
                revert MerkleProofInvalidMultiproof();
    306
            }
    307
    
                                                    
                                                
    308
            // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
    309
            // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
    310
            bytes32[] memory hashes = new bytes32[](proofFlagsLen);
    311
            uint256 leafPos = 0;
    312
            uint256 hashPos = 0;
    313
            uint256 proofPos = 0;
    314
            // At each step, we compute the next hash using two values:
    315
            // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
    316
            //   get the next hash.
    317
            // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
    318
            //   `proof` array.
    319
            for (uint256 i = 0; i < proofFlagsLen; i++) {
    320
                bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
    321
                bytes32 b = proofFlags[i]
    322
                    ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
    323
                    : proof[proofPos++];
    324
                hashes[i] = hasher(a, b);
    325
            }
    326
    
                                                    
                                                
    327
            if (proofFlagsLen > 0) {
    328
                if (proofPos != proof.length) {
    329
                    revert MerkleProofInvalidMultiproof();
    330
                }
    331
                unchecked {
    332
                    return hashes[proofFlagsLen - 1];
    333
                }
    334
            } else if (leavesLen > 0) {
    335
                return leaves[0];
    336
            } else {
    337
                return proof[0];
    338
            }
    339
        }
    340
    
                                                    
                                                
    341
        /**
    342
         * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
    343
         * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
    344
         *
    345
         * This version handles multiproofs in calldata with the default hashing function.
    346
         *
    347
         * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
    348
         *
    349
         * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
    350
         * The `leaves` must be validated independently. See {processMultiProofCalldata}.
    351
         */
    352
        function multiProofVerifyCalldata(
    353
            bytes32[] calldata proof,
    354
            bool[] calldata proofFlags,
    355
            bytes32 root,
    356
            bytes32[] memory leaves
    357
        ) internal pure returns (bool) {
    358
            return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    359
        }
    360
    
                                                    
                                                
    361
        /**
    362
         * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
    363
         * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
    364
         * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
    365
         * respectively.
    366
         *
    367
         * This version handles multiproofs in calldata with the default hashing function.
    368
         *
    369
         * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
    370
         * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
    371
         * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
    372
         *
    373
         * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
    374
         * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
    375
         * validating the leaves elsewhere.
    376
         */
    377
        function processMultiProofCalldata(
    378
            bytes32[] calldata proof,
    379
            bool[] calldata proofFlags,
    380
            bytes32[] memory leaves
    381
        ) internal pure returns (bytes32 merkleRoot) {
    382
            // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
    383
            // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
    384
            // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
    385
            // the Merkle tree.
    386
            uint256 leavesLen = leaves.length;
    387
            uint256 proofFlagsLen = proofFlags.length;
    388
    
                                                    
                                                
    389
            // Check proof validity.
    390
            if (leavesLen + proof.length != proofFlagsLen + 1) {
    391
                revert MerkleProofInvalidMultiproof();
    392
            }
    393
    
                                                    
                                                
    394
            // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
    395
            // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
    396
            bytes32[] memory hashes = new bytes32[](proofFlagsLen);
    397
            uint256 leafPos = 0;
    398
            uint256 hashPos = 0;
    399
            uint256 proofPos = 0;
    400
            // At each step, we compute the next hash using two values:
    401
            // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
    402
            //   get the next hash.
    403
            // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
    404
            //   `proof` array.
    405
            for (uint256 i = 0; i < proofFlagsLen; i++) {
    406
                bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
    407
                bytes32 b = proofFlags[i]
    408
                    ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
    409
                    : proof[proofPos++];
    410
                hashes[i] = Hashes.commutativeKeccak256(a, b);
    411
            }
    412
    
                                                    
                                                
    413
            if (proofFlagsLen > 0) {
    414
                if (proofPos != proof.length) {
    415
                    revert MerkleProofInvalidMultiproof();
    416
                }
    417
                unchecked {
    418
                    return hashes[proofFlagsLen - 1];
    419
                }
    420
            } else if (leavesLen > 0) {
    421
                return leaves[0];
    422
            } else {
    423
                return proof[0];
    424
            }
    425
        }
    426
    
                                                    
                                                
    427
        /**
    428
         * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
    429
         * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
    430
         *
    431
         * This version handles multiproofs in calldata with a custom hashing function.
    432
         *
    433
         * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
    434
         *
    435
         * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
    436
         * The `leaves` must be validated independently. See {processMultiProofCalldata}.
    437
         */
    438
        function multiProofVerifyCalldata(
    439
            bytes32[] calldata proof,
    440
            bool[] calldata proofFlags,
    441
            bytes32 root,
    442
            bytes32[] memory leaves,
    443
            function(bytes32, bytes32) view returns (bytes32) hasher
    444
        ) internal view returns (bool) {
    445
            return processMultiProofCalldata(proof, proofFlags, leaves, hasher) == root;
    446
        }
    447
    
                                                    
                                                
    448
        /**
    449
         * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
    450
         * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
    451
         * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
    452
         * respectively.
    453
         *
    454
         * This version handles multiproofs in calldata with a custom hashing function.
    455
         *
    456
         * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
    457
         * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
    458
         * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
    459
         *
    460
         * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
    461
         * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
    462
         * validating the leaves elsewhere.
    463
         */
    464
        function processMultiProofCalldata(
    465
            bytes32[] calldata proof,
    466
            bool[] calldata proofFlags,
    467
            bytes32[] memory leaves,
    468
            function(bytes32, bytes32) view returns (bytes32) hasher
    469
        ) internal view returns (bytes32 merkleRoot) {
    470
            // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
    471
            // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
    472
            // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
    473
            // the Merkle tree.
    474
            uint256 leavesLen = leaves.length;
    475
            uint256 proofFlagsLen = proofFlags.length;
    476
    
                                                    
                                                
    477
            // Check proof validity.
    478
            if (leavesLen + proof.length != proofFlagsLen + 1) {
    479
                revert MerkleProofInvalidMultiproof();
    480
            }
    481
    
                                                    
                                                
    482
            // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
    483
            // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
    484
            bytes32[] memory hashes = new bytes32[](proofFlagsLen);
    485
            uint256 leafPos = 0;
    486
            uint256 hashPos = 0;
    487
            uint256 proofPos = 0;
    488
            // At each step, we compute the next hash using two values:
    489
            // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
    490
            //   get the next hash.
    491
            // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
    492
            //   `proof` array.
    493
            for (uint256 i = 0; i < proofFlagsLen; i++) {
    494
                bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
    495
                bytes32 b = proofFlags[i]
    496
                    ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
    497
                    : proof[proofPos++];
    498
                hashes[i] = hasher(a, b);
    499
            }
    500
    
                                                    
                                                
    501
            if (proofFlagsLen > 0) {
    502
                if (proofPos != proof.length) {
    503
                    revert MerkleProofInvalidMultiproof();
    504
                }
    505
                unchecked {
    506
                    return hashes[proofFlagsLen - 1];
    507
                }
    508
            } else if (leavesLen > 0) {
    509
                return leaves[0];
    510
            } else {
    511
                return proof[0];
    512
            }
    513
        }
    514
    }
    515
    
                                                    
                                                
    16.7% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/cryptography/MessageHashUtils.sol
    Lines covered: 1 / 6 (16.7%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/MessageHashUtils.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {Strings} from "../Strings.sol";
    7
    
                                                    
                                                
    8
    /**
    9
     * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
    10
     *
    11
     * The library provides methods for generating a hash of a message that conforms to the
    12
     * https://eips.ethereum.org/EIPS/eip-191[ERC-191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
    13
     * specifications.
    14
     */
    15
    ✓ 35.8K
    ⟲ 35.8K
    library MessageHashUtils {
    16
        /**
    17
         * @dev Returns the keccak256 digest of an ERC-191 signed data with version
    18
         * `0x45` (`personal_sign` messages).
    19
         *
    20
         * The digest is calculated by prefixing a bytes32 `messageHash` with
    21
         * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
    22
         * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
    23
         *
    24
         * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
    25
         * keccak256, although any bytes32 value can be safely used because the final digest will
    26
         * be re-hashed.
    27
         *
    28
         * See {ECDSA-recover}.
    29
         */
    30
        function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
    31
            assembly ("memory-safe") {
    32
                mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
    33
                mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
    34
                digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
    35
            }
    36
        }
    37
    
                                                    
                                                
    38
        /**
    39
         * @dev Returns the keccak256 digest of an ERC-191 signed data with version
    40
         * `0x45` (`personal_sign` messages).
    41
         *
    42
         * The digest is calculated by prefixing an arbitrary `message` with
    43
         * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
    44
         * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
    45
         *
    46
         * See {ECDSA-recover}.
    47
         */
    48
        function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
    49
            return
    50
                keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    51
        }
    52
    
                                                    
                                                
    53
        /**
    54
         * @dev Returns the keccak256 digest of an ERC-191 signed data with version
    55
         * `0x00` (data with intended validator).
    56
         *
    57
         * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
    58
         * `validator` address. Then hashing the result.
    59
         *
    60
         * See {ECDSA-recover}.
    61
         */
    62
        function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
    63
            return keccak256(abi.encodePacked(hex"19_00", validator, data));
    64
        }
    65
    
                                                    
                                                
    66
        /**
    67
         * @dev Variant of {toDataWithIntendedValidatorHash-address-bytes} optimized for cases where `data` is a bytes32.
    68
         */
    69
        function toDataWithIntendedValidatorHash(
    70
            address validator,
    71
            bytes32 messageHash
    72
        ) internal pure returns (bytes32 digest) {
    73
            assembly ("memory-safe") {
    74
                mstore(0x00, hex"19_00")
    75
                mstore(0x02, shl(96, validator))
    76
                mstore(0x16, messageHash)
    77
                digest := keccak256(0x00, 0x36)
    78
            }
    79
        }
    80
    
                                                    
                                                
    81
        /**
    82
         * @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`).
    83
         *
    84
         * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
    85
         * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
    86
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
    87
         *
    88
         * See {ECDSA-recover}.
    89
         */
    90
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
    91
            assembly ("memory-safe") {
    92
                let ptr := mload(0x40)
    93
                mstore(ptr, hex"19_01")
    94
                mstore(add(ptr, 0x02), domainSeparator)
    95
                mstore(add(ptr, 0x22), structHash)
    96
                digest := keccak256(ptr, 0x42)
    97
            }
    98
        }
    99
    }
    100
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol
    Lines covered: 0 / 1 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {IERC165} from "./IERC165.sol";
    7
    
                                                    
                                                
    8
    /**
    9
     * @dev Implementation of the {IERC165} interface.
    10
     *
    11
     * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
    12
     * for the additional interface id that will be supported. For example:
    13
     *
    14
     * ```solidity
    15
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    16
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
    17
     * }
    18
     * ```
    19
     */
    20
    abstract contract ERC165 is IERC165 {
    21
        /// @inheritdoc IERC165
    22
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
    23
            return interfaceId == type(IERC165).interfaceId;
    24
        }
    25
    }
    26
    
                                                    
                                                
    0.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
    3
    
                                                    
                                                
    4
    pragma solidity >=0.4.16;
    5
    
                                                    
                                                
    6
    /**
    7
     * @dev Interface of the ERC-165 standard, as defined in the
    8
     * https://eips.ethereum.org/EIPS/eip-165[ERC].
    9
     *
    10
     * Implementers can declare support of contract interfaces, which can then be
    11
     * queried by others ({ERC165Checker}).
    12
     *
    13
     * For an implementation, see {ERC165}.
    14
     */
    15
    interface IERC165 {
    16
        /**
    17
         * @dev Returns true if this contract implements the interface defined by
    18
         * `interfaceId`. See the corresponding
    19
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
    20
         * to learn more about how these ids are created.
    21
         *
    22
         * This function call must use less than 30 000 gas.
    23
         */
    24
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    25
    }
    26
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/math/Math.sol
    Lines covered: 33 / 33 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {Panic} from "../Panic.sol";
    7
    import {SafeCast} from "./SafeCast.sol";
    8
    
                                                    
                                                
    9
    /**
    10
     * @dev Standard math utilities missing in the Solidity language.
    11
     */
    12
    ✓ 15.7K
    ⟲ 15.7K
    library Math {
    13
        enum Rounding {
    14
            Floor, // Toward negative infinity
    15
            Ceil, // Toward positive infinity
    16
            Trunc, // Toward zero
    17
            Expand // Away from zero
    18
        }
    19
    
                                                    
                                                
    20
        /**
    21
         * @dev Return the 512-bit addition of two uint256.
    22
         *
    23
         * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
    24
         */
    25
        function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
    26
            assembly ("memory-safe") {
    27
                low := add(a, b)
    28
                high := lt(low, a)
    29
            }
    30
        }
    31
    
                                                    
                                                
    32
        /**
    33
         * @dev Return the 512-bit multiplication of two uint256.
    34
         *
    35
         * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
    36
         */
    37
    ✓ 15.5M
        function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
    38
            // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
    39
            // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
    40
            // variables such that product = high * 2²⁵⁶ + low.
    41
            assembly ("memory-safe") {
    42
    ✓ 15.5M
                let mm := mulmod(a, b, not(0))
    43
    ✓ 15.5M
                low := mul(a, b)
    44
    ✓ 15.5M
                high := sub(sub(mm, low), lt(mm, low))
    45
            }
    46
        }
    47
    
                                                    
                                                
    48
        /**
    49
         * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
    50
         */
    51
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
    52
            unchecked {
    53
                uint256 c = a + b;
    54
                success = c >= a;
    55
                result = c * SafeCast.toUint(success);
    56
            }
    57
        }
    58
    
                                                    
                                                
    59
        /**
    60
         * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
    61
         */
    62
        function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
    63
            unchecked {
    64
                uint256 c = a - b;
    65
                success = c <= a;
    66
                result = c * SafeCast.toUint(success);
    67
            }
    68
        }
    69
    
                                                    
                                                
    70
        /**
    71
         * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
    72
         */
    73
        function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
    74
            unchecked {
    75
                uint256 c = a * b;
    76
                assembly ("memory-safe") {
    77
                    // Only true when the multiplication doesn't overflow
    78
                    // (c / a == b) || (a == 0)
    79
                    success := or(eq(div(c, a), b), iszero(a))
    80
                }
    81
                // equivalent to: success ? c : 0
    82
                result = c * SafeCast.toUint(success);
    83
            }
    84
        }
    85
    
                                                    
                                                
    86
        /**
    87
         * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
    88
         */
    89
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
    90
            unchecked {
    91
                success = b > 0;
    92
                assembly ("memory-safe") {
    93
                    // The `DIV` opcode returns zero when the denominator is 0.
    94
                    result := div(a, b)
    95
                }
    96
            }
    97
        }
    98
    
                                                    
                                                
    99
        /**
    100
         * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
    101
         */
    102
        function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
    103
            unchecked {
    104
                success = b > 0;
    105
                assembly ("memory-safe") {
    106
                    // The `MOD` opcode returns zero when the denominator is 0.
    107
                    result := mod(a, b)
    108
                }
    109
            }
    110
        }
    111
    
                                                    
                                                
    112
        /**
    113
         * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
    114
         */
    115
        function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
    116
            (bool success, uint256 result) = tryAdd(a, b);
    117
            return ternary(success, result, type(uint256).max);
    118
        }
    119
    
                                                    
                                                
    120
        /**
    121
         * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
    122
         */
    123
        function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
    124
            (, uint256 result) = trySub(a, b);
    125
            return result;
    126
        }
    127
    
                                                    
                                                
    128
        /**
    129
         * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
    130
         */
    131
        function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
    132
            (bool success, uint256 result) = tryMul(a, b);
    133
            return ternary(success, result, type(uint256).max);
    134
        }
    135
    
                                                    
                                                
    136
        /**
    137
         * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
    138
         *
    139
         * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
    140
         * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
    141
         * one branch when needed, making this function more expensive.
    142
         */
    143
        function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
    144
            unchecked {
    145
                // branchless ternary works because:
    146
                // b ^ (a ^ b) == a
    147
                // b ^ 0 == b
    148
    ✓ 189.0K
                return b ^ ((a ^ b) * SafeCast.toUint(condition));
    149
            }
    150
        }
    151
    
                                                    
                                                
    152
        /**
    153
         * @dev Returns the largest of two numbers.
    154
         */
    155
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
    156
            return ternary(a > b, a, b);
    157
        }
    158
    
                                                    
                                                
    159
        /**
    160
         * @dev Returns the smallest of two numbers.
    161
         */
    162
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
    163
            return ternary(a < b, a, b);
    164
        }
    165
    
                                                    
                                                
    166
        /**
    167
         * @dev Returns the average of two numbers. The result is rounded towards
    168
         * zero.
    169
         */
    170
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
    171
            // (a + b) / 2 can overflow.
    172
            return (a & b) + (a ^ b) / 2;
    173
        }
    174
    
                                                    
                                                
    175
        /**
    176
         * @dev Returns the ceiling of the division of two numbers.
    177
         *
    178
         * This differs from standard division with `/` in that it rounds towards infinity instead
    179
         * of rounding towards zero.
    180
         */
    181
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
    182
            if (b == 0) {
    183
                // Guarantee the same behavior as in a regular Solidity division.
    184
                Panic.panic(Panic.DIVISION_BY_ZERO);
    185
            }
    186
    
                                                    
                                                
    187
            // The following calculation ensures accurate ceiling division without overflow.
    188
            // Since a is non-zero, (a - 1) / b will not overflow.
    189
            // The largest possible result occurs when (a - 1) / b is type(uint256).max,
    190
            // but the largest value we can obtain is type(uint256).max - 1, which happens
    191
            // when a = type(uint256).max and b = 1.
    192
            unchecked {
    193
                return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
    194
            }
    195
        }
    196
    
                                                    
                                                
    197
        /**
    198
         * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
    199
         * denominator == 0.
    200
         *
    201
         * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
    202
         * Uniswap Labs also under MIT license.
    203
         */
    204
    ✓ 15.5M
        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
    205
            unchecked {
    206
    ✓ 15.5M
                (uint256 high, uint256 low) = mul512(x, y);
    207
    
                                                    
                                                
    208
                // Handle non-overflow cases, 256 by 256 division.
    209
    ✓ 15.5M
                if (high == 0) {
    210
                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
    211
                    // The surrounding unchecked block does not change this fact.
    212
                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
    213
    ✓ 12.9M
                    return low / denominator;
    214
                }
    215
    
                                                    
                                                
    216
                // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
    217
    ✓ 2.6M
                if (denominator <= high) {
    218
    ✓ 189.0K
                    Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
    219
                }
    220
    
                                                    
                                                
    221
                ///////////////////////////////////////////////
    222
                // 512 by 256 division.
    223
                ///////////////////////////////////////////////
    224
    
                                                    
                                                
    225
                // Make division exact by subtracting the remainder from [high low].
    226
    ✓ 2.4M
                uint256 remainder;
    227
                assembly ("memory-safe") {
    228
                    // Compute remainder using mulmod.
    229
    ✓ 2.4M
                    remainder := mulmod(x, y, denominator)
    230
    
                                                    
                                                
    231
                    // Subtract 256 bit number from 512 bit number.
    232
    ✓ 2.4M
                    high := sub(high, gt(remainder, low))
    233
    ✓ 2.4M
                    low := sub(low, remainder)
    234
                }
    235
    
                                                    
                                                
    236
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
    237
                // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
    238
    
                                                    
                                                
    239
    ✓ 2.4M
                uint256 twos = denominator & (0 - denominator);
    240
                assembly ("memory-safe") {
    241
                    // Divide denominator by twos.
    242
    ✓ 2.4M
                    denominator := div(denominator, twos)
    243
    
                                                    
                                                
    244
                    // Divide [high low] by twos.
    245
    ✓ 2.4M
                    low := div(low, twos)
    246
    
                                                    
                                                
    247
                    // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
    248
    ✓ 2.4M
                    twos := add(div(sub(0, twos), twos), 1)
    249
                }
    250
    
                                                    
                                                
    251
                // Shift in bits from high into low.
    252
    ✓ 2.4M
                low |= high * twos;
    253
    
                                                    
                                                
    254
                // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
    255
                // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
    256
                // four bits. That is, denominator * inv ≡ 1 mod 2⁴.
    257
    ✓ 2.4M
                uint256 inverse = (3 * denominator) ^ 2;
    258
    
                                                    
                                                
    259
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
    260
                // works in modular arithmetic, doubling the correct bits in each step.
    261
    ✓ 2.4M
                inverse *= 2 - denominator * inverse; // inverse mod 2⁸
    262
    ✓ 2.4M
                inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
    263
    ✓ 2.4M
                inverse *= 2 - denominator * inverse; // inverse mod 2³²
    264
    ✓ 2.4M
                inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
    265
    ✓ 2.4M
                inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
    266
    ✓ 2.4M
                inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
    267
    
                                                    
                                                
    268
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
    269
                // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
    270
                // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
    271
                // is no longer required.
    272
    ✓ 2.4M
                result = low * inverse;
    273
                return result;
    274
            }
    275
        }
    276
    
                                                    
                                                
    277
        /**
    278
         * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
    279
         */
    280
    ✓ 15.5M
        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
    281
    ✓ 15.5M
            return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
    282
        }
    283
    
                                                    
                                                
    284
        /**
    285
         * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
    286
         */
    287
        function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
    288
            unchecked {
    289
                (uint256 high, uint256 low) = mul512(x, y);
    290
                if (high >= 1 << n) {
    291
                    Panic.panic(Panic.UNDER_OVERFLOW);
    292
                }
    293
                return (high << (256 - n)) | (low >> n);
    294
            }
    295
        }
    296
    
                                                    
                                                
    297
        /**
    298
         * @dev Calculates x * y >> n with full precision, following the selected rounding direction.
    299
         */
    300
        function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
    301
            return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
    302
        }
    303
    
                                                    
                                                
    304
        /**
    305
         * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
    306
         *
    307
         * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
    308
         * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
    309
         *
    310
         * If the input value is not inversible, 0 is returned.
    311
         *
    312
         * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
    313
         * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
    314
         */
    315
        function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
    316
            unchecked {
    317
                if (n == 0) return 0;
    318
    
                                                    
                                                
    319
                // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
    320
                // Used to compute integers x and y such that: ax + ny = gcd(a, n).
    321
                // When the gcd is 1, then the inverse of a modulo n exists and it's x.
    322
                // ax + ny = 1
    323
                // ax = 1 + (-y)n
    324
                // ax ≡ 1 (mod n) # x is the inverse of a modulo n
    325
    
                                                    
                                                
    326
                // If the remainder is 0 the gcd is n right away.
    327
                uint256 remainder = a % n;
    328
                uint256 gcd = n;
    329
    
                                                    
                                                
    330
                // Therefore the initial coefficients are:
    331
                // ax + ny = gcd(a, n) = n
    332
                // 0a + 1n = n
    333
                int256 x = 0;
    334
                int256 y = 1;
    335
    
                                                    
                                                
    336
                while (remainder != 0) {
    337
                    uint256 quotient = gcd / remainder;
    338
    
                                                    
                                                
    339
                    (gcd, remainder) = (
    340
                        // The old remainder is the next gcd to try.
    341
                        remainder,
    342
                        // Compute the next remainder.
    343
                        // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
    344
                        // where gcd is at most n (capped to type(uint256).max)
    345
                        gcd - remainder * quotient
    346
                    );
    347
    
                                                    
                                                
    348
                    (x, y) = (
    349
                        // Increment the coefficient of a.
    350
                        y,
    351
                        // Decrement the coefficient of n.
    352
                        // Can overflow, but the result is casted to uint256 so that the
    353
                        // next value of y is "wrapped around" to a value between 0 and n - 1.
    354
                        x - y * int256(quotient)
    355
                    );
    356
                }
    357
    
                                                    
                                                
    358
                if (gcd != 1) return 0; // No inverse exists.
    359
                return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
    360
            }
    361
        }
    362
    
                                                    
                                                
    363
        /**
    364
         * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
    365
         *
    366
         * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
    367
         * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
    368
         * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
    369
         *
    370
         * NOTE: this function does NOT check that `p` is a prime greater than `2`.
    371
         */
    372
        function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
    373
            unchecked {
    374
                return Math.modExp(a, p - 2, p);
    375
            }
    376
        }
    377
    
                                                    
                                                
    378
        /**
    379
         * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
    380
         *
    381
         * Requirements:
    382
         * - modulus can't be zero
    383
         * - underlying staticcall to precompile must succeed
    384
         *
    385
         * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
    386
         * sure the chain you're using it on supports the precompiled contract for modular exponentiation
    387
         * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
    388
         * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
    389
         * interpreted as 0.
    390
         */
    391
        function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
    392
            (bool success, uint256 result) = tryModExp(b, e, m);
    393
            if (!success) {
    394
                Panic.panic(Panic.DIVISION_BY_ZERO);
    395
            }
    396
            return result;
    397
        }
    398
    
                                                    
                                                
    399
        /**
    400
         * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
    401
         * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
    402
         * to operate modulo 0 or if the underlying precompile reverted.
    403
         *
    404
         * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
    405
         * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
    406
         * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
    407
         * of a revert, but the result may be incorrectly interpreted as 0.
    408
         */
    409
        function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
    410
            if (m == 0) return (false, 0);
    411
            assembly ("memory-safe") {
    412
                let ptr := mload(0x40)
    413
                // | Offset    | Content    | Content (Hex)                                                      |
    414
                // |-----------|------------|--------------------------------------------------------------------|
    415
                // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
    416
                // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
    417
                // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
    418
                // | 0x60:0x7f | value of b | 0x<.............................................................b> |
    419
                // | 0x80:0x9f | value of e | 0x<.............................................................e> |
    420
                // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
    421
                mstore(ptr, 0x20)
    422
                mstore(add(ptr, 0x20), 0x20)
    423
                mstore(add(ptr, 0x40), 0x20)
    424
                mstore(add(ptr, 0x60), b)
    425
                mstore(add(ptr, 0x80), e)
    426
                mstore(add(ptr, 0xa0), m)
    427
    
                                                    
                                                
    428
                // Given the result < m, it's guaranteed to fit in 32 bytes,
    429
                // so we can use the memory scratch space located at offset 0.
    430
                success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
    431
                result := mload(0x00)
    432
            }
    433
        }
    434
    
                                                    
                                                
    435
        /**
    436
         * @dev Variant of {modExp} that supports inputs of arbitrary length.
    437
         */
    438
        function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
    439
            (bool success, bytes memory result) = tryModExp(b, e, m);
    440
            if (!success) {
    441
                Panic.panic(Panic.DIVISION_BY_ZERO);
    442
            }
    443
            return result;
    444
        }
    445
    
                                                    
                                                
    446
        /**
    447
         * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
    448
         */
    449
        function tryModExp(
    450
            bytes memory b,
    451
            bytes memory e,
    452
            bytes memory m
    453
        ) internal view returns (bool success, bytes memory result) {
    454
            if (_zeroBytes(m)) return (false, new bytes(0));
    455
    
                                                    
                                                
    456
            uint256 mLen = m.length;
    457
    
                                                    
                                                
    458
            // Encode call args in result and move the free memory pointer
    459
            result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
    460
    
                                                    
                                                
    461
            assembly ("memory-safe") {
    462
                let dataPtr := add(result, 0x20)
    463
                // Write result on top of args to avoid allocating extra memory.
    464
                success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
    465
                // Overwrite the length.
    466
                // result.length > returndatasize() is guaranteed because returndatasize() == m.length
    467
                mstore(result, mLen)
    468
                // Set the memory pointer after the returned data.
    469
                mstore(0x40, add(dataPtr, mLen))
    470
            }
    471
        }
    472
    
                                                    
                                                
    473
        /**
    474
         * @dev Returns whether the provided byte array is zero.
    475
         */
    476
        function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
    477
            for (uint256 i = 0; i < byteArray.length; ++i) {
    478
                if (byteArray[i] != 0) {
    479
                    return false;
    480
                }
    481
            }
    482
            return true;
    483
        }
    484
    
                                                    
                                                
    485
        /**
    486
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
    487
         * towards zero.
    488
         *
    489
         * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
    490
         * using integer operations.
    491
         */
    492
        function sqrt(uint256 a) internal pure returns (uint256) {
    493
            unchecked {
    494
                // Take care of easy edge cases when a == 0 or a == 1
    495
                if (a <= 1) {
    496
                    return a;
    497
                }
    498
    
                                                    
                                                
    499
                // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
    500
                // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
    501
                // the current value as `ε_n = | x_n - sqrt(a) |`.
    502
                //
    503
                // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
    504
                // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
    505
                // bigger than any uint256.
    506
                //
    507
                // By noticing that
    508
                // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
    509
                // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
    510
                // to the msb function.
    511
                uint256 aa = a;
    512
                uint256 xn = 1;
    513
    
                                                    
                                                
    514
                if (aa >= (1 << 128)) {
    515
                    aa >>= 128;
    516
                    xn <<= 64;
    517
                }
    518
                if (aa >= (1 << 64)) {
    519
                    aa >>= 64;
    520
                    xn <<= 32;
    521
                }
    522
                if (aa >= (1 << 32)) {
    523
                    aa >>= 32;
    524
                    xn <<= 16;
    525
                }
    526
                if (aa >= (1 << 16)) {
    527
                    aa >>= 16;
    528
                    xn <<= 8;
    529
                }
    530
                if (aa >= (1 << 8)) {
    531
                    aa >>= 8;
    532
                    xn <<= 4;
    533
                }
    534
                if (aa >= (1 << 4)) {
    535
                    aa >>= 4;
    536
                    xn <<= 2;
    537
                }
    538
                if (aa >= (1 << 2)) {
    539
                    xn <<= 1;
    540
                }
    541
    
                                                    
                                                
    542
                // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
    543
                //
    544
                // We can refine our estimation by noticing that the middle of that interval minimizes the error.
    545
                // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
    546
                // This is going to be our x_0 (and ε_0)
    547
                xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
    548
    
                                                    
                                                
    549
                // From here, Newton's method give us:
    550
                // x_{n+1} = (x_n + a / x_n) / 2
    551
                //
    552
                // One should note that:
    553
                // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
    554
                //              = ((x_n² + a) / (2 * x_n))² - a
    555
                //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
    556
                //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
    557
                //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
    558
                //              = (x_n² - a)² / (2 * x_n)²
    559
                //              = ((x_n² - a) / (2 * x_n))²
    560
                //              ≥ 0
    561
                // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
    562
                //
    563
                // This gives us the proof of quadratic convergence of the sequence:
    564
                // ε_{n+1} = | x_{n+1} - sqrt(a) |
    565
                //         = | (x_n + a / x_n) / 2 - sqrt(a) |
    566
                //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
    567
                //         = | (x_n - sqrt(a))² / (2 * x_n) |
    568
                //         = | ε_n² / (2 * x_n) |
    569
                //         = ε_n² / | (2 * x_n) |
    570
                //
    571
                // For the first iteration, we have a special case where x_0 is known:
    572
                // ε_1 = ε_0² / | (2 * x_0) |
    573
                //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
    574
                //     ≤ 2**(2*e-4) / (3 * 2**(e-1))
    575
                //     ≤ 2**(e-3) / 3
    576
                //     ≤ 2**(e-3-log2(3))
    577
                //     ≤ 2**(e-4.5)
    578
                //
    579
                // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
    580
                // ε_{n+1} = ε_n² / | (2 * x_n) |
    581
                //         ≤ (2**(e-k))² / (2 * 2**(e-1))
    582
                //         ≤ 2**(2*e-2*k) / 2**e
    583
                //         ≤ 2**(e-2*k)
    584
                xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above
    585
                xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5
    586
                xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9
    587
                xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18
    588
                xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36
    589
                xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72
    590
    
                                                    
                                                
    591
                // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
    592
                // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
    593
                // sqrt(a) or sqrt(a) + 1.
    594
                return xn - SafeCast.toUint(xn > a / xn);
    595
            }
    596
        }
    597
    
                                                    
                                                
    598
        /**
    599
         * @dev Calculates sqrt(a), following the selected rounding direction.
    600
         */
    601
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
    602
            unchecked {
    603
                uint256 result = sqrt(a);
    604
                return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
    605
            }
    606
        }
    607
    
                                                    
                                                
    608
        /**
    609
         * @dev Return the log in base 2 of a positive value rounded towards zero.
    610
         * Returns 0 if given 0.
    611
         */
    612
        function log2(uint256 x) internal pure returns (uint256 r) {
    613
            // If value has upper 128 bits set, log2 result is at least 128
    614
            r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
    615
            // If upper 64 bits of 128-bit half set, add 64 to result
    616
            r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
    617
            // If upper 32 bits of 64-bit half set, add 32 to result
    618
            r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
    619
            // If upper 16 bits of 32-bit half set, add 16 to result
    620
            r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
    621
            // If upper 8 bits of 16-bit half set, add 8 to result
    622
            r |= SafeCast.toUint((x >> r) > 0xff) << 3;
    623
            // If upper 4 bits of 8-bit half set, add 4 to result
    624
            r |= SafeCast.toUint((x >> r) > 0xf) << 2;
    625
    
                                                    
                                                
    626
            // Shifts value right by the current result and use it as an index into this lookup table:
    627
            //
    628
            // | x (4 bits) |  index  | table[index] = MSB position |
    629
            // |------------|---------|-----------------------------|
    630
            // |    0000    |    0    |        table[0] = 0         |
    631
            // |    0001    |    1    |        table[1] = 0         |
    632
            // |    0010    |    2    |        table[2] = 1         |
    633
            // |    0011    |    3    |        table[3] = 1         |
    634
            // |    0100    |    4    |        table[4] = 2         |
    635
            // |    0101    |    5    |        table[5] = 2         |
    636
            // |    0110    |    6    |        table[6] = 2         |
    637
            // |    0111    |    7    |        table[7] = 2         |
    638
            // |    1000    |    8    |        table[8] = 3         |
    639
            // |    1001    |    9    |        table[9] = 3         |
    640
            // |    1010    |   10    |        table[10] = 3        |
    641
            // |    1011    |   11    |        table[11] = 3        |
    642
            // |    1100    |   12    |        table[12] = 3        |
    643
            // |    1101    |   13    |        table[13] = 3        |
    644
            // |    1110    |   14    |        table[14] = 3        |
    645
            // |    1111    |   15    |        table[15] = 3        |
    646
            //
    647
            // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
    648
            assembly ("memory-safe") {
    649
                r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
    650
            }
    651
        }
    652
    
                                                    
                                                
    653
        /**
    654
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
    655
         * Returns 0 if given 0.
    656
         */
    657
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
    658
            unchecked {
    659
                uint256 result = log2(value);
    660
                return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
    661
            }
    662
        }
    663
    
                                                    
                                                
    664
        /**
    665
         * @dev Return the log in base 10 of a positive value rounded towards zero.
    666
         * Returns 0 if given 0.
    667
         */
    668
        function log10(uint256 value) internal pure returns (uint256) {
    669
            uint256 result = 0;
    670
            unchecked {
    671
                if (value >= 10 ** 64) {
    672
                    value /= 10 ** 64;
    673
                    result += 64;
    674
                }
    675
                if (value >= 10 ** 32) {
    676
                    value /= 10 ** 32;
    677
                    result += 32;
    678
                }
    679
                if (value >= 10 ** 16) {
    680
                    value /= 10 ** 16;
    681
                    result += 16;
    682
                }
    683
                if (value >= 10 ** 8) {
    684
                    value /= 10 ** 8;
    685
                    result += 8;
    686
                }
    687
                if (value >= 10 ** 4) {
    688
                    value /= 10 ** 4;
    689
                    result += 4;
    690
                }
    691
                if (value >= 10 ** 2) {
    692
                    value /= 10 ** 2;
    693
                    result += 2;
    694
                }
    695
                if (value >= 10 ** 1) {
    696
                    result += 1;
    697
                }
    698
            }
    699
            return result;
    700
        }
    701
    
                                                    
                                                
    702
        /**
    703
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
    704
         * Returns 0 if given 0.
    705
         */
    706
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
    707
            unchecked {
    708
                uint256 result = log10(value);
    709
                return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
    710
            }
    711
        }
    712
    
                                                    
                                                
    713
        /**
    714
         * @dev Return the log in base 256 of a positive value rounded towards zero.
    715
         * Returns 0 if given 0.
    716
         *
    717
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
    718
         */
    719
        function log256(uint256 x) internal pure returns (uint256 r) {
    720
            // If value has upper 128 bits set, log2 result is at least 128
    721
            r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
    722
            // If upper 64 bits of 128-bit half set, add 64 to result
    723
            r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
    724
            // If upper 32 bits of 64-bit half set, add 32 to result
    725
            r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
    726
            // If upper 16 bits of 32-bit half set, add 16 to result
    727
            r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
    728
            // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
    729
            return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
    730
        }
    731
    
                                                    
                                                
    732
        /**
    733
         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
    734
         * Returns 0 if given 0.
    735
         */
    736
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
    737
            unchecked {
    738
                uint256 result = log256(value);
    739
                return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
    740
            }
    741
        }
    742
    
                                                    
                                                
    743
        /**
    744
         * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
    745
         */
    746
    ✓ 15.5M
        function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
    747
    ✓ 15.5M
            return uint8(rounding) % 2 == 1;
    748
        }
    749
    }
    750
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol
    Lines covered: 2 / 2 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
    3
    // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
    4
    
                                                    
                                                
    5
    pragma solidity ^0.8.20;
    6
    
                                                    
                                                
    7
    /**
    8
     * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
    9
     * checks.
    10
     *
    11
     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
    12
     * easily result in undesired exploitation or bugs, since developers usually
    13
     * assume that overflows raise errors. `SafeCast` restores this intuition by
    14
     * reverting the transaction when such an operation overflows.
    15
     *
    16
     * Using this library instead of the unchecked operations eliminates an entire
    17
     * class of bugs, so it's recommended to use it always.
    18
     */
    19
    ✓ 16.2K
    ⟲ 16.2K
    library SafeCast {
    20
        /**
    21
         * @dev Value doesn't fit in an uint of `bits` size.
    22
         */
    23
        error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
    24
    
                                                    
                                                
    25
        /**
    26
         * @dev An int value doesn't fit in an uint of `bits` size.
    27
         */
    28
        error SafeCastOverflowedIntToUint(int256 value);
    29
    
                                                    
                                                
    30
        /**
    31
         * @dev Value doesn't fit in an int of `bits` size.
    32
         */
    33
        error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
    34
    
                                                    
                                                
    35
        /**
    36
         * @dev An uint value doesn't fit in an int of `bits` size.
    37
         */
    38
        error SafeCastOverflowedUintToInt(uint256 value);
    39
    
                                                    
                                                
    40
        /**
    41
         * @dev Returns the downcasted uint248 from uint256, reverting on
    42
         * overflow (when the input is greater than largest uint248).
    43
         *
    44
         * Counterpart to Solidity's `uint248` operator.
    45
         *
    46
         * Requirements:
    47
         *
    48
         * - input must fit into 248 bits
    49
         */
    50
        function toUint248(uint256 value) internal pure returns (uint248) {
    51
            if (value > type(uint248).max) {
    52
                revert SafeCastOverflowedUintDowncast(248, value);
    53
            }
    54
            return uint248(value);
    55
        }
    56
    
                                                    
                                                
    57
        /**
    58
         * @dev Returns the downcasted uint240 from uint256, reverting on
    59
         * overflow (when the input is greater than largest uint240).
    60
         *
    61
         * Counterpart to Solidity's `uint240` operator.
    62
         *
    63
         * Requirements:
    64
         *
    65
         * - input must fit into 240 bits
    66
         */
    67
        function toUint240(uint256 value) internal pure returns (uint240) {
    68
            if (value > type(uint240).max) {
    69
                revert SafeCastOverflowedUintDowncast(240, value);
    70
            }
    71
            return uint240(value);
    72
        }
    73
    
                                                    
                                                
    74
        /**
    75
         * @dev Returns the downcasted uint232 from uint256, reverting on
    76
         * overflow (when the input is greater than largest uint232).
    77
         *
    78
         * Counterpart to Solidity's `uint232` operator.
    79
         *
    80
         * Requirements:
    81
         *
    82
         * - input must fit into 232 bits
    83
         */
    84
        function toUint232(uint256 value) internal pure returns (uint232) {
    85
            if (value > type(uint232).max) {
    86
                revert SafeCastOverflowedUintDowncast(232, value);
    87
            }
    88
            return uint232(value);
    89
        }
    90
    
                                                    
                                                
    91
        /**
    92
         * @dev Returns the downcasted uint224 from uint256, reverting on
    93
         * overflow (when the input is greater than largest uint224).
    94
         *
    95
         * Counterpart to Solidity's `uint224` operator.
    96
         *
    97
         * Requirements:
    98
         *
    99
         * - input must fit into 224 bits
    100
         */
    101
        function toUint224(uint256 value) internal pure returns (uint224) {
    102
            if (value > type(uint224).max) {
    103
                revert SafeCastOverflowedUintDowncast(224, value);
    104
            }
    105
            return uint224(value);
    106
        }
    107
    
                                                    
                                                
    108
        /**
    109
         * @dev Returns the downcasted uint216 from uint256, reverting on
    110
         * overflow (when the input is greater than largest uint216).
    111
         *
    112
         * Counterpart to Solidity's `uint216` operator.
    113
         *
    114
         * Requirements:
    115
         *
    116
         * - input must fit into 216 bits
    117
         */
    118
        function toUint216(uint256 value) internal pure returns (uint216) {
    119
            if (value > type(uint216).max) {
    120
                revert SafeCastOverflowedUintDowncast(216, value);
    121
            }
    122
            return uint216(value);
    123
        }
    124
    
                                                    
                                                
    125
        /**
    126
         * @dev Returns the downcasted uint208 from uint256, reverting on
    127
         * overflow (when the input is greater than largest uint208).
    128
         *
    129
         * Counterpart to Solidity's `uint208` operator.
    130
         *
    131
         * Requirements:
    132
         *
    133
         * - input must fit into 208 bits
    134
         */
    135
        function toUint208(uint256 value) internal pure returns (uint208) {
    136
            if (value > type(uint208).max) {
    137
                revert SafeCastOverflowedUintDowncast(208, value);
    138
            }
    139
            return uint208(value);
    140
        }
    141
    
                                                    
                                                
    142
        /**
    143
         * @dev Returns the downcasted uint200 from uint256, reverting on
    144
         * overflow (when the input is greater than largest uint200).
    145
         *
    146
         * Counterpart to Solidity's `uint200` operator.
    147
         *
    148
         * Requirements:
    149
         *
    150
         * - input must fit into 200 bits
    151
         */
    152
        function toUint200(uint256 value) internal pure returns (uint200) {
    153
            if (value > type(uint200).max) {
    154
                revert SafeCastOverflowedUintDowncast(200, value);
    155
            }
    156
            return uint200(value);
    157
        }
    158
    
                                                    
                                                
    159
        /**
    160
         * @dev Returns the downcasted uint192 from uint256, reverting on
    161
         * overflow (when the input is greater than largest uint192).
    162
         *
    163
         * Counterpart to Solidity's `uint192` operator.
    164
         *
    165
         * Requirements:
    166
         *
    167
         * - input must fit into 192 bits
    168
         */
    169
        function toUint192(uint256 value) internal pure returns (uint192) {
    170
            if (value > type(uint192).max) {
    171
                revert SafeCastOverflowedUintDowncast(192, value);
    172
            }
    173
            return uint192(value);
    174
        }
    175
    
                                                    
                                                
    176
        /**
    177
         * @dev Returns the downcasted uint184 from uint256, reverting on
    178
         * overflow (when the input is greater than largest uint184).
    179
         *
    180
         * Counterpart to Solidity's `uint184` operator.
    181
         *
    182
         * Requirements:
    183
         *
    184
         * - input must fit into 184 bits
    185
         */
    186
        function toUint184(uint256 value) internal pure returns (uint184) {
    187
            if (value > type(uint184).max) {
    188
                revert SafeCastOverflowedUintDowncast(184, value);
    189
            }
    190
            return uint184(value);
    191
        }
    192
    
                                                    
                                                
    193
        /**
    194
         * @dev Returns the downcasted uint176 from uint256, reverting on
    195
         * overflow (when the input is greater than largest uint176).
    196
         *
    197
         * Counterpart to Solidity's `uint176` operator.
    198
         *
    199
         * Requirements:
    200
         *
    201
         * - input must fit into 176 bits
    202
         */
    203
        function toUint176(uint256 value) internal pure returns (uint176) {
    204
            if (value > type(uint176).max) {
    205
                revert SafeCastOverflowedUintDowncast(176, value);
    206
            }
    207
            return uint176(value);
    208
        }
    209
    
                                                    
                                                
    210
        /**
    211
         * @dev Returns the downcasted uint168 from uint256, reverting on
    212
         * overflow (when the input is greater than largest uint168).
    213
         *
    214
         * Counterpart to Solidity's `uint168` operator.
    215
         *
    216
         * Requirements:
    217
         *
    218
         * - input must fit into 168 bits
    219
         */
    220
        function toUint168(uint256 value) internal pure returns (uint168) {
    221
            if (value > type(uint168).max) {
    222
                revert SafeCastOverflowedUintDowncast(168, value);
    223
            }
    224
            return uint168(value);
    225
        }
    226
    
                                                    
                                                
    227
        /**
    228
         * @dev Returns the downcasted uint160 from uint256, reverting on
    229
         * overflow (when the input is greater than largest uint160).
    230
         *
    231
         * Counterpart to Solidity's `uint160` operator.
    232
         *
    233
         * Requirements:
    234
         *
    235
         * - input must fit into 160 bits
    236
         */
    237
        function toUint160(uint256 value) internal pure returns (uint160) {
    238
            if (value > type(uint160).max) {
    239
                revert SafeCastOverflowedUintDowncast(160, value);
    240
            }
    241
            return uint160(value);
    242
        }
    243
    
                                                    
                                                
    244
        /**
    245
         * @dev Returns the downcasted uint152 from uint256, reverting on
    246
         * overflow (when the input is greater than largest uint152).
    247
         *
    248
         * Counterpart to Solidity's `uint152` operator.
    249
         *
    250
         * Requirements:
    251
         *
    252
         * - input must fit into 152 bits
    253
         */
    254
        function toUint152(uint256 value) internal pure returns (uint152) {
    255
            if (value > type(uint152).max) {
    256
                revert SafeCastOverflowedUintDowncast(152, value);
    257
            }
    258
            return uint152(value);
    259
        }
    260
    
                                                    
                                                
    261
        /**
    262
         * @dev Returns the downcasted uint144 from uint256, reverting on
    263
         * overflow (when the input is greater than largest uint144).
    264
         *
    265
         * Counterpart to Solidity's `uint144` operator.
    266
         *
    267
         * Requirements:
    268
         *
    269
         * - input must fit into 144 bits
    270
         */
    271
        function toUint144(uint256 value) internal pure returns (uint144) {
    272
            if (value > type(uint144).max) {
    273
                revert SafeCastOverflowedUintDowncast(144, value);
    274
            }
    275
            return uint144(value);
    276
        }
    277
    
                                                    
                                                
    278
        /**
    279
         * @dev Returns the downcasted uint136 from uint256, reverting on
    280
         * overflow (when the input is greater than largest uint136).
    281
         *
    282
         * Counterpart to Solidity's `uint136` operator.
    283
         *
    284
         * Requirements:
    285
         *
    286
         * - input must fit into 136 bits
    287
         */
    288
        function toUint136(uint256 value) internal pure returns (uint136) {
    289
            if (value > type(uint136).max) {
    290
                revert SafeCastOverflowedUintDowncast(136, value);
    291
            }
    292
            return uint136(value);
    293
        }
    294
    
                                                    
                                                
    295
        /**
    296
         * @dev Returns the downcasted uint128 from uint256, reverting on
    297
         * overflow (when the input is greater than largest uint128).
    298
         *
    299
         * Counterpart to Solidity's `uint128` operator.
    300
         *
    301
         * Requirements:
    302
         *
    303
         * - input must fit into 128 bits
    304
         */
    305
        function toUint128(uint256 value) internal pure returns (uint128) {
    306
            if (value > type(uint128).max) {
    307
                revert SafeCastOverflowedUintDowncast(128, value);
    308
            }
    309
            return uint128(value);
    310
        }
    311
    
                                                    
                                                
    312
        /**
    313
         * @dev Returns the downcasted uint120 from uint256, reverting on
    314
         * overflow (when the input is greater than largest uint120).
    315
         *
    316
         * Counterpart to Solidity's `uint120` operator.
    317
         *
    318
         * Requirements:
    319
         *
    320
         * - input must fit into 120 bits
    321
         */
    322
        function toUint120(uint256 value) internal pure returns (uint120) {
    323
            if (value > type(uint120).max) {
    324
                revert SafeCastOverflowedUintDowncast(120, value);
    325
            }
    326
            return uint120(value);
    327
        }
    328
    
                                                    
                                                
    329
        /**
    330
         * @dev Returns the downcasted uint112 from uint256, reverting on
    331
         * overflow (when the input is greater than largest uint112).
    332
         *
    333
         * Counterpart to Solidity's `uint112` operator.
    334
         *
    335
         * Requirements:
    336
         *
    337
         * - input must fit into 112 bits
    338
         */
    339
        function toUint112(uint256 value) internal pure returns (uint112) {
    340
            if (value > type(uint112).max) {
    341
                revert SafeCastOverflowedUintDowncast(112, value);
    342
            }
    343
            return uint112(value);
    344
        }
    345
    
                                                    
                                                
    346
        /**
    347
         * @dev Returns the downcasted uint104 from uint256, reverting on
    348
         * overflow (when the input is greater than largest uint104).
    349
         *
    350
         * Counterpart to Solidity's `uint104` operator.
    351
         *
    352
         * Requirements:
    353
         *
    354
         * - input must fit into 104 bits
    355
         */
    356
        function toUint104(uint256 value) internal pure returns (uint104) {
    357
            if (value > type(uint104).max) {
    358
                revert SafeCastOverflowedUintDowncast(104, value);
    359
            }
    360
            return uint104(value);
    361
        }
    362
    
                                                    
                                                
    363
        /**
    364
         * @dev Returns the downcasted uint96 from uint256, reverting on
    365
         * overflow (when the input is greater than largest uint96).
    366
         *
    367
         * Counterpart to Solidity's `uint96` operator.
    368
         *
    369
         * Requirements:
    370
         *
    371
         * - input must fit into 96 bits
    372
         */
    373
        function toUint96(uint256 value) internal pure returns (uint96) {
    374
            if (value > type(uint96).max) {
    375
                revert SafeCastOverflowedUintDowncast(96, value);
    376
            }
    377
            return uint96(value);
    378
        }
    379
    
                                                    
                                                
    380
        /**
    381
         * @dev Returns the downcasted uint88 from uint256, reverting on
    382
         * overflow (when the input is greater than largest uint88).
    383
         *
    384
         * Counterpart to Solidity's `uint88` operator.
    385
         *
    386
         * Requirements:
    387
         *
    388
         * - input must fit into 88 bits
    389
         */
    390
        function toUint88(uint256 value) internal pure returns (uint88) {
    391
            if (value > type(uint88).max) {
    392
                revert SafeCastOverflowedUintDowncast(88, value);
    393
            }
    394
            return uint88(value);
    395
        }
    396
    
                                                    
                                                
    397
        /**
    398
         * @dev Returns the downcasted uint80 from uint256, reverting on
    399
         * overflow (when the input is greater than largest uint80).
    400
         *
    401
         * Counterpart to Solidity's `uint80` operator.
    402
         *
    403
         * Requirements:
    404
         *
    405
         * - input must fit into 80 bits
    406
         */
    407
        function toUint80(uint256 value) internal pure returns (uint80) {
    408
            if (value > type(uint80).max) {
    409
                revert SafeCastOverflowedUintDowncast(80, value);
    410
            }
    411
            return uint80(value);
    412
        }
    413
    
                                                    
                                                
    414
        /**
    415
         * @dev Returns the downcasted uint72 from uint256, reverting on
    416
         * overflow (when the input is greater than largest uint72).
    417
         *
    418
         * Counterpart to Solidity's `uint72` operator.
    419
         *
    420
         * Requirements:
    421
         *
    422
         * - input must fit into 72 bits
    423
         */
    424
        function toUint72(uint256 value) internal pure returns (uint72) {
    425
            if (value > type(uint72).max) {
    426
                revert SafeCastOverflowedUintDowncast(72, value);
    427
            }
    428
            return uint72(value);
    429
        }
    430
    
                                                    
                                                
    431
        /**
    432
         * @dev Returns the downcasted uint64 from uint256, reverting on
    433
         * overflow (when the input is greater than largest uint64).
    434
         *
    435
         * Counterpart to Solidity's `uint64` operator.
    436
         *
    437
         * Requirements:
    438
         *
    439
         * - input must fit into 64 bits
    440
         */
    441
        function toUint64(uint256 value) internal pure returns (uint64) {
    442
            if (value > type(uint64).max) {
    443
                revert SafeCastOverflowedUintDowncast(64, value);
    444
            }
    445
            return uint64(value);
    446
        }
    447
    
                                                    
                                                
    448
        /**
    449
         * @dev Returns the downcasted uint56 from uint256, reverting on
    450
         * overflow (when the input is greater than largest uint56).
    451
         *
    452
         * Counterpart to Solidity's `uint56` operator.
    453
         *
    454
         * Requirements:
    455
         *
    456
         * - input must fit into 56 bits
    457
         */
    458
        function toUint56(uint256 value) internal pure returns (uint56) {
    459
            if (value > type(uint56).max) {
    460
                revert SafeCastOverflowedUintDowncast(56, value);
    461
            }
    462
            return uint56(value);
    463
        }
    464
    
                                                    
                                                
    465
        /**
    466
         * @dev Returns the downcasted uint48 from uint256, reverting on
    467
         * overflow (when the input is greater than largest uint48).
    468
         *
    469
         * Counterpart to Solidity's `uint48` operator.
    470
         *
    471
         * Requirements:
    472
         *
    473
         * - input must fit into 48 bits
    474
         */
    475
        function toUint48(uint256 value) internal pure returns (uint48) {
    476
            if (value > type(uint48).max) {
    477
                revert SafeCastOverflowedUintDowncast(48, value);
    478
            }
    479
            return uint48(value);
    480
        }
    481
    
                                                    
                                                
    482
        /**
    483
         * @dev Returns the downcasted uint40 from uint256, reverting on
    484
         * overflow (when the input is greater than largest uint40).
    485
         *
    486
         * Counterpart to Solidity's `uint40` operator.
    487
         *
    488
         * Requirements:
    489
         *
    490
         * - input must fit into 40 bits
    491
         */
    492
        function toUint40(uint256 value) internal pure returns (uint40) {
    493
            if (value > type(uint40).max) {
    494
                revert SafeCastOverflowedUintDowncast(40, value);
    495
            }
    496
            return uint40(value);
    497
        }
    498
    
                                                    
                                                
    499
        /**
    500
         * @dev Returns the downcasted uint32 from uint256, reverting on
    501
         * overflow (when the input is greater than largest uint32).
    502
         *
    503
         * Counterpart to Solidity's `uint32` operator.
    504
         *
    505
         * Requirements:
    506
         *
    507
         * - input must fit into 32 bits
    508
         */
    509
        function toUint32(uint256 value) internal pure returns (uint32) {
    510
            if (value > type(uint32).max) {
    511
                revert SafeCastOverflowedUintDowncast(32, value);
    512
            }
    513
            return uint32(value);
    514
        }
    515
    
                                                    
                                                
    516
        /**
    517
         * @dev Returns the downcasted uint24 from uint256, reverting on
    518
         * overflow (when the input is greater than largest uint24).
    519
         *
    520
         * Counterpart to Solidity's `uint24` operator.
    521
         *
    522
         * Requirements:
    523
         *
    524
         * - input must fit into 24 bits
    525
         */
    526
        function toUint24(uint256 value) internal pure returns (uint24) {
    527
            if (value > type(uint24).max) {
    528
                revert SafeCastOverflowedUintDowncast(24, value);
    529
            }
    530
            return uint24(value);
    531
        }
    532
    
                                                    
                                                
    533
        /**
    534
         * @dev Returns the downcasted uint16 from uint256, reverting on
    535
         * overflow (when the input is greater than largest uint16).
    536
         *
    537
         * Counterpart to Solidity's `uint16` operator.
    538
         *
    539
         * Requirements:
    540
         *
    541
         * - input must fit into 16 bits
    542
         */
    543
        function toUint16(uint256 value) internal pure returns (uint16) {
    544
            if (value > type(uint16).max) {
    545
                revert SafeCastOverflowedUintDowncast(16, value);
    546
            }
    547
            return uint16(value);
    548
        }
    549
    
                                                    
                                                
    550
        /**
    551
         * @dev Returns the downcasted uint8 from uint256, reverting on
    552
         * overflow (when the input is greater than largest uint8).
    553
         *
    554
         * Counterpart to Solidity's `uint8` operator.
    555
         *
    556
         * Requirements:
    557
         *
    558
         * - input must fit into 8 bits
    559
         */
    560
        function toUint8(uint256 value) internal pure returns (uint8) {
    561
            if (value > type(uint8).max) {
    562
                revert SafeCastOverflowedUintDowncast(8, value);
    563
            }
    564
            return uint8(value);
    565
        }
    566
    
                                                    
                                                
    567
        /**
    568
         * @dev Converts a signed int256 into an unsigned uint256.
    569
         *
    570
         * Requirements:
    571
         *
    572
         * - input must be greater than or equal to 0.
    573
         */
    574
        function toUint256(int256 value) internal pure returns (uint256) {
    575
            if (value < 0) {
    576
                revert SafeCastOverflowedIntToUint(value);
    577
            }
    578
            return uint256(value);
    579
        }
    580
    
                                                    
                                                
    581
        /**
    582
         * @dev Returns the downcasted int248 from int256, reverting on
    583
         * overflow (when the input is less than smallest int248 or
    584
         * greater than largest int248).
    585
         *
    586
         * Counterpart to Solidity's `int248` operator.
    587
         *
    588
         * Requirements:
    589
         *
    590
         * - input must fit into 248 bits
    591
         */
    592
        function toInt248(int256 value) internal pure returns (int248 downcasted) {
    593
            downcasted = int248(value);
    594
            if (downcasted != value) {
    595
                revert SafeCastOverflowedIntDowncast(248, value);
    596
            }
    597
        }
    598
    
                                                    
                                                
    599
        /**
    600
         * @dev Returns the downcasted int240 from int256, reverting on
    601
         * overflow (when the input is less than smallest int240 or
    602
         * greater than largest int240).
    603
         *
    604
         * Counterpart to Solidity's `int240` operator.
    605
         *
    606
         * Requirements:
    607
         *
    608
         * - input must fit into 240 bits
    609
         */
    610
        function toInt240(int256 value) internal pure returns (int240 downcasted) {
    611
            downcasted = int240(value);
    612
            if (downcasted != value) {
    613
                revert SafeCastOverflowedIntDowncast(240, value);
    614
            }
    615
        }
    616
    
                                                    
                                                
    617
        /**
    618
         * @dev Returns the downcasted int232 from int256, reverting on
    619
         * overflow (when the input is less than smallest int232 or
    620
         * greater than largest int232).
    621
         *
    622
         * Counterpart to Solidity's `int232` operator.
    623
         *
    624
         * Requirements:
    625
         *
    626
         * - input must fit into 232 bits
    627
         */
    628
        function toInt232(int256 value) internal pure returns (int232 downcasted) {
    629
            downcasted = int232(value);
    630
            if (downcasted != value) {
    631
                revert SafeCastOverflowedIntDowncast(232, value);
    632
            }
    633
        }
    634
    
                                                    
                                                
    635
        /**
    636
         * @dev Returns the downcasted int224 from int256, reverting on
    637
         * overflow (when the input is less than smallest int224 or
    638
         * greater than largest int224).
    639
         *
    640
         * Counterpart to Solidity's `int224` operator.
    641
         *
    642
         * Requirements:
    643
         *
    644
         * - input must fit into 224 bits
    645
         */
    646
        function toInt224(int256 value) internal pure returns (int224 downcasted) {
    647
            downcasted = int224(value);
    648
            if (downcasted != value) {
    649
                revert SafeCastOverflowedIntDowncast(224, value);
    650
            }
    651
        }
    652
    
                                                    
                                                
    653
        /**
    654
         * @dev Returns the downcasted int216 from int256, reverting on
    655
         * overflow (when the input is less than smallest int216 or
    656
         * greater than largest int216).
    657
         *
    658
         * Counterpart to Solidity's `int216` operator.
    659
         *
    660
         * Requirements:
    661
         *
    662
         * - input must fit into 216 bits
    663
         */
    664
        function toInt216(int256 value) internal pure returns (int216 downcasted) {
    665
            downcasted = int216(value);
    666
            if (downcasted != value) {
    667
                revert SafeCastOverflowedIntDowncast(216, value);
    668
            }
    669
        }
    670
    
                                                    
                                                
    671
        /**
    672
         * @dev Returns the downcasted int208 from int256, reverting on
    673
         * overflow (when the input is less than smallest int208 or
    674
         * greater than largest int208).
    675
         *
    676
         * Counterpart to Solidity's `int208` operator.
    677
         *
    678
         * Requirements:
    679
         *
    680
         * - input must fit into 208 bits
    681
         */
    682
        function toInt208(int256 value) internal pure returns (int208 downcasted) {
    683
            downcasted = int208(value);
    684
            if (downcasted != value) {
    685
                revert SafeCastOverflowedIntDowncast(208, value);
    686
            }
    687
        }
    688
    
                                                    
                                                
    689
        /**
    690
         * @dev Returns the downcasted int200 from int256, reverting on
    691
         * overflow (when the input is less than smallest int200 or
    692
         * greater than largest int200).
    693
         *
    694
         * Counterpart to Solidity's `int200` operator.
    695
         *
    696
         * Requirements:
    697
         *
    698
         * - input must fit into 200 bits
    699
         */
    700
        function toInt200(int256 value) internal pure returns (int200 downcasted) {
    701
            downcasted = int200(value);
    702
            if (downcasted != value) {
    703
                revert SafeCastOverflowedIntDowncast(200, value);
    704
            }
    705
        }
    706
    
                                                    
                                                
    707
        /**
    708
         * @dev Returns the downcasted int192 from int256, reverting on
    709
         * overflow (when the input is less than smallest int192 or
    710
         * greater than largest int192).
    711
         *
    712
         * Counterpart to Solidity's `int192` operator.
    713
         *
    714
         * Requirements:
    715
         *
    716
         * - input must fit into 192 bits
    717
         */
    718
        function toInt192(int256 value) internal pure returns (int192 downcasted) {
    719
            downcasted = int192(value);
    720
            if (downcasted != value) {
    721
                revert SafeCastOverflowedIntDowncast(192, value);
    722
            }
    723
        }
    724
    
                                                    
                                                
    725
        /**
    726
         * @dev Returns the downcasted int184 from int256, reverting on
    727
         * overflow (when the input is less than smallest int184 or
    728
         * greater than largest int184).
    729
         *
    730
         * Counterpart to Solidity's `int184` operator.
    731
         *
    732
         * Requirements:
    733
         *
    734
         * - input must fit into 184 bits
    735
         */
    736
        function toInt184(int256 value) internal pure returns (int184 downcasted) {
    737
            downcasted = int184(value);
    738
            if (downcasted != value) {
    739
                revert SafeCastOverflowedIntDowncast(184, value);
    740
            }
    741
        }
    742
    
                                                    
                                                
    743
        /**
    744
         * @dev Returns the downcasted int176 from int256, reverting on
    745
         * overflow (when the input is less than smallest int176 or
    746
         * greater than largest int176).
    747
         *
    748
         * Counterpart to Solidity's `int176` operator.
    749
         *
    750
         * Requirements:
    751
         *
    752
         * - input must fit into 176 bits
    753
         */
    754
        function toInt176(int256 value) internal pure returns (int176 downcasted) {
    755
            downcasted = int176(value);
    756
            if (downcasted != value) {
    757
                revert SafeCastOverflowedIntDowncast(176, value);
    758
            }
    759
        }
    760
    
                                                    
                                                
    761
        /**
    762
         * @dev Returns the downcasted int168 from int256, reverting on
    763
         * overflow (when the input is less than smallest int168 or
    764
         * greater than largest int168).
    765
         *
    766
         * Counterpart to Solidity's `int168` operator.
    767
         *
    768
         * Requirements:
    769
         *
    770
         * - input must fit into 168 bits
    771
         */
    772
        function toInt168(int256 value) internal pure returns (int168 downcasted) {
    773
            downcasted = int168(value);
    774
            if (downcasted != value) {
    775
                revert SafeCastOverflowedIntDowncast(168, value);
    776
            }
    777
        }
    778
    
                                                    
                                                
    779
        /**
    780
         * @dev Returns the downcasted int160 from int256, reverting on
    781
         * overflow (when the input is less than smallest int160 or
    782
         * greater than largest int160).
    783
         *
    784
         * Counterpart to Solidity's `int160` operator.
    785
         *
    786
         * Requirements:
    787
         *
    788
         * - input must fit into 160 bits
    789
         */
    790
        function toInt160(int256 value) internal pure returns (int160 downcasted) {
    791
            downcasted = int160(value);
    792
            if (downcasted != value) {
    793
                revert SafeCastOverflowedIntDowncast(160, value);
    794
            }
    795
        }
    796
    
                                                    
                                                
    797
        /**
    798
         * @dev Returns the downcasted int152 from int256, reverting on
    799
         * overflow (when the input is less than smallest int152 or
    800
         * greater than largest int152).
    801
         *
    802
         * Counterpart to Solidity's `int152` operator.
    803
         *
    804
         * Requirements:
    805
         *
    806
         * - input must fit into 152 bits
    807
         */
    808
        function toInt152(int256 value) internal pure returns (int152 downcasted) {
    809
            downcasted = int152(value);
    810
            if (downcasted != value) {
    811
                revert SafeCastOverflowedIntDowncast(152, value);
    812
            }
    813
        }
    814
    
                                                    
                                                
    815
        /**
    816
         * @dev Returns the downcasted int144 from int256, reverting on
    817
         * overflow (when the input is less than smallest int144 or
    818
         * greater than largest int144).
    819
         *
    820
         * Counterpart to Solidity's `int144` operator.
    821
         *
    822
         * Requirements:
    823
         *
    824
         * - input must fit into 144 bits
    825
         */
    826
        function toInt144(int256 value) internal pure returns (int144 downcasted) {
    827
            downcasted = int144(value);
    828
            if (downcasted != value) {
    829
                revert SafeCastOverflowedIntDowncast(144, value);
    830
            }
    831
        }
    832
    
                                                    
                                                
    833
        /**
    834
         * @dev Returns the downcasted int136 from int256, reverting on
    835
         * overflow (when the input is less than smallest int136 or
    836
         * greater than largest int136).
    837
         *
    838
         * Counterpart to Solidity's `int136` operator.
    839
         *
    840
         * Requirements:
    841
         *
    842
         * - input must fit into 136 bits
    843
         */
    844
        function toInt136(int256 value) internal pure returns (int136 downcasted) {
    845
            downcasted = int136(value);
    846
            if (downcasted != value) {
    847
                revert SafeCastOverflowedIntDowncast(136, value);
    848
            }
    849
        }
    850
    
                                                    
                                                
    851
        /**
    852
         * @dev Returns the downcasted int128 from int256, reverting on
    853
         * overflow (when the input is less than smallest int128 or
    854
         * greater than largest int128).
    855
         *
    856
         * Counterpart to Solidity's `int128` operator.
    857
         *
    858
         * Requirements:
    859
         *
    860
         * - input must fit into 128 bits
    861
         */
    862
        function toInt128(int256 value) internal pure returns (int128 downcasted) {
    863
            downcasted = int128(value);
    864
            if (downcasted != value) {
    865
                revert SafeCastOverflowedIntDowncast(128, value);
    866
            }
    867
        }
    868
    
                                                    
                                                
    869
        /**
    870
         * @dev Returns the downcasted int120 from int256, reverting on
    871
         * overflow (when the input is less than smallest int120 or
    872
         * greater than largest int120).
    873
         *
    874
         * Counterpart to Solidity's `int120` operator.
    875
         *
    876
         * Requirements:
    877
         *
    878
         * - input must fit into 120 bits
    879
         */
    880
        function toInt120(int256 value) internal pure returns (int120 downcasted) {
    881
            downcasted = int120(value);
    882
            if (downcasted != value) {
    883
                revert SafeCastOverflowedIntDowncast(120, value);
    884
            }
    885
        }
    886
    
                                                    
                                                
    887
        /**
    888
         * @dev Returns the downcasted int112 from int256, reverting on
    889
         * overflow (when the input is less than smallest int112 or
    890
         * greater than largest int112).
    891
         *
    892
         * Counterpart to Solidity's `int112` operator.
    893
         *
    894
         * Requirements:
    895
         *
    896
         * - input must fit into 112 bits
    897
         */
    898
        function toInt112(int256 value) internal pure returns (int112 downcasted) {
    899
            downcasted = int112(value);
    900
            if (downcasted != value) {
    901
                revert SafeCastOverflowedIntDowncast(112, value);
    902
            }
    903
        }
    904
    
                                                    
                                                
    905
        /**
    906
         * @dev Returns the downcasted int104 from int256, reverting on
    907
         * overflow (when the input is less than smallest int104 or
    908
         * greater than largest int104).
    909
         *
    910
         * Counterpart to Solidity's `int104` operator.
    911
         *
    912
         * Requirements:
    913
         *
    914
         * - input must fit into 104 bits
    915
         */
    916
        function toInt104(int256 value) internal pure returns (int104 downcasted) {
    917
            downcasted = int104(value);
    918
            if (downcasted != value) {
    919
                revert SafeCastOverflowedIntDowncast(104, value);
    920
            }
    921
        }
    922
    
                                                    
                                                
    923
        /**
    924
         * @dev Returns the downcasted int96 from int256, reverting on
    925
         * overflow (when the input is less than smallest int96 or
    926
         * greater than largest int96).
    927
         *
    928
         * Counterpart to Solidity's `int96` operator.
    929
         *
    930
         * Requirements:
    931
         *
    932
         * - input must fit into 96 bits
    933
         */
    934
        function toInt96(int256 value) internal pure returns (int96 downcasted) {
    935
            downcasted = int96(value);
    936
            if (downcasted != value) {
    937
                revert SafeCastOverflowedIntDowncast(96, value);
    938
            }
    939
        }
    940
    
                                                    
                                                
    941
        /**
    942
         * @dev Returns the downcasted int88 from int256, reverting on
    943
         * overflow (when the input is less than smallest int88 or
    944
         * greater than largest int88).
    945
         *
    946
         * Counterpart to Solidity's `int88` operator.
    947
         *
    948
         * Requirements:
    949
         *
    950
         * - input must fit into 88 bits
    951
         */
    952
        function toInt88(int256 value) internal pure returns (int88 downcasted) {
    953
            downcasted = int88(value);
    954
            if (downcasted != value) {
    955
                revert SafeCastOverflowedIntDowncast(88, value);
    956
            }
    957
        }
    958
    
                                                    
                                                
    959
        /**
    960
         * @dev Returns the downcasted int80 from int256, reverting on
    961
         * overflow (when the input is less than smallest int80 or
    962
         * greater than largest int80).
    963
         *
    964
         * Counterpart to Solidity's `int80` operator.
    965
         *
    966
         * Requirements:
    967
         *
    968
         * - input must fit into 80 bits
    969
         */
    970
        function toInt80(int256 value) internal pure returns (int80 downcasted) {
    971
            downcasted = int80(value);
    972
            if (downcasted != value) {
    973
                revert SafeCastOverflowedIntDowncast(80, value);
    974
            }
    975
        }
    976
    
                                                    
                                                
    977
        /**
    978
         * @dev Returns the downcasted int72 from int256, reverting on
    979
         * overflow (when the input is less than smallest int72 or
    980
         * greater than largest int72).
    981
         *
    982
         * Counterpart to Solidity's `int72` operator.
    983
         *
    984
         * Requirements:
    985
         *
    986
         * - input must fit into 72 bits
    987
         */
    988
        function toInt72(int256 value) internal pure returns (int72 downcasted) {
    989
            downcasted = int72(value);
    990
            if (downcasted != value) {
    991
                revert SafeCastOverflowedIntDowncast(72, value);
    992
            }
    993
        }
    994
    
                                                    
                                                
    995
        /**
    996
         * @dev Returns the downcasted int64 from int256, reverting on
    997
         * overflow (when the input is less than smallest int64 or
    998
         * greater than largest int64).
    999
         *
    1000
         * Counterpart to Solidity's `int64` operator.
    1001
         *
    1002
         * Requirements:
    1003
         *
    1004
         * - input must fit into 64 bits
    1005
         */
    1006
        function toInt64(int256 value) internal pure returns (int64 downcasted) {
    1007
            downcasted = int64(value);
    1008
            if (downcasted != value) {
    1009
                revert SafeCastOverflowedIntDowncast(64, value);
    1010
            }
    1011
        }
    1012
    
                                                    
                                                
    1013
        /**
    1014
         * @dev Returns the downcasted int56 from int256, reverting on
    1015
         * overflow (when the input is less than smallest int56 or
    1016
         * greater than largest int56).
    1017
         *
    1018
         * Counterpart to Solidity's `int56` operator.
    1019
         *
    1020
         * Requirements:
    1021
         *
    1022
         * - input must fit into 56 bits
    1023
         */
    1024
        function toInt56(int256 value) internal pure returns (int56 downcasted) {
    1025
            downcasted = int56(value);
    1026
            if (downcasted != value) {
    1027
                revert SafeCastOverflowedIntDowncast(56, value);
    1028
            }
    1029
        }
    1030
    
                                                    
                                                
    1031
        /**
    1032
         * @dev Returns the downcasted int48 from int256, reverting on
    1033
         * overflow (when the input is less than smallest int48 or
    1034
         * greater than largest int48).
    1035
         *
    1036
         * Counterpart to Solidity's `int48` operator.
    1037
         *
    1038
         * Requirements:
    1039
         *
    1040
         * - input must fit into 48 bits
    1041
         */
    1042
        function toInt48(int256 value) internal pure returns (int48 downcasted) {
    1043
            downcasted = int48(value);
    1044
            if (downcasted != value) {
    1045
                revert SafeCastOverflowedIntDowncast(48, value);
    1046
            }
    1047
        }
    1048
    
                                                    
                                                
    1049
        /**
    1050
         * @dev Returns the downcasted int40 from int256, reverting on
    1051
         * overflow (when the input is less than smallest int40 or
    1052
         * greater than largest int40).
    1053
         *
    1054
         * Counterpart to Solidity's `int40` operator.
    1055
         *
    1056
         * Requirements:
    1057
         *
    1058
         * - input must fit into 40 bits
    1059
         */
    1060
        function toInt40(int256 value) internal pure returns (int40 downcasted) {
    1061
            downcasted = int40(value);
    1062
            if (downcasted != value) {
    1063
                revert SafeCastOverflowedIntDowncast(40, value);
    1064
            }
    1065
        }
    1066
    
                                                    
                                                
    1067
        /**
    1068
         * @dev Returns the downcasted int32 from int256, reverting on
    1069
         * overflow (when the input is less than smallest int32 or
    1070
         * greater than largest int32).
    1071
         *
    1072
         * Counterpart to Solidity's `int32` operator.
    1073
         *
    1074
         * Requirements:
    1075
         *
    1076
         * - input must fit into 32 bits
    1077
         */
    1078
        function toInt32(int256 value) internal pure returns (int32 downcasted) {
    1079
            downcasted = int32(value);
    1080
            if (downcasted != value) {
    1081
                revert SafeCastOverflowedIntDowncast(32, value);
    1082
            }
    1083
        }
    1084
    
                                                    
                                                
    1085
        /**
    1086
         * @dev Returns the downcasted int24 from int256, reverting on
    1087
         * overflow (when the input is less than smallest int24 or
    1088
         * greater than largest int24).
    1089
         *
    1090
         * Counterpart to Solidity's `int24` operator.
    1091
         *
    1092
         * Requirements:
    1093
         *
    1094
         * - input must fit into 24 bits
    1095
         */
    1096
        function toInt24(int256 value) internal pure returns (int24 downcasted) {
    1097
            downcasted = int24(value);
    1098
            if (downcasted != value) {
    1099
                revert SafeCastOverflowedIntDowncast(24, value);
    1100
            }
    1101
        }
    1102
    
                                                    
                                                
    1103
        /**
    1104
         * @dev Returns the downcasted int16 from int256, reverting on
    1105
         * overflow (when the input is less than smallest int16 or
    1106
         * greater than largest int16).
    1107
         *
    1108
         * Counterpart to Solidity's `int16` operator.
    1109
         *
    1110
         * Requirements:
    1111
         *
    1112
         * - input must fit into 16 bits
    1113
         */
    1114
        function toInt16(int256 value) internal pure returns (int16 downcasted) {
    1115
            downcasted = int16(value);
    1116
            if (downcasted != value) {
    1117
                revert SafeCastOverflowedIntDowncast(16, value);
    1118
            }
    1119
        }
    1120
    
                                                    
                                                
    1121
        /**
    1122
         * @dev Returns the downcasted int8 from int256, reverting on
    1123
         * overflow (when the input is less than smallest int8 or
    1124
         * greater than largest int8).
    1125
         *
    1126
         * Counterpart to Solidity's `int8` operator.
    1127
         *
    1128
         * Requirements:
    1129
         *
    1130
         * - input must fit into 8 bits
    1131
         */
    1132
        function toInt8(int256 value) internal pure returns (int8 downcasted) {
    1133
            downcasted = int8(value);
    1134
            if (downcasted != value) {
    1135
                revert SafeCastOverflowedIntDowncast(8, value);
    1136
            }
    1137
        }
    1138
    
                                                    
                                                
    1139
        /**
    1140
         * @dev Converts an unsigned uint256 into a signed int256.
    1141
         *
    1142
         * Requirements:
    1143
         *
    1144
         * - input must be less than or equal to maxInt256.
    1145
         */
    1146
        function toInt256(uint256 value) internal pure returns (int256) {
    1147
            // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
    1148
            if (value > uint256(type(int256).max)) {
    1149
                revert SafeCastOverflowedUintToInt(value);
    1150
            }
    1151
            return int256(value);
    1152
        }
    1153
    
                                                    
                                                
    1154
        /**
    1155
         * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
    1156
         */
    1157
        function toUint(bool b) internal pure returns (uint256 u) {
    1158
            assembly ("memory-safe") {
    1159
    ✓ 15.5M
                u := iszero(iszero(b))
    1160
            }
    1161
        }
    1162
    }
    1163
    
                                                    
                                                
    100.0% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol
    Lines covered: 1 / 1 (100.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol)
    3
    
                                                    
                                                
    4
    pragma solidity ^0.8.20;
    5
    
                                                    
                                                
    6
    import {SafeCast} from "./SafeCast.sol";
    7
    
                                                    
                                                
    8
    /**
    9
     * @dev Standard signed math utilities missing in the Solidity language.
    10
     */
    11
    ✓ 34.3K
    ⟲ 34.3K
    library SignedMath {
    12
        /**
    13
         * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
    14
         *
    15
         * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
    16
         * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
    17
         * one branch when needed, making this function more expensive.
    18
         */
    19
        function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {
    20
            unchecked {
    21
                // branchless ternary works because:
    22
                // b ^ (a ^ b) == a
    23
                // b ^ 0 == b
    24
                return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
    25
            }
    26
        }
    27
    
                                                    
                                                
    28
        /**
    29
         * @dev Returns the largest of two signed numbers.
    30
         */
    31
        function max(int256 a, int256 b) internal pure returns (int256) {
    32
            return ternary(a > b, a, b);
    33
        }
    34
    
                                                    
                                                
    35
        /**
    36
         * @dev Returns the smallest of two signed numbers.
    37
         */
    38
        function min(int256 a, int256 b) internal pure returns (int256) {
    39
            return ternary(a < b, a, b);
    40
        }
    41
    
                                                    
                                                
    42
        /**
    43
         * @dev Returns the average of two signed numbers without overflow.
    44
         * The result is rounded towards zero.
    45
         */
    46
        function average(int256 a, int256 b) internal pure returns (int256) {
    47
            // Formula from the book "Hacker's Delight"
    48
            int256 x = (a & b) + ((a ^ b) >> 1);
    49
            return x + (int256(uint256(x) >> 255) & (a ^ b));
    50
        }
    51
    
                                                    
                                                
    52
        /**
    53
         * @dev Returns the absolute unsigned value of a signed value.
    54
         */
    55
        function abs(int256 n) internal pure returns (uint256) {
    56
            unchecked {
    57
                // Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
    58
                // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
    59
                // taking advantage of the most significant (or "sign" bit) in two's complement representation.
    60
                // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
    61
                // the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
    62
                int256 mask = n >> 255;
    63
    
                                                    
                                                
    64
                // A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
    65
                return uint256((n + mask) ^ mask);
    66
            }
    67
        }
    68
    }
    69
    
                                                    
                                                
    89.2% lib/v2-core/lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol
    Lines covered: 33 / 37 (89.2%)
    1
    // SPDX-License-Identifier: MIT
    2
    // OpenZeppelin Contracts (last updated v5.3.0) (utils/structs/EnumerableSet.sol)
    3
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    4
    
                                                    
                                                
    5
    pragma solidity ^0.8.20;
    6
    
                                                    
                                                
    7
    import {Arrays} from "../Arrays.sol";
    8
    import {Math} from "../math/Math.sol";
    9
    
                                                    
                                                
    10
    /**
    11
     * @dev Library for managing
    12
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
    13
     * types.
    14
     *
    15
     * Sets have the following properties:
    16
     *
    17
     * - Elements are added, removed, and checked for existence in constant time
    18
     * (O(1)).
    19
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
    20
     * - Set can be cleared (all elements removed) in O(n).
    21
     *
    22
     * ```solidity
    23
     * contract Example {
    24
     *     // Add the library methods
    25
     *     using EnumerableSet for EnumerableSet.AddressSet;
    26
     *
    27
     *     // Declare a set state variable
    28
     *     EnumerableSet.AddressSet private mySet;
    29
     * }
    30
     * ```
    31
     *
    32
     * The following types are supported:
    33
     *
    34
     * - `bytes32` (`Bytes32Set`) since v3.3.0
    35
     * - `address` (`AddressSet`) since v3.3.0
    36
     * - `uint256` (`UintSet`) since v3.3.0
    37
     * - `string` (`StringSet`) since v5.4.0
    38
     * - `bytes` (`BytesSet`) since v5.4.0
    39
     *
    40
     * [WARNING]
    41
     * ====
    42
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
    43
     * unusable.
    44
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
    45
     *
    46
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
    47
     * array of EnumerableSet.
    48
     * ====
    49
     */
    50
    ✓ 23
    library EnumerableSet {
    51
        // To implement this library for multiple types with as little code
    52
        // repetition as possible, we write it in terms of a generic Set type with
    53
        // bytes32 values.
    54
        // The Set implementation uses private functions, and user-facing
    55
        // implementations (such as AddressSet) are just wrappers around the
    56
        // underlying Set.
    57
        // This means that we can only create new EnumerableSets for types that fit
    58
        // in bytes32.
    59
    
                                                    
                                                
    60
        struct Set {
    61
            // Storage of set values
    62
            bytes32[] _values;
    63
            // Position is the index of the value in the `values` array plus 1.
    64
            // Position 0 is used to mean a value is not in the set.
    65
            mapping(bytes32 value => uint256) _positions;
    66
        }
    67
    
                                                    
                                                
    68
        /**
    69
         * @dev Add a value to a set. O(1).
    70
         *
    71
         * Returns true if the value was added to the set, that is if it was not
    72
         * already present.
    73
         */
    74
    ✓ 5.5M
        function _add(Set storage set, bytes32 value) private returns (bool) {
    75
            if (!_contains(set, value)) {
    76
    ✓ 4.2M
                set._values.push(value);
    77
                // The value is stored at length-1, but we add 1 to all indexes
    78
                // and use 0 as a sentinel value
    79
    ✓ 4.2M
                set._positions[value] = set._values.length;
    80
    ✓ 4.2M
                return true;
    81
            } else {
    82
    ✓ 5.0K
                return false;
    83
            }
    84
        }
    85
    
                                                    
                                                
    86
        /**
    87
         * @dev Removes a value from a set. O(1).
    88
         *
    89
         * Returns true if the value was removed from the set, that is if it was
    90
         * present.
    91
         */
    92
    ✓ 501.9K
        function _remove(Set storage set, bytes32 value) private returns (bool) {
    93
            // We cache the value's position to prevent multiple reads from the same storage slot
    94
    ✓ 501.9K
            uint256 position = set._positions[value];
    95
    
                                                    
                                                
    96
    ✓ 501.9K
            if (position != 0) {
    97
                // Equivalent to contains(set, value)
    98
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
    99
                // the array, and then remove the last element (sometimes called as 'swap and pop').
    100
                // This modifies the order of the array, as noted in {at}.
    101
    
                                                    
                                                
    102
    ✓ 501.9K
                uint256 valueIndex = position - 1;
    103
    ✓ 501.9K
                uint256 lastIndex = set._values.length - 1;
    104
    
                                                    
                                                
    105
    ✓ 501.9K
                if (valueIndex != lastIndex) {
    106
    ✓ 246.0K
                    bytes32 lastValue = set._values[lastIndex];
    107
    
                                                    
                                                
    108
                    // Move the lastValue to the index where the value to delete is
    109
    ✓ 246.0K
                    set._values[valueIndex] = lastValue;
    110
                    // Update the tracked position of the lastValue (that was just moved)
    111
    ✓ 246.0K
                    set._positions[lastValue] = position;
    112
                }
    113
    
                                                    
                                                
    114
                // Delete the slot where the moved value was stored
    115
    ✓ 501.9K
                set._values.pop();
    116
    
                                                    
                                                
    117
                // Delete the tracked position for the deleted slot
    118
    ✓ 501.9K
                delete set._positions[value];
    119
    
                                                    
                                                
    120
    ✓ 501.9K
                return true;
    121
            } else {
    122
    ✓ 3.5K
                return false;
    123
            }
    124
        }
    125
    
                                                    
                                                
    126
        /**
    127
         * @dev Removes all the values from a set. O(n).
    128
         *
    129
         * WARNING: This function has an unbounded cost that scales with set size. Developers should keep in mind that
    130
         * using it may render the function uncallable if the set grows to the point where clearing it consumes too much
    131
         * gas to fit in a block.
    132
         */
    133
        function _clear(Set storage set) private {
    134
            uint256 len = _length(set);
    135
            for (uint256 i = 0; i < len; ++i) {
    136
                delete set._positions[set._values[i]];
    137
            }
    138
            Arrays.unsafeSetLength(set._values, 0);
    139
        }
    140
    
                                                    
                                                
    141
        /**
    142
         * @dev Returns true if the value is in the set. O(1).
    143
         */
    144
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
    145
    ✓ 21.9M
            return set._positions[value] != 0;
    146
        }
    147
    
                                                    
                                                
    148
        /**
    149
         * @dev Returns the number of values on the set. O(1).
    150
         */
    151
        function _length(Set storage set) private view returns (uint256) {
    152
    ✓ 81.8K
            return set._values.length;
    153
        }
    154
    
                                                    
                                                
    155
        /**
    156
         * @dev Returns the value stored at position `index` in the set. O(1).
    157
         *
    158
         * Note that there are no guarantees on the ordering of values inside the
    159
         * array, and it may change when more values are added or removed.
    160
         *
    161
         * Requirements:
    162
         *
    163
         * - `index` must be strictly less than {length}.
    164
         */
    165
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
    166
            return set._values[index];
    167
        }
    168
    
                                                    
                                                
    169
        /**
    170
         * @dev Return the entire set in an array
    171
         *
    172
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    173
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    174
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    175
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    176
         */
    177
    ✓ 164.6K
        function _values(Set storage set) private view returns (bytes32[] memory) {
    178
    ✓ 164.6K
            return set._values;
    179
        }
    180
    
                                                    
                                                
    181
        /**
    182
         * @dev Return a slice of the set in an array
    183
         *
    184
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    185
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    186
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    187
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    188
         */
    189
        function _values(Set storage set, uint256 start, uint256 end) private view returns (bytes32[] memory) {
    190
            unchecked {
    191
                end = Math.min(end, _length(set));
    192
                start = Math.min(start, end);
    193
    
                                                    
                                                
    194
                uint256 len = end - start;
    195
                bytes32[] memory result = new bytes32[](len);
    196
                for (uint256 i = 0; i < len; ++i) {
    197
                    result[i] = Arrays.unsafeAccess(set._values, start + i).value;
    198
                }
    199
                return result;
    200
            }
    201
        }
    202
    
                                                    
                                                
    203
        // Bytes32Set
    204
    
                                                    
                                                
    205
        struct Bytes32Set {
    206
            Set _inner;
    207
        }
    208
    
                                                    
                                                
    209
        /**
    210
         * @dev Add a value to a set. O(1).
    211
         *
    212
         * Returns true if the value was added to the set, that is if it was not
    213
         * already present.
    214
         */
    215
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    216
            return _add(set._inner, value);
    217
        }
    218
    
                                                    
                                                
    219
        /**
    220
         * @dev Removes a value from a set. O(1).
    221
         *
    222
         * Returns true if the value was removed from the set, that is if it was
    223
         * present.
    224
         */
    225
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    226
            return _remove(set._inner, value);
    227
        }
    228
    
                                                    
                                                
    229
        /**
    230
         * @dev Removes all the values from a set. O(n).
    231
         *
    232
         * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
    233
         * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
    234
         */
    235
        function clear(Bytes32Set storage set) internal {
    236
            _clear(set._inner);
    237
        }
    238
    
                                                    
                                                
    239
        /**
    240
         * @dev Returns true if the value is in the set. O(1).
    241
         */
    242
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
    243
            return _contains(set._inner, value);
    244
        }
    245
    
                                                    
                                                
    246
        /**
    247
         * @dev Returns the number of values in the set. O(1).
    248
         */
    249
        function length(Bytes32Set storage set) internal view returns (uint256) {
    250
            return _length(set._inner);
    251
        }
    252
    
                                                    
                                                
    253
        /**
    254
         * @dev Returns the value stored at position `index` in the set. O(1).
    255
         *
    256
         * Note that there are no guarantees on the ordering of values inside the
    257
         * array, and it may change when more values are added or removed.
    258
         *
    259
         * Requirements:
    260
         *
    261
         * - `index` must be strictly less than {length}.
    262
         */
    263
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
    264
            return _at(set._inner, index);
    265
        }
    266
    
                                                    
                                                
    267
        /**
    268
         * @dev Return the entire set in an array
    269
         *
    270
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    271
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    272
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    273
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    274
         */
    275
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
    276
            bytes32[] memory store = _values(set._inner);
    277
            bytes32[] memory result;
    278
    
                                                    
                                                
    279
            assembly ("memory-safe") {
    280
                result := store
    281
            }
    282
    
                                                    
                                                
    283
            return result;
    284
        }
    285
    
                                                    
                                                
    286
        /**
    287
         * @dev Return a slice of the set in an array
    288
         *
    289
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    290
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    291
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    292
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    293
         */
    294
        function values(Bytes32Set storage set, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
    295
            bytes32[] memory store = _values(set._inner, start, end);
    296
            bytes32[] memory result;
    297
    
                                                    
                                                
    298
            assembly ("memory-safe") {
    299
                result := store
    300
            }
    301
    
                                                    
                                                
    302
            return result;
    303
        }
    304
    
                                                    
                                                
    305
        // AddressSet
    306
    
                                                    
                                                
    307
        struct AddressSet {
    308
            Set _inner;
    309
        }
    310
    
                                                    
                                                
    311
        /**
    312
         * @dev Add a value to a set. O(1).
    313
         *
    314
         * Returns true if the value was added to the set, that is if it was not
    315
         * already present.
    316
         */
    317
    ✓ 5.5M
        function add(AddressSet storage set, address value) internal returns (bool) {
    318
    ✓ 5.5M
            return _add(set._inner, bytes32(uint256(uint160(value))));
    319
        }
    320
    
                                                    
                                                
    321
        /**
    322
         * @dev Removes a value from a set. O(1).
    323
         *
    324
         * Returns true if the value was removed from the set, that is if it was
    325
         * present.
    326
         */
    327
    ✓ 501.9K
        function remove(AddressSet storage set, address value) internal returns (bool) {
    328
    ✓ 501.9K
            return _remove(set._inner, bytes32(uint256(uint160(value))));
    329
        }
    330
    
                                                    
                                                
    331
        /**
    332
         * @dev Removes all the values from a set. O(n).
    333
         *
    334
         * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
    335
         * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
    336
         */
    337
        function clear(AddressSet storage set) internal {
    338
            _clear(set._inner);
    339
        }
    340
    
                                                    
                                                
    341
        /**
    342
         * @dev Returns true if the value is in the set. O(1).
    343
         */
    344
    ✓ 21.9M
        function contains(AddressSet storage set, address value) internal view returns (bool) {
    345
    ✓ 21.9M
            return _contains(set._inner, bytes32(uint256(uint160(value))));
    346
        }
    347
    
                                                    
                                                
    348
        /**
    349
         * @dev Returns the number of values in the set. O(1).
    350
         */
    351
    ✓ 81.8K
        function length(AddressSet storage set) internal view returns (uint256) {
    352
    ✓ 81.8K
            return _length(set._inner);
    353
        }
    354
    
                                                    
                                                
    355
        /**
    356
         * @dev Returns the value stored at position `index` in the set. O(1).
    357
         *
    358
         * Note that there are no guarantees on the ordering of values inside the
    359
         * array, and it may change when more values are added or removed.
    360
         *
    361
         * Requirements:
    362
         *
    363
         * - `index` must be strictly less than {length}.
    364
         */
    365
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
    366
            return address(uint160(uint256(_at(set._inner, index))));
    367
        }
    368
    
                                                    
                                                
    369
        /**
    370
         * @dev Return the entire set in an array
    371
         *
    372
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    373
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    374
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    375
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    376
         */
    377
    ✓ 164.6K
        function values(AddressSet storage set) internal view returns (address[] memory) {
    378
    ✓ 164.6K
            bytes32[] memory store = _values(set._inner);
    379
            address[] memory result;
    380
    
                                                    
                                                
    381
            assembly ("memory-safe") {
    382
                result := store
    383
            }
    384
    
                                                    
                                                
    385
            return result;
    386
        }
    387
    
                                                    
                                                
    388
        /**
    389
         * @dev Return a slice of the set in an array
    390
         *
    391
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    392
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    393
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    394
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    395
         */
    396
        function values(AddressSet storage set, uint256 start, uint256 end) internal view returns (address[] memory) {
    397
            bytes32[] memory store = _values(set._inner, start, end);
    398
            address[] memory result;
    399
    
                                                    
                                                
    400
            assembly ("memory-safe") {
    401
                result := store
    402
            }
    403
    
                                                    
                                                
    404
            return result;
    405
        }
    406
    
                                                    
                                                
    407
        // UintSet
    408
    
                                                    
                                                
    409
        struct UintSet {
    410
            Set _inner;
    411
        }
    412
    
                                                    
                                                
    413
        /**
    414
         * @dev Add a value to a set. O(1).
    415
         *
    416
         * Returns true if the value was added to the set, that is if it was not
    417
         * already present.
    418
         */
    419
        function add(UintSet storage set, uint256 value) internal returns (bool) {
    420
            return _add(set._inner, bytes32(value));
    421
        }
    422
    
                                                    
                                                
    423
        /**
    424
         * @dev Removes a value from a set. O(1).
    425
         *
    426
         * Returns true if the value was removed from the set, that is if it was
    427
         * present.
    428
         */
    429
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
    430
            return _remove(set._inner, bytes32(value));
    431
        }
    432
    
                                                    
                                                
    433
        /**
    434
         * @dev Removes all the values from a set. O(n).
    435
         *
    436
         * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
    437
         * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
    438
         */
    439
        function clear(UintSet storage set) internal {
    440
            _clear(set._inner);
    441
        }
    442
    
                                                    
                                                
    443
        /**
    444
         * @dev Returns true if the value is in the set. O(1).
    445
         */
    446
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
    447
            return _contains(set._inner, bytes32(value));
    448
        }
    449
    
                                                    
                                                
    450
        /**
    451
         * @dev Returns the number of values in the set. O(1).
    452
         */
    453
        function length(UintSet storage set) internal view returns (uint256) {
    454
            return _length(set._inner);
    455
        }
    456
    
                                                    
                                                
    457
        /**
    458
         * @dev Returns the value stored at position `index` in the set. O(1).
    459
         *
    460
         * Note that there are no guarantees on the ordering of values inside the
    461
         * array, and it may change when more values are added or removed.
    462
         *
    463
         * Requirements:
    464
         *
    465
         * - `index` must be strictly less than {length}.
    466
         */
    467
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
    468
            return uint256(_at(set._inner, index));
    469
        }
    470
    
                                                    
                                                
    471
        /**
    472
         * @dev Return the entire set in an array
    473
         *
    474
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    475
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    476
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    477
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    478
         */
    479
        function values(UintSet storage set) internal view returns (uint256[] memory) {
    480
            bytes32[] memory store = _values(set._inner);
    481
            uint256[] memory result;
    482
    
                                                    
                                                
    483
            assembly ("memory-safe") {
    484
                result := store
    485
            }
    486
    
                                                    
                                                
    487
            return result;
    488
        }
    489
    
                                                    
                                                
    490
        /**
    491
         * @dev Return a slice of the set in an array
    492
         *
    493
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    494
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    495
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    496
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    497
         */
    498
        function values(UintSet storage set, uint256 start, uint256 end) internal view returns (uint256[] memory) {
    499
            bytes32[] memory store = _values(set._inner, start, end);
    500
            uint256[] memory result;
    501
    
                                                    
                                                
    502
            assembly ("memory-safe") {
    503
                result := store
    504
            }
    505
    
                                                    
                                                
    506
            return result;
    507
        }
    508
    
                                                    
                                                
    509
        struct StringSet {
    510
            // Storage of set values
    511
            string[] _values;
    512
            // Position is the index of the value in the `values` array plus 1.
    513
            // Position 0 is used to mean a value is not in the set.
    514
            mapping(string value => uint256) _positions;
    515
        }
    516
    
                                                    
                                                
    517
        /**
    518
         * @dev Add a value to a set. O(1).
    519
         *
    520
         * Returns true if the value was added to the set, that is if it was not
    521
         * already present.
    522
         */
    523
        function add(StringSet storage set, string memory value) internal returns (bool) {
    524
            if (!contains(set, value)) {
    525
                set._values.push(value);
    526
                // The value is stored at length-1, but we add 1 to all indexes
    527
                // and use 0 as a sentinel value
    528
                set._positions[value] = set._values.length;
    529
                return true;
    530
            } else {
    531
                return false;
    532
            }
    533
        }
    534
    
                                                    
                                                
    535
        /**
    536
         * @dev Removes a value from a set. O(1).
    537
         *
    538
         * Returns true if the value was removed from the set, that is if it was
    539
         * present.
    540
         */
    541
        function remove(StringSet storage set, string memory value) internal returns (bool) {
    542
            // We cache the value's position to prevent multiple reads from the same storage slot
    543
            uint256 position = set._positions[value];
    544
    
                                                    
                                                
    545
            if (position != 0) {
    546
                // Equivalent to contains(set, value)
    547
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
    548
                // the array, and then remove the last element (sometimes called as 'swap and pop').
    549
                // This modifies the order of the array, as noted in {at}.
    550
    
                                                    
                                                
    551
                uint256 valueIndex = position - 1;
    552
                uint256 lastIndex = set._values.length - 1;
    553
    
                                                    
                                                
    554
                if (valueIndex != lastIndex) {
    555
                    string memory lastValue = set._values[lastIndex];
    556
    
                                                    
                                                
    557
                    // Move the lastValue to the index where the value to delete is
    558
                    set._values[valueIndex] = lastValue;
    559
                    // Update the tracked position of the lastValue (that was just moved)
    560
                    set._positions[lastValue] = position;
    561
                }
    562
    
                                                    
                                                
    563
                // Delete the slot where the moved value was stored
    564
                set._values.pop();
    565
    
                                                    
                                                
    566
                // Delete the tracked position for the deleted slot
    567
                delete set._positions[value];
    568
    
                                                    
                                                
    569
                return true;
    570
            } else {
    571
                return false;
    572
            }
    573
        }
    574
    
                                                    
                                                
    575
        /**
    576
         * @dev Removes all the values from a set. O(n).
    577
         *
    578
         * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
    579
         * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
    580
         */
    581
        function clear(StringSet storage set) internal {
    582
            uint256 len = length(set);
    583
            for (uint256 i = 0; i < len; ++i) {
    584
                delete set._positions[set._values[i]];
    585
            }
    586
            Arrays.unsafeSetLength(set._values, 0);
    587
        }
    588
    
                                                    
                                                
    589
        /**
    590
         * @dev Returns true if the value is in the set. O(1).
    591
         */
    592
        function contains(StringSet storage set, string memory value) internal view returns (bool) {
    593
            return set._positions[value] != 0;
    594
        }
    595
    
                                                    
                                                
    596
        /**
    597
         * @dev Returns the number of values on the set. O(1).
    598
         */
    599
        function length(StringSet storage set) internal view returns (uint256) {
    600
            return set._values.length;
    601
        }
    602
    
                                                    
                                                
    603
        /**
    604
         * @dev Returns the value stored at position `index` in the set. O(1).
    605
         *
    606
         * Note that there are no guarantees on the ordering of values inside the
    607
         * array, and it may change when more values are added or removed.
    608
         *
    609
         * Requirements:
    610
         *
    611
         * - `index` must be strictly less than {length}.
    612
         */
    613
        function at(StringSet storage set, uint256 index) internal view returns (string memory) {
    614
            return set._values[index];
    615
        }
    616
    
                                                    
                                                
    617
        /**
    618
         * @dev Return the entire set in an array
    619
         *
    620
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    621
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    622
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    623
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    624
         */
    625
        function values(StringSet storage set) internal view returns (string[] memory) {
    626
            return set._values;
    627
        }
    628
    
                                                    
                                                
    629
        /**
    630
         * @dev Return a slice of the set in an array
    631
         *
    632
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    633
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    634
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    635
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    636
         */
    637
        function values(StringSet storage set, uint256 start, uint256 end) internal view returns (string[] memory) {
    638
            unchecked {
    639
                end = Math.min(end, length(set));
    640
                start = Math.min(start, end);
    641
    
                                                    
                                                
    642
                uint256 len = end - start;
    643
                string[] memory result = new string[](len);
    644
                for (uint256 i = 0; i < len; ++i) {
    645
                    result[i] = Arrays.unsafeAccess(set._values, start + i).value;
    646
                }
    647
                return result;
    648
            }
    649
        }
    650
    
                                                    
                                                
    651
        struct BytesSet {
    652
            // Storage of set values
    653
            bytes[] _values;
    654
            // Position is the index of the value in the `values` array plus 1.
    655
            // Position 0 is used to mean a value is not in the set.
    656
            mapping(bytes value => uint256) _positions;
    657
        }
    658
    
                                                    
                                                
    659
        /**
    660
         * @dev Add a value to a set. O(1).
    661
         *
    662
         * Returns true if the value was added to the set, that is if it was not
    663
         * already present.
    664
         */
    665
        function add(BytesSet storage set, bytes memory value) internal returns (bool) {
    666
            if (!contains(set, value)) {
    667
                set._values.push(value);
    668
                // The value is stored at length-1, but we add 1 to all indexes
    669
                // and use 0 as a sentinel value
    670
                set._positions[value] = set._values.length;
    671
                return true;
    672
            } else {
    673
                return false;
    674
            }
    675
        }
    676
    
                                                    
                                                
    677
        /**
    678
         * @dev Removes a value from a set. O(1).
    679
         *
    680
         * Returns true if the value was removed from the set, that is if it was
    681
         * present.
    682
         */
    683
        function remove(BytesSet storage set, bytes memory value) internal returns (bool) {
    684
            // We cache the value's position to prevent multiple reads from the same storage slot
    685
            uint256 position = set._positions[value];
    686
    
                                                    
                                                
    687
            if (position != 0) {
    688
                // Equivalent to contains(set, value)
    689
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
    690
                // the array, and then remove the last element (sometimes called as 'swap and pop').
    691
                // This modifies the order of the array, as noted in {at}.
    692
    
                                                    
                                                
    693
                uint256 valueIndex = position - 1;
    694
                uint256 lastIndex = set._values.length - 1;
    695
    
                                                    
                                                
    696
                if (valueIndex != lastIndex) {
    697
                    bytes memory lastValue = set._values[lastIndex];
    698
    
                                                    
                                                
    699
                    // Move the lastValue to the index where the value to delete is
    700
                    set._values[valueIndex] = lastValue;
    701
                    // Update the tracked position of the lastValue (that was just moved)
    702
                    set._positions[lastValue] = position;
    703
                }
    704
    
                                                    
                                                
    705
                // Delete the slot where the moved value was stored
    706
                set._values.pop();
    707
    
                                                    
                                                
    708
                // Delete the tracked position for the deleted slot
    709
                delete set._positions[value];
    710
    
                                                    
                                                
    711
                return true;
    712
            } else {
    713
                return false;
    714
            }
    715
        }
    716
    
                                                    
                                                
    717
        /**
    718
         * @dev Removes all the values from a set. O(n).
    719
         *
    720
         * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
    721
         * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
    722
         */
    723
        function clear(BytesSet storage set) internal {
    724
            uint256 len = length(set);
    725
            for (uint256 i = 0; i < len; ++i) {
    726
                delete set._positions[set._values[i]];
    727
            }
    728
            Arrays.unsafeSetLength(set._values, 0);
    729
        }
    730
    
                                                    
                                                
    731
        /**
    732
         * @dev Returns true if the value is in the set. O(1).
    733
         */
    734
        function contains(BytesSet storage set, bytes memory value) internal view returns (bool) {
    735
            return set._positions[value] != 0;
    736
        }
    737
    
                                                    
                                                
    738
        /**
    739
         * @dev Returns the number of values on the set. O(1).
    740
         */
    741
        function length(BytesSet storage set) internal view returns (uint256) {
    742
            return set._values.length;
    743
        }
    744
    
                                                    
                                                
    745
        /**
    746
         * @dev Returns the value stored at position `index` in the set. O(1).
    747
         *
    748
         * Note that there are no guarantees on the ordering of values inside the
    749
         * array, and it may change when more values are added or removed.
    750
         *
    751
         * Requirements:
    752
         *
    753
         * - `index` must be strictly less than {length}.
    754
         */
    755
        function at(BytesSet storage set, uint256 index) internal view returns (bytes memory) {
    756
            return set._values[index];
    757
        }
    758
    
                                                    
                                                
    759
        /**
    760
         * @dev Return the entire set in an array
    761
         *
    762
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    763
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    764
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    765
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    766
         */
    767
        function values(BytesSet storage set) internal view returns (bytes[] memory) {
    768
            return set._values;
    769
        }
    770
    
                                                    
                                                
    771
        /**
    772
         * @dev Return a slice of the set in an array
    773
         *
    774
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
    775
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
    776
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
    777
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
    778
         */
    779
        function values(BytesSet storage set, uint256 start, uint256 end) internal view returns (bytes[] memory) {
    780
            unchecked {
    781
                end = Math.min(end, length(set));
    782
                start = Math.min(start, end);
    783
    
                                                    
                                                
    784
                uint256 len = end - start;
    785
                bytes[] memory result = new bytes[](len);
    786
                for (uint256 i = 0; i < len; ++i) {
    787
                    result[i] = Arrays.unsafeAccess(set._values, start + i).value;
    788
                }
    789
                return result;
    790
            }
    791
        }
    792
    }
    793
    
                                                    
                                                
    86.9% lib/v2-core/src/hooks/BaseHook.sol
    Lines covered: 73 / 84 (86.9%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // External
    5
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    6
    
                                                    
                                                
    7
    // Superform
    8
    import { HookDataDecoder } from "../libraries/HookDataDecoder.sol";
    9
    import { ISuperHook, ISuperHookSetter, ISuperHookResult, ISuperHookInspector } from "../interfaces/ISuperHook.sol";
    10
    
                                                    
                                                
    11
    /// @title BaseHook
    12
    /// @author Superform Labs
    13
    /// @notice Base implementation for all hooks in the Superform system
    14
    /// @dev Provides core security validation and execution flow management for hooks
    15
    ///      All specialized hooks should inherit from this base contract
    16
    ///      Implements the ISuperHook interface defined lifecycle methods
    17
    ///      Uses a transient storage pattern for stateful execution context
    18
    abstract contract BaseHook is ISuperHook, ISuperHookSetter, ISuperHookResult, ISuperHookInspector {
    19
        using HookDataDecoder for bytes;
    20
    
                                                    
                                                
    21
        /*//////////////////////////////////////////////////////////////
    22
                                     STORAGE
    23
        //////////////////////////////////////////////////////////////*/
    24
    
                                                    
                                                
    25
        /// @notice The number of shares used by this hook's operation
    26
        /// @dev Used for accounting and tracking consumption of position shares
    27
        uint256 public transient usedShares;
    28
    
                                                    
                                                
    29
        /// @notice The special token address (if any) associated with this hook's operation
    30
        /// @dev May be used to track token addresses for various operations
    31
        address public transient spToken;
    32
    
                                                    
                                                
    33
        /// @notice The primary asset address this hook operates on
    34
        /// @dev Typically the base token or asset being processed
    35
        address public transient asset;
    36
    
                                                    
                                                
    37
        /// @notice Execution nonce for creating unique contexts
    38
    ✓ 2.9M
        uint256 public transient executionNonce;
    39
    
                                                    
                                                
    40
        /// @notice Last execution context caller
    41
        address public transient lastCaller;
    42
    
                                                    
                                                
    43
        // Storage offsets for different state variables
    44
    ✓ 779.7K
        uint256 private constant OUT_AMOUNT_OFFSET = 1;
    45
    ✓ 779.7K
        uint256 private constant PRE_EXECUTE_MUTEX_OFFSET = 2;
    46
    ✓ 761.5K
        uint256 private constant POST_EXECUTE_MUTEX_OFFSET = 3;
    47
    
                                                    
                                                
    48
        /// @notice Base storage key for hook execution state
    49
    ✓ 4.2M
        bytes32 private constant HOOK_EXECUTION_STORAGE = keccak256("hook.execution.state");
    50
    
                                                    
                                                
    51
        /// @notice Storage key for account context mapping
    52
    ✓ 2.8M
        bytes32 private constant ACCOUNT_CONTEXT_STORAGE = keccak256("hook.account.context");
    53
    
                                                    
                                                
    54
        /// @notice The specific subtype identifier for this hook
    55
        /// @dev Used to identify specialized hook types beyond the basic HookType enum
    56
        bytes32 public immutable SUB_TYPE;
    57
    
                                                    
                                                
    58
        /// @notice The type of hook (NONACCOUNTING, INFLOW, OUTFLOW)
    59
        /// @dev Determines how the hook impacts accounting in the system
    60
    ✓ 522.0K
        ISuperHook.HookType public hookType;
    61
    
                                                    
                                                
    62
        /*//////////////////////////////////////////////////////////////
    63
                                     ERRORS
    64
        //////////////////////////////////////////////////////////////*/
    65
        /// @notice Thrown when a caller attempts to execute hook methods without proper authorization
    66
        /// @dev Used by security validation to prevent unauthorized hook execution
    67
        error NOT_AUTHORIZED();
    68
    
                                                    
                                                
    69
        /// @notice Thrown when an amount parameter is invalid (e.g., zero or overflow)
    70
        /// @dev Used in validation checks for asset amounts and share values
    71
        error AMOUNT_NOT_VALID();
    72
    
                                                    
                                                
    73
        /// @notice Thrown when an address parameter is invalid (e.g., zero address)
    74
        /// @dev Used in validation checks for tokens, accounts, and other addresses
    75
        error ADDRESS_NOT_VALID();
    76
    
                                                    
                                                
    77
        /// @notice Thrown when a caller is not authorized to execute hook methods
    78
        /// @dev Used by security validation to prevent unauthorized hook execution
    79
        error UNAUTHORIZED_CALLER();
    80
    
                                                    
                                                
    81
        /// @notice Thrown when preExecute is called more than once
    82
        /// @dev Used to prevent reentrancy attacks and ensure proper execution flow
    83
        error PRE_EXECUTE_ALREADY_CALLED();
    84
    
                                                    
                                                
    85
        /// @notice Thrown when postExecute is called more than once
    86
        /// @dev Used to prevent reentrancy attacks and ensure proper execution flow
    87
        error POST_EXECUTE_ALREADY_CALLED();
    88
    
                                                    
                                                
    89
        /// @notice Thrown when a hook execution is incomplete
    90
        /// @dev Used to prevent incomplete hook execution
    91
        error INCOMPLETE_HOOK_EXECUTION();
    92
    
                                                    
                                                
    93
        /// @notice Thrown when trying to set outAmount after preExecute or postExecute
    94
        /// @dev Used to prevent setting outAmount after preExecute or postExecute
    95
        error CANNOT_SET_OUT_AMOUNT();
    96
    
                                                    
                                                
    97
        /// @notice Initializes the hook with its type and subtype
    98
        /// @dev Sets immutable parameters that define the hook's behavior
    99
        /// @param hookType_ The type classification for this hook (NONACCOUNTING, INFLOW, OUTFLOW)
    100
        /// @param subType_ The specific subtype identifier for specialized hook functionality
    101
        constructor(ISuperHook.HookType hookType_, bytes32 subType_) {
    102
    ✓ 2
            hookType = hookType_;
    103
    ✓ 2
            SUB_TYPE = subType_;
    104
        }
    105
    
                                                    
                                                
    106
        modifier onlyLastCaller() {
    107
    ✓ 380.8K
            if (msg.sender != lastCaller) revert UNAUTHORIZED_CALLER();
    108
            _;
    109
        }
    110
    
                                                    
                                                
    111
        /*//////////////////////////////////////////////////////////////
    112
                              EXECUTION SECURITY
    113
        //////////////////////////////////////////////////////////////*/
    114
        /// @inheritdoc ISuperHook
    115
        function setExecutionContext(address caller) external {
    116
    ✓ 456.2K
            _createExecutionContext(caller);
    117
    ✓ 456.2K
            lastCaller = msg.sender;
    118
        }
    119
    
                                                    
                                                
    120
        /// @dev Standard build pattern - MUST include preExecute first, postExecute last
    121
        /// @inheritdoc ISuperHook
    122
        function build(
    123
            address prevHook,
    124
            address account,
    125
            bytes calldata hookData
    126
        )
    127
            external
    128
            view
    129
            virtual
    130
    ✓ 456.2K
            returns (Execution[] memory executions)
    131
        {
    132
            // Get hook-specific executions
    133
    ✓ 456.2K
            Execution[] memory hookExecutions = _buildHookExecutions(prevHook, account, hookData);
    134
    
                                                    
                                                
    135
            // Always include pre + hook + post
    136
    ✓ 398.9K
            executions = new Execution[](hookExecutions.length + 2);
    137
    
                                                    
                                                
    138
            // FIRST: preExecute
    139
    ✓ 398.9K
            executions[0] = Execution({
    140
    ✓ 398.9K
                target: address(this),
    141
    ✓ 398.9K
                value: 0,
    142
    ✓ 398.9K
                callData: abi.encodeCall(this.preExecute, (prevHook, account, hookData))
    143
            });
    144
    
                                                    
                                                
    145
            // MIDDLE: hook-specific operations
    146
    ✓ 797.8K
            for (uint256 i = 0; i < hookExecutions.length; i++) {
    147
    ✓ 398.9K
                executions[i + 1] = hookExecutions[i];
    148
            }
    149
    
                                                    
                                                
    150
            // LAST: postExecute
    151
    ✓ 398.9K
            executions[executions.length - 1] = Execution({
    152
    ✓ 398.9K
                target: address(this),
    153
    ✓ 398.9K
                value: 0,
    154
    ✓ 398.9K
                callData: abi.encodeCall(this.postExecute, (prevHook, account, hookData))
    155
            });
    156
        }
    157
    
                                                    
                                                
    158
        /// @inheritdoc ISuperHook
    159
        function preExecute(address prevHook, address account, bytes calldata data) external {
    160
    ✓ 398.9K
            if (msg.sender != account) revert UNAUTHORIZED_CALLER();
    161
    ✓ 398.9K
            uint256 context = _getCurrentExecutionContext(account);
    162
    ✓ 398.9K
            if (_getPreExecuteMutex(context)) revert PRE_EXECUTE_ALREADY_CALLED();
    163
    ✓ 398.9K
            _setPreExecuteMutex(context, true);
    164
    ✓ 398.9K
            _preExecute(prevHook, account, data);
    165
        }
    166
    
                                                    
                                                
    167
        /// @inheritdoc ISuperHook
    168
        function postExecute(address prevHook, address account, bytes calldata data) external {
    169
    ✓ 380.8K
            if (msg.sender != account) revert UNAUTHORIZED_CALLER();
    170
    ✓ 380.8K
            uint256 context = _getCurrentExecutionContext(account);
    171
    ✓ 380.8K
            if (_getPostExecuteMutex(context)) revert POST_EXECUTE_ALREADY_CALLED();
    172
    ✓ 380.8K
            _setPostExecuteMutex(context, true);
    173
    ✓ 380.8K
            _postExecute(prevHook, account, data);
    174
        }
    175
    
                                                    
                                                
    176
        /// @inheritdoc ISuperHookSetter
    177
        function setOutAmount(uint256 _outAmount, address caller) external {
    178
            uint256 context = _getCurrentExecutionContext(caller);
    179
            if (_getPreExecuteMutex(context) || _getPostExecuteMutex(context)) {
    180
                revert CANNOT_SET_OUT_AMOUNT();
    181
            }
    182
    
                                                    
                                                
    183
            bytes32 key = _makeKey(context, OUT_AMOUNT_OFFSET);
    184
            assembly {
    185
    ✓ 779.7K
                tstore(key, _outAmount)
    186
            }
    187
        }
    188
    
                                                    
                                                
    189
    ✓ 380.8K
        function getOutAmount(address caller) public view returns (uint256) {
    190
    ✓ 380.8K
            return _getOutAmount(_getCurrentExecutionContext(caller));
    191
        }
    192
    
                                                    
                                                
    193
        /// @inheritdoc ISuperHook
    194
        function resetExecutionState(address caller) external onlyLastCaller {
    195
    ✓ 380.8K
            uint256 context = _getCurrentExecutionContext(caller);
    196
    ✓ 380.8K
            if (!_getPreExecuteMutex(context) || !_getPostExecuteMutex(context)) {
    197
                revert INCOMPLETE_HOOK_EXECUTION();
    198
            }
    199
    
                                                    
                                                
    200
    ✓ 380.8K
            _clearExecutionState(context);
    201
        }
    202
    
                                                    
                                                
    203
        /*//////////////////////////////////////////////////////////////
    204
                                     VIEW METHODS
    205
        //////////////////////////////////////////////////////////////*/
    206
        /// @inheritdoc ISuperHook
    207
        function subtype() external view returns (bytes32) {
    208
            return SUB_TYPE;
    209
        }
    210
    
                                                    
                                                
    211
        /// @inheritdoc ISuperHookInspector
    212
        function inspect(bytes calldata) external view virtual returns (bytes memory) { }
    213
    
                                                    
                                                
    214
        /*//////////////////////////////////////////////////////////////
    215
                                     INTERNAL METHODS
    216
        //////////////////////////////////////////////////////////////*/
    217
        /// @notice Internal implementation of build
    218
        /// @dev Abstract function to be implemented by derived hooks
    219
        ///      Called during build to generate hook-specific execution sequences
    220
        /// @param prevHook The previous hook in the chain, or address(0) if first hook
    221
        /// @param account The account that operations will be performed for
    222
        /// @param data Hook-specific parameters and configuration data
    223
        /// @return executions Array of execution objects containing target, value, and callData
    224
        function _buildHookExecutions(
    225
            address prevHook,
    226
            address account,
    227
            bytes calldata data
    228
        )
    229
            internal
    230
            view
    231
            virtual
    232
            returns (Execution[] memory executions);
    233
    
                                                    
                                                
    234
        /// @notice Internal implementation of preExecute
    235
        /// @dev Abstract function to be implemented by derived hooks
    236
        ///      Called before execution to validate inputs and prepare the hook's state
    237
        ///      Typically sets up the hook context by parsing parameters from data
    238
        ///      May check balances, permissions, or other preconditions
    239
        /// @param prevHook The previous hook in the chain, or address(0) if first hook
    240
        /// @param account The account that operations will be performed for
    241
        /// @param data Hook-specific parameters and configuration data
    242
        function _preExecute(address prevHook, address account, bytes calldata data) internal virtual { }
    243
    
                                                    
                                                
    244
        /// @notice Internal implementation of postExecute
    245
        /// @dev Abstract function to be implemented by derived hooks
    246
        ///      Called after execution to finalize the hook's state and set output values
    247
        ///      Typically calculates final results and sets outAmount, usedShares, etc.
    248
        ///      May perform additional validations on execution results
    249
        /// @param prevHook The previous hook in the chain, or address(0) if first hook
    250
        /// @param account The account operations were performed for
    251
        /// @param data Hook-specific parameters and configuration data
    252
        function _postExecute(address prevHook, address account, bytes calldata data) internal virtual { }
    253
    
                                                    
                                                
    254
        /// @notice Decodes a boolean value from a byte array at the specified offset
    255
        /// @dev Helper function for extracting boolean values from packed data
    256
        ///      Used when parsing hook-specific data parameters
    257
        /// @param data The byte array containing the encoded data
    258
        /// @param offset The position in the array to read from
    259
        /// @return The decoded boolean value (true if byte is non-zero)
    260
    ✓ 456.8K
        function _decodeBool(bytes memory data, uint256 offset) internal pure returns (bool) {
    261
    ✓ 456.8K
            return data[offset] != 0;
    262
        }
    263
    
                                                    
                                                
    264
        /// @notice Replaces an amount value within a byte array at the specified offset
    265
        /// @dev Used to modify hook calldata for amount adjustments without full re-encoding
    266
        ///      Particularly useful for modifying amounts in multi-hook execution chains
    267
        ///      Directly modifies the bytes in-place for gas efficiency
    268
        /// @param data The original byte array containing encoded calldata
    269
        /// @param amount The new amount value to insert
    270
        /// @param offset The position in the array where the amount starts
    271
        /// @return The modified byte array with the replaced amount
    272
        function _replaceCalldataAmount(
    273
            bytes memory data,
    274
            uint256 amount,
    275
            uint256 offset
    276
        )
    277
            internal
    278
            pure
    279
    ✓ 455.7K
            returns (bytes memory)
    280
        {
    281
    ✓ 455.7K
            bytes memory newAmountEncoded = abi.encodePacked(amount);
    282
    ✓ 15.0M
            for (uint256 i; i < 32; ++i) {
    283
    ✓ 14.6M
                data[offset + i] = newAmountEncoded[i];
    284
            }
    285
    ✓ 455.7K
            return data;
    286
        }
    287
    
                                                    
                                                
    288
    ✓ 2.8M
        function _makeAccountContextKey(address account) private pure returns (bytes32) {
    289
    ✓ 2.8M
            return keccak256(abi.encodePacked(ACCOUNT_CONTEXT_STORAGE, account));
    290
        }
    291
    
                                                    
                                                
    292
    ✓ 456.2K
        function _createExecutionContext(address caller) private returns (uint256) {
    293
            // Always increment nonce for new execution context
    294
    ✓ 456.2K
            executionNonce++;
    295
    ✓ 456.2K
            bytes32 key = _makeAccountContextKey(caller);
    296
    
                                                    
                                                
    297
            // Store this context for the current caller
    298
    ✓ 456.2K
            uint256 currentNonce = executionNonce; // Load into local variable for assembly
    299
            assembly {
    300
    ✓ 456.2K
                tstore(key, currentNonce)
    301
            }
    302
    
                                                    
                                                
    303
    ✓ 456.2K
            return executionNonce;
    304
        }
    305
    
                                                    
                                                
    306
    ✓ 2.3M
        function _getCurrentExecutionContext(address caller) private view returns (uint256 context) {
    307
    ✓ 2.3M
            bytes32 key = _makeAccountContextKey(caller);
    308
            assembly {
    309
    ✓ 4.2M
                context := tload(key)
    310
            }
    311
        }
    312
    
                                                    
                                                
    313
        function _makeKey(uint256 context, uint256 offset) private pure returns (bytes32) {
    314
    ✓ 4.2M
            return keccak256(abi.encodePacked(HOOK_EXECUTION_STORAGE, context, offset));
    315
        }
    316
    
                                                    
                                                
    317
    ✓ 380.8K
        function _getOutAmount(uint256 context) private view returns (uint256 value) {
    318
    ✓ 380.8K
            bytes32 key = _makeKey(context, OUT_AMOUNT_OFFSET);
    319
            assembly {
    320
                value := tload(key)
    321
            }
    322
        }
    323
    
                                                    
                                                
    324
        function _setOutAmount(uint256 value, address caller) internal {
    325
    ✓ 779.7K
            uint256 context = _getCurrentExecutionContext(caller);
    326
    ✓ 779.7K
            bytes32 key = _makeKey(context, OUT_AMOUNT_OFFSET);
    327
            assembly {
    328
                tstore(key, value)
    329
            }
    330
        }
    331
    
                                                    
                                                
    332
    ✓ 779.7K
        function _getPreExecuteMutex(uint256 context) private view returns (bool value) {
    333
    ✓ 779.7K
            bytes32 key = _makeKey(context, PRE_EXECUTE_MUTEX_OFFSET);
    334
            assembly {
    335
                value := tload(key)
    336
            }
    337
        }
    338
    
                                                    
                                                
    339
        function _setPreExecuteMutex(uint256 context, bool value) private {
    340
    ✓ 779.7K
            bytes32 key = _makeKey(context, PRE_EXECUTE_MUTEX_OFFSET);
    341
            assembly {
    342
                tstore(key, value)
    343
            }
    344
        }
    345
    
                                                    
                                                
    346
    ✓ 761.5K
        function _getPostExecuteMutex(uint256 context) private view returns (bool value) {
    347
    ✓ 761.5K
            bytes32 key = _makeKey(context, POST_EXECUTE_MUTEX_OFFSET);
    348
            assembly {
    349
                value := tload(key)
    350
            }
    351
        }
    352
    
                                                    
                                                
    353
        function _setPostExecuteMutex(uint256 context, bool value) private {
    354
    ✓ 761.5K
            bytes32 key = _makeKey(context, POST_EXECUTE_MUTEX_OFFSET);
    355
            assembly {
    356
    ✓ 1.5M
                tstore(key, value)
    357
            }
    358
        }
    359
    
                                                    
                                                
    360
        function _clearExecutionState(uint256 context) private {
    361
    ✓ 380.8K
            _setPreExecuteMutex(context, false);
    362
    ✓ 380.8K
            _setPostExecuteMutex(context, false);
    363
        }
    364
    }
    365
    
                                                    
                                                
    0.0% lib/v2-core/src/hooks/VaultBankLockableHook.sol
    Lines covered: 0 / 2 (0.0%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    import { HookDataDecoder } from "../libraries/HookDataDecoder.sol";
    5
    
                                                    
                                                
    6
    /// @title VaultBankLockableHook
    7
    /// @author Superform Labs
    8
    /// @notice Base implementation for all vault bank lockable hooks in the Superform system
    9
    abstract contract VaultBankLockableHook {
    10
        using HookDataDecoder for bytes;
    11
    
                                                    
                                                
    12
        /// @notice The vault bank address (if applicable) for cross-chain operations
    13
        /// @dev Used primarily in bridge hooks to track source/destination vault banks
    14
        address public transient vaultBank;
    15
    
                                                    
                                                
    16
        /// @notice The destination chain ID for cross-chain operations
    17
        /// @dev Used primarily in bridge hooks to track target chain
    18
        uint256 public transient dstChainId;
    19
    }
    20
    
                                                    
                                                
    94.3% lib/v2-core/src/hooks/vaults/4626/ApproveAndDeposit4626VaultHook.sol
    Lines covered: 33 / 35 (94.3%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    8
    import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import { BaseHook } from "../../BaseHook.sol";
    12
    import { VaultBankLockableHook } from "../../VaultBankLockableHook.sol";
    13
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    14
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    15
    import {
    16
        ISuperHookResult,
    17
        ISuperHookInflowOutflow,
    18
        ISuperHookContextAware,
    19
        ISuperHookInspector
    20
    } from "../../../interfaces/ISuperHook.sol";
    21
    
                                                    
                                                
    22
    /// @title ApproveAndDeposit4626VaultHook
    23
    /// @author Superform Labs
    24
    /// @notice This hook does not support tokens reverting on 0 approval
    25
    /// @dev data has the following structure
    26
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    27
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    28
    /// @notice         address token = BytesLib.toAddress(data, 52);
    29
    /// @notice         uint256 amount = BytesLib.toUint256(data, 72);
    30
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 104);
    31
    contract ApproveAndDeposit4626VaultHook is
    32
        BaseHook,
    33
        VaultBankLockableHook,
    34
        ISuperHookInflowOutflow,
    35
        ISuperHookContextAware
    36
    {
    37
        using HookDataDecoder for bytes;
    38
    
                                                    
                                                
    39
    ✓ 16.2K
        uint256 private constant AMOUNT_POSITION = 72;
    40
    ✓ 16.2K
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 104;
    41
    
                                                    
                                                
    42
    ✓ 1
        constructor() BaseHook(HookType.INFLOW, HookSubTypes.ERC4626) { }
    43
    
                                                    
                                                
    44
        /*//////////////////////////////////////////////////////////////
    45
                                     VIEW METHODS
    46
        //////////////////////////////////////////////////////////////*/
    47
        /// @inheritdoc BaseHook
    48
        function _buildHookExecutions(
    49
            address prevHook,
    50
            address account,
    51
            bytes calldata data
    52
        )
    53
            internal
    54
            view
    55
            override
    56
    ✓ 16.2K
            returns (Execution[] memory executions)
    57
        {
    58
    ✓ 16.2K
            address yieldSource = data.extractYieldSource();
    59
    ✓ 16.2K
            address token = BytesLib.toAddress(data, 52);
    60
    ✓ 16.2K
            uint256 amount = _decodeAmount(data);
    61
    ✓ 16.2K
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    62
    
                                                    
                                                
    63
    ✓ 16.2K
            if (usePrevHookAmount) {
    64
    ✓ 1.8K
                amount = ISuperHookResult(prevHook).getOutAmount(account);
    65
            }
    66
    
                                                    
                                                
    67
    ✓ 14.3K
    ⟲ 632
            if (amount == 0) revert AMOUNT_NOT_VALID();
    68
    ✓ 13.7K
            if (yieldSource == address(0) || token == address(0)) revert ADDRESS_NOT_VALID();
    69
    
                                                    
                                                
    70
    ✓ 13.7K
            executions = new Execution[](4);
    71
    ✓ 13.7K
            executions[0] =
    72
    ✓ 13.7K
                Execution({ target: token, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, 0)) });
    73
    ✓ 13.7K
            executions[1] =
    74
    ✓ 13.7K
                Execution({ target: token, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, amount)) });
    75
    ✓ 13.7K
            executions[2] =
    76
    ✓ 13.7K
                Execution({ target: yieldSource, value: 0, callData: abi.encodeCall(IERC4626.deposit, (amount, account)) });
    77
    ✓ 13.7K
            executions[3] =
    78
    ✓ 13.7K
                Execution({ target: token, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, 0)) });
    79
        }
    80
    
                                                    
                                                
    81
        /*//////////////////////////////////////////////////////////////
    82
                                     EXTERNAL METHODS
    83
        //////////////////////////////////////////////////////////////*/
    84
    
                                                    
                                                
    85
        /// @inheritdoc ISuperHookInflowOutflow
    86
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    87
            return _decodeAmount(data);
    88
        }
    89
    
                                                    
                                                
    90
        /// @inheritdoc ISuperHookContextAware
    91
    ✓ 16.2K
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    92
    ✓ 16.2K
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    93
        }
    94
    
                                                    
                                                
    95
        /// @inheritdoc ISuperHookInspector
    96
    ✓ 54.2K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    97
            return abi.encodePacked(
    98
    ✓ 54.2K
                data.extractYieldSource(),
    99
    ✓ 54.2K
                BytesLib.toAddress(data, 52) //token
    100
            );
    101
        }
    102
    
                                                    
                                                
    103
        /*//////////////////////////////////////////////////////////////
    104
                                     INTERNAL METHODS
    105
        //////////////////////////////////////////////////////////////*/
    106
        function _preExecute(address, address account, bytes calldata data) internal override {
    107
            // store current balance
    108
    ✓ 13.7K
            _setOutAmount(_getBalance(account, data), account);
    109
    ✓ 13.7K
            spToken = data.extractYieldSource();
    110
        }
    111
    
                                                    
                                                
    112
        function _postExecute(address, address account, bytes calldata data) internal override {
    113
    ✓ 27.4K
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    114
        }
    115
    
                                                    
                                                
    116
        /*//////////////////////////////////////////////////////////////
    117
                                     PRIVATE METHODS
    118
        //////////////////////////////////////////////////////////////*/
    119
    ✓ 16.2K
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    120
    ✓ 16.2K
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    121
        }
    122
    
                                                    
                                                
    123
    ✓ 27.4K
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    124
    ✓ 27.4K
            return IERC4626(data.extractYieldSource()).balanceOf(account);
    125
        }
    126
    }
    127
    
                                                    
                                                
    59.3% lib/v2-core/src/hooks/vaults/4626/Deposit4626VaultHook.sol
    Lines covered: 16 / 27 (59.3%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
    8
    
                                                    
                                                
    9
    // Superform
    10
    import { BaseHook } from "../../BaseHook.sol";
    11
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    12
    import { VaultBankLockableHook } from "../../VaultBankLockableHook.sol";
    13
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    14
    import {
    15
        ISuperHookResult,
    16
        ISuperHookInflowOutflow,
    17
        ISuperHookContextAware,
    18
        ISuperHookInspector
    19
    } from "../../../interfaces/ISuperHook.sol";
    20
    
                                                    
                                                
    21
    /// @title Deposit4626VaultHook
    22
    /// @author Superform Labs
    23
    /// @dev data has the following structure
    24
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    25
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    26
    /// @notice         uint256 amount = BytesLib.toUint256(data, 52);
    27
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 84);
    28
    contract Deposit4626VaultHook is BaseHook, VaultBankLockableHook, ISuperHookInflowOutflow, ISuperHookContextAware {
    29
        using HookDataDecoder for bytes;
    30
    
                                                    
                                                
    31
    ✓ 773
        uint256 private constant AMOUNT_POSITION = 52;
    32
    ✓ 773
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 84;
    33
    
                                                    
                                                
    34
    ✓ 1
        constructor() BaseHook(HookType.INFLOW, HookSubTypes.ERC4626) { }
    35
        /*//////////////////////////////////////////////////////////////
    36
                                     VIEW METHODS
    37
        //////////////////////////////////////////////////////////////*/
    38
        /// @inheritdoc BaseHook
    39
    
                                                    
                                                
    40
        function _buildHookExecutions(
    41
            address prevHook,
    42
            address account,
    43
            bytes calldata data
    44
        )
    45
            internal
    46
            view
    47
            override
    48
    ✓ 773
            returns (Execution[] memory executions)
    49
        {
    50
    ✓ 773
            address yieldSource = data.extractYieldSource();
    51
    ✓ 773
            uint256 amount = _decodeAmount(data);
    52
    ✓ 773
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    53
    
                                                    
                                                
    54
    ✓ 773
            if (usePrevHookAmount) {
    55
    ✓ 372
                amount = ISuperHookResult(prevHook).getOutAmount(account);
    56
            }
    57
    
                                                    
                                                
    58
    ✓ 401
    ⟲ 401
            if (amount == 0) revert AMOUNT_NOT_VALID();
    59
            if (yieldSource == address(0)) revert ADDRESS_NOT_VALID();
    60
    
                                                    
                                                
    61
            executions = new Execution[](1);
    62
            executions[0] =
    63
                Execution({ target: yieldSource, value: 0, callData: abi.encodeCall(IERC4626.deposit, (amount, account)) });
    64
        }
    65
    
                                                    
                                                
    66
        /*//////////////////////////////////////////////////////////////
    67
                                     EXTERNAL METHODS
    68
        //////////////////////////////////////////////////////////////*/
    69
    
                                                    
                                                
    70
        /// @inheritdoc ISuperHookInflowOutflow
    71
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    72
            return _decodeAmount(data);
    73
        }
    74
    
                                                    
                                                
    75
        /// @inheritdoc ISuperHookContextAware
    76
    ✓ 773
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    77
    ✓ 773
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    78
        }
    79
    
                                                    
                                                
    80
        /// @inheritdoc ISuperHookInspector
    81
    ✓ 5.0K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    82
    ✓ 5.0K
            return abi.encodePacked(data.extractYieldSource());
    83
        }
    84
    
                                                    
                                                
    85
        /*//////////////////////////////////////////////////////////////
    86
                                     INTERNAL METHODS
    87
        //////////////////////////////////////////////////////////////*/
    88
        function _preExecute(address, address account, bytes calldata data) internal override {
    89
            // store current balance
    90
            _setOutAmount(_getBalance(account, data), account);
    91
            spToken = data.extractYieldSource();
    92
        }
    93
    
                                                    
                                                
    94
        function _postExecute(address, address account, bytes calldata data) internal override {
    95
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    96
        }
    97
    
                                                    
                                                
    98
        /*//////////////////////////////////////////////////////////////
    99
                                     PRIVATE METHODS
    100
        //////////////////////////////////////////////////////////////*/
    101
    ✓ 773
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    102
    ✓ 773
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    103
        }
    104
    
                                                    
                                                
    105
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    106
            return IERC4626(data.extractYieldSource()).balanceOf(account);
    107
        }
    108
    }
    109
    
                                                    
                                                
    97.6% lib/v2-core/src/hooks/vaults/4626/Redeem4626VaultHook.sol
    Lines covered: 40 / 41 (97.6%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
    8
    import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import { BaseHook } from "../../BaseHook.sol";
    12
    import {
    13
        ISuperHookResultOutflow,
    14
        ISuperHookInflowOutflow,
    15
        ISuperHookOutflow,
    16
        ISuperHookContextAware,
    17
        ISuperHookInspector
    18
    } from "../../../interfaces/ISuperHook.sol";
    19
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    20
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    21
    
                                                    
                                                
    22
    /// @title Redeem4626VaultHook
    23
    /// @author Superform Labs
    24
    /// @dev data has the following structure
    25
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    26
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    27
    /// @notice         address owner = BytesLib.toAddress(data, 52);
    28
    /// @notice         uint256 shares = BytesLib.toUint256(data, 72);
    29
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 104);
    30
    contract Redeem4626VaultHook is BaseHook, ISuperHookInflowOutflow, ISuperHookOutflow, ISuperHookContextAware {
    31
        using HookDataDecoder for bytes;
    32
    
                                                    
                                                
    33
    ✓ 1.5M
        uint256 private constant AMOUNT_POSITION = 72;
    34
    ✓ 456.2K
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 104;
    35
    
                                                    
                                                
    36
    ✓ 1
        constructor() BaseHook(HookType.OUTFLOW, HookSubTypes.ERC4626) { }
    37
    
                                                    
                                                
    38
        /*//////////////////////////////////////////////////////////////
    39
                                     VIEW METHODS
    40
        //////////////////////////////////////////////////////////////*/
    41
        /// @inheritdoc BaseHook
    42
        function _buildHookExecutions(
    43
            address prevHook,
    44
            address account,
    45
            bytes calldata data
    46
        )
    47
            internal
    48
            view
    49
            override
    50
    ✓ 456.2K
            returns (Execution[] memory executions)
    51
        {
    52
    ✓ 456.2K
            address yieldSource = data.extractYieldSource();
    53
    ✓ 456.2K
            address owner = BytesLib.toAddress(data, 52);
    54
    ✓ 456.2K
            uint256 shares = _decodeAmount(data);
    55
    ✓ 456.2K
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    56
    
                                                    
                                                
    57
    ✓ 456.2K
            if (usePrevHookAmount) {
    58
    ✓ 544
                shares = ISuperHookResultOutflow(prevHook).getOutAmount(account);
    59
            }
    60
    
                                                    
                                                
    61
    ✓ 455.7K
    ⟲ 56.8K
            if (shares == 0) revert AMOUNT_NOT_VALID();
    62
    ✓ 398.9K
            if (yieldSource == address(0)) revert ADDRESS_NOT_VALID();
    63
    
                                                    
                                                
    64
    ✓ 398.9K
            executions = new Execution[](1);
    65
    ✓ 398.9K
            executions[0] = Execution({
    66
                target: yieldSource,
    67
                value: 0,
    68
    ✓ 398.9K
                callData: abi.encodeCall(IERC4626.redeem, (shares, account, owner))
    69
            });
    70
        }
    71
    
                                                    
                                                
    72
        /*//////////////////////////////////////////////////////////////
    73
                                     EXTERNAL METHODS
    74
        //////////////////////////////////////////////////////////////*/
    75
    
                                                    
                                                
    76
        /// @inheritdoc ISuperHookInflowOutflow
    77
    ✓ 1.0M
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    78
    ✓ 1.0M
            return _decodeAmount(data);
    79
        }
    80
    
                                                    
                                                
    81
        /// @inheritdoc ISuperHookContextAware
    82
    ✓ 544
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    83
    ✓ 544
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    84
        }
    85
    
                                                    
                                                
    86
        /// @inheritdoc ISuperHookOutflow
    87
    ✓ 455.7K
        function replaceCalldataAmount(bytes memory data, uint256 amount) external pure returns (bytes memory) {
    88
    ✓ 455.7K
            return _replaceCalldataAmount(data, amount, AMOUNT_POSITION);
    89
        }
    90
    
                                                    
                                                
    91
        /// @inheritdoc ISuperHookInspector
    92
    ✓ 545.9K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    93
            return abi.encodePacked(
    94
    ✓ 545.9K
                data.extractYieldSource(),
    95
    ✓ 545.9K
                BytesLib.toAddress(data, 52) // owner
    96
            );
    97
        }
    98
    
                                                    
                                                
    99
        /*//////////////////////////////////////////////////////////////
    100
                                     INTERNAL METHODS
    101
        //////////////////////////////////////////////////////////////*/
    102
        function _preExecute(address, address account, bytes calldata data) internal override {
    103
    ✓ 398.9K
            address yieldSource = data.extractYieldSource();
    104
    ✓ 398.9K
            asset = IERC4626(yieldSource).asset();
    105
    ✓ 398.9K
            _setOutAmount(_getBalance(account, data), account);
    106
    ✓ 398.9K
            usedShares = _getSharesBalance(account, data);
    107
    ✓ 398.9K
            spToken = yieldSource;
    108
        }
    109
    
                                                    
                                                
    110
        function _postExecute(address, address account, bytes calldata data) internal override {
    111
    ✓ 380.8K
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    112
    ✓ 380.8K
            usedShares = usedShares - _getSharesBalance(account, data);
    113
        }
    114
    
                                                    
                                                
    115
        /*//////////////////////////////////////////////////////////////
    116
                                     PRIVATE METHODS
    117
        //////////////////////////////////////////////////////////////*/
    118
    ✓ 1.5M
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    119
    ✓ 1.5M
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    120
        }
    121
    
                                                    
                                                
    122
    ✓ 779.7K
        function _getBalance(address account, bytes memory) private view returns (uint256) {
    123
    ✓ 779.7K
            return IERC20(asset).balanceOf(account);
    124
        }
    125
    
                                                    
                                                
    126
    ✓ 779.7K
        function _getSharesBalance(address account, bytes memory data) private view returns (uint256) {
    127
    ✓ 779.7K
            address yieldSource = data.extractYieldSource();
    128
    ✓ 779.7K
            address owner = BytesLib.toAddress(data, 52);
    129
    ✓ 779.7K
            if (owner == address(0)) {
    130
                owner = account;
    131
            }
    132
    ✓ 779.7K
            return IERC4626(yieldSource).balanceOf(owner);
    133
        }
    134
    }
    135
    
                                                    
                                                
    89.7% lib/v2-core/src/hooks/vaults/5115/ApproveAndDeposit5115VaultHook.sol
    Lines covered: 35 / 39 (89.7%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
    8
    import { IStandardizedYield } from "../../../vendor/pendle/IStandardizedYield.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import { BaseHook } from "../../BaseHook.sol";
    12
    import { VaultBankLockableHook } from "../../VaultBankLockableHook.sol";
    13
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    14
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    15
    import {
    16
        ISuperHookResult,
    17
        ISuperHookInflowOutflow,
    18
        ISuperHookContextAware,
    19
        ISuperHookInspector
    20
    } from "../../../interfaces/ISuperHook.sol";
    21
    
                                                    
                                                
    22
    /// @title ApproveAndDeposit5115VaultHook
    23
    /// @author Superform Labs
    24
    /// @notice This hook does not support tokens reverting on 0 approval
    25
    /// @dev data has the following structure
    26
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    27
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    28
    /// @notice         address tokenIn = BytesLib.toAddress(data, 52);
    29
    /// @notice         uint256 amount = BytesLib.toUint256(data, 72);
    30
    /// @notice         uint256 minSharesOut = BytesLib.toUint256(data, 104);
    31
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 136);
    32
    contract ApproveAndDeposit5115VaultHook is
    33
        BaseHook,
    34
        VaultBankLockableHook,
    35
        ISuperHookInflowOutflow,
    36
        ISuperHookContextAware
    37
    {
    38
        using HookDataDecoder for bytes;
    39
    
                                                    
                                                
    40
    ✓ 1.3K
        uint256 private constant AMOUNT_POSITION = 72;
    41
    ✓ 1.3K
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 136;
    42
    
                                                    
                                                
    43
    ✓ 1
        constructor() BaseHook(HookType.INFLOW, HookSubTypes.ERC5115) { }
    44
    
                                                    
                                                
    45
        /*//////////////////////////////////////////////////////////////
    46
                                     VIEW METHODS
    47
        //////////////////////////////////////////////////////////////*/
    48
        /// @inheritdoc BaseHook
    49
        function _buildHookExecutions(
    50
            address prevHook,
    51
            address account,
    52
            bytes calldata data
    53
        )
    54
            internal
    55
            view
    56
            override
    57
    ✓ 1.3K
            returns (Execution[] memory executions)
    58
        {
    59
    ✓ 1.3K
            address yieldSource = data.extractYieldSource();
    60
    ✓ 1.3K
            address tokenIn = BytesLib.toAddress(data, 52);
    61
    ✓ 1.3K
            uint256 amount = BytesLib.toUint256(data, AMOUNT_POSITION);
    62
    ✓ 1.3K
            uint256 minSharesOut = BytesLib.toUint256(data, 104);
    63
    ✓ 1.3K
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    64
    
                                                    
                                                
    65
    ✓ 1.3K
            if (usePrevHookAmount) {
    66
    ✓ 393
                amount = ISuperHookResult(prevHook).getOutAmount(account);
    67
            }
    68
    
                                                    
                                                
    69
    ✓ 872
    ⟲ 511
            if (amount == 0) revert AMOUNT_NOT_VALID();
    70
    ✓ 361
            if (yieldSource == address(0) || account == address(0) || tokenIn == address(0)) revert ADDRESS_NOT_VALID();
    71
    
                                                    
                                                
    72
    ✓ 361
            executions = new Execution[](4);
    73
    ✓ 361
            executions[0] =
    74
    ✓ 361
                Execution({ target: tokenIn, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, 0)) });
    75
    ✓ 361
            executions[1] =
    76
    ✓ 361
                Execution({ target: tokenIn, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, amount)) });
    77
    ✓ 361
            executions[2] = Execution({
    78
    ✓ 361
                target: yieldSource,
    79
    ✓ 361
                value: 0,
    80
    ✓ 361
                callData: abi.encodeCall(IStandardizedYield.deposit, (account, tokenIn, amount, minSharesOut))
    81
            });
    82
    ✓ 361
            executions[3] =
    83
    ✓ 361
                Execution({ target: tokenIn, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, 0)) });
    84
        }
    85
    
                                                    
                                                
    86
        /*//////////////////////////////////////////////////////////////
    87
                                     EXTERNAL METHODS
    88
        //////////////////////////////////////////////////////////////*/
    89
    
                                                    
                                                
    90
        /// @inheritdoc ISuperHookInflowOutflow
    91
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    92
            return _decodeAmount(data);
    93
        }
    94
    
                                                    
                                                
    95
        /// @inheritdoc ISuperHookContextAware
    96
    ✓ 1.3K
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    97
    ✓ 1.3K
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    98
        }
    99
    
                                                    
                                                
    100
        /// @inheritdoc ISuperHookInspector
    101
    ✓ 8.2K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    102
            return abi.encodePacked(
    103
    ✓ 8.2K
                data.extractYieldSource(),
    104
    ✓ 8.2K
                BytesLib.toAddress(data, 52) // tokenIn
    105
            );
    106
        }
    107
    
                                                    
                                                
    108
        /*//////////////////////////////////////////////////////////////
    109
                                     INTERNAL METHODS
    110
        //////////////////////////////////////////////////////////////*/
    111
        function _preExecute(address, address account, bytes calldata data) internal override {
    112
    ✓ 361
            _setOutAmount(_getBalance(account, data), account);
    113
    ✓ 361
            spToken = data.extractYieldSource();
    114
    ✓ 361
            asset = BytesLib.toAddress(data, 52);
    115
        }
    116
    
                                                    
                                                
    117
        function _postExecute(address, address account, bytes calldata data) internal override {
    118
    ✓ 361
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    119
        }
    120
    
                                                    
                                                
    121
        /*//////////////////////////////////////////////////////////////
    122
                                     PRIVATE METHODS
    123
        //////////////////////////////////////////////////////////////*/
    124
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    125
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    126
        }
    127
    
                                                    
                                                
    128
    ✓ 361
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    129
    ✓ 361
            return IStandardizedYield(data.extractYieldSource()).balanceOf(account);
    130
        }
    131
    }
    132
    
                                                    
                                                
    39.4% lib/v2-core/src/hooks/vaults/5115/Deposit5115VaultHook.sol
    Lines covered: 13 / 33 (39.4%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IStandardizedYield } from "../../../vendor/pendle/IStandardizedYield.sol";
    8
    
                                                    
                                                
    9
    // Superform
    10
    import { BaseHook } from "../../BaseHook.sol";
    11
    import { VaultBankLockableHook } from "../../VaultBankLockableHook.sol";
    12
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    13
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    14
    import {
    15
        ISuperHookResult,
    16
        ISuperHookInflowOutflow,
    17
        ISuperHookContextAware,
    18
        ISuperHookInspector
    19
    } from "../../../interfaces/ISuperHook.sol";
    20
    
                                                    
                                                
    21
    /// @title Deposit5115VaultHook
    22
    /// @author Superform Labs
    23
    /// @dev data has the following structure
    24
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    25
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    26
    /// @notice         address tokenIn = BytesLib.toAddress(data, 52);
    27
    /// @notice         uint256 amount = BytesLib.toUint256(data, 72);
    28
    /// @notice         uint256 minSharesOut = BytesLib.toUint256(data, 104);
    29
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 136);
    30
    contract Deposit5115VaultHook is BaseHook, VaultBankLockableHook, ISuperHookInflowOutflow, ISuperHookContextAware {
    31
        using HookDataDecoder for bytes;
    32
    
                                                    
                                                
    33
    ✓ 556
        uint256 private constant AMOUNT_POSITION = 72;
    34
    ✓ 556
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 136;
    35
    
                                                    
                                                
    36
    ✓ 1
        constructor() BaseHook(HookType.INFLOW, HookSubTypes.ERC5115) { }
    37
    
                                                    
                                                
    38
        /*//////////////////////////////////////////////////////////////
    39
                                     VIEW METHODS
    40
        //////////////////////////////////////////////////////////////*/
    41
        /// @inheritdoc BaseHook
    42
        function _buildHookExecutions(
    43
            address prevHook,
    44
            address account,
    45
            bytes calldata data
    46
        )
    47
            internal
    48
            view
    49
            override
    50
    ✓ 556
            returns (Execution[] memory executions)
    51
        {
    52
    ✓ 556
            address yieldSource = data.extractYieldSource();
    53
    ✓ 556
            address tokenIn = BytesLib.toAddress(data, 52);
    54
    ✓ 556
            uint256 amount = BytesLib.toUint256(data, AMOUNT_POSITION);
    55
    ✓ 556
            uint256 minSharesOut = BytesLib.toUint256(data, 104);
    56
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    57
    
                                                    
                                                
    58
            if (usePrevHookAmount) {
    59
                amount = ISuperHookResult(prevHook).getOutAmount(account);
    60
            }
    61
    
                                                    
                                                
    62
            if (amount == 0) revert AMOUNT_NOT_VALID();
    63
            if (yieldSource == address(0) || account == address(0)) revert ADDRESS_NOT_VALID();
    64
    
                                                    
                                                
    65
            executions = new Execution[](1);
    66
            executions[0] = Execution({
    67
                target: yieldSource,
    68
                value: tokenIn == address(0) ? amount : 0,
    69
                callData: abi.encodeCall(IStandardizedYield.deposit, (account, tokenIn, amount, minSharesOut))
    70
            });
    71
        }
    72
    
                                                    
                                                
    73
        /*//////////////////////////////////////////////////////////////
    74
                                     EXTERNAL METHODS
    75
        //////////////////////////////////////////////////////////////*/
    76
    
                                                    
                                                
    77
        /// @inheritdoc ISuperHookInflowOutflow
    78
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    79
            return _decodeAmount(data);
    80
        }
    81
    
                                                    
                                                
    82
        /// @inheritdoc ISuperHookContextAware
    83
    ✓ 556
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    84
    ✓ 556
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    85
        }
    86
    
                                                    
                                                
    87
        /// @inheritdoc ISuperHookInspector
    88
    ✓ 3.5K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    89
            return abi.encodePacked(
    90
    ✓ 3.5K
                data.extractYieldSource(),
    91
    ✓ 3.5K
                BytesLib.toAddress(data, 52) // tokenIn
    92
            );
    93
        }
    94
    
                                                    
                                                
    95
        /*//////////////////////////////////////////////////////////////
    96
                                     INTERNAL METHODS
    97
        //////////////////////////////////////////////////////////////*/
    98
        function _preExecute(address, address account, bytes calldata data) internal override {
    99
            _setOutAmount(_getBalance(account, data), account);
    100
            spToken = data.extractYieldSource();
    101
            asset = BytesLib.toAddress(data, 52);
    102
        }
    103
    
                                                    
                                                
    104
        function _postExecute(address, address account, bytes calldata data) internal override {
    105
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    106
        }
    107
    
                                                    
                                                
    108
        /*//////////////////////////////////////////////////////////////
    109
                                     PRIVATE METHODS
    110
        //////////////////////////////////////////////////////////////*/
    111
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    112
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    113
        }
    114
    
                                                    
                                                
    115
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    116
            return IStandardizedYield(data.extractYieldSource()).balanceOf(account);
    117
        }
    118
    }
    119
    
                                                    
                                                
    57.9% lib/v2-core/src/hooks/vaults/5115/Redeem5115VaultHook.sol
    Lines covered: 22 / 38 (57.9%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    7
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    8
    import { IStandardizedYield } from "../../../vendor/pendle/IStandardizedYield.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import { BaseHook } from "../../BaseHook.sol";
    12
    import {
    13
        ISuperHookResultOutflow,
    14
        ISuperHookInflowOutflow,
    15
        ISuperHookOutflow,
    16
        ISuperHookContextAware,
    17
        ISuperHookInspector
    18
    } from "../../../interfaces/ISuperHook.sol";
    19
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    20
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    21
    
                                                    
                                                
    22
    /// @title Redeem5115VaultHook
    23
    /// @author Superform Labs
    24
    /// @dev data has the following structure
    25
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    26
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    27
    /// @notice         address tokenOut = BytesLib.toAddress(data, 52);
    28
    /// @notice         uint256 shares = BytesLib.toUint256(data, 72);
    29
    /// @notice         uint256 minTokenOut = BytesLib.toUint256(data, 104);
    30
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 136);
    31
    contract Redeem5115VaultHook is BaseHook, ISuperHookInflowOutflow, ISuperHookOutflow, ISuperHookContextAware {
    32
        using HookDataDecoder for bytes;
    33
    
                                                    
                                                
    34
    ✓ 40.3K
        uint256 private constant AMOUNT_POSITION = 72;
    35
    ✓ 505
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 136;
    36
    
                                                    
                                                
    37
    ✓ 1
        constructor() BaseHook(HookType.OUTFLOW, HookSubTypes.ERC5115) { }
    38
    
                                                    
                                                
    39
        /*//////////////////////////////////////////////////////////////
    40
                                     VIEW METHODS
    41
        //////////////////////////////////////////////////////////////*/
    42
        /// @inheritdoc BaseHook
    43
        function _buildHookExecutions(
    44
            address prevHook,
    45
            address account,
    46
            bytes calldata data
    47
        )
    48
            internal
    49
            view
    50
            override
    51
    ✓ 4.5K
            returns (Execution[] memory executions)
    52
        {
    53
    ✓ 4.5K
            address yieldSource = data.extractYieldSource();
    54
    ✓ 4.5K
            address tokenOut = BytesLib.toAddress(data, 52);
    55
    ✓ 4.5K
            uint256 shares = _decodeAmount(data);
    56
    ✓ 4.5K
            uint256 minTokenOut = BytesLib.toUint256(data, 104);
    57
    ✓ 505
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    58
    
                                                    
                                                
    59
    ✓ 505
            if (usePrevHookAmount) {
    60
    ✓ 505
                shares = ISuperHookResultOutflow(prevHook).getOutAmount(account);
    61
            }
    62
    
                                                    
                                                
    63
            if (shares == 0) revert AMOUNT_NOT_VALID();
    64
            if (yieldSource == address(0) || tokenOut == address(0)) revert ADDRESS_NOT_VALID();
    65
    
                                                    
                                                
    66
            executions = new Execution[](1);
    67
            executions[0] = Execution({
    68
                target: yieldSource,
    69
                value: 0,
    70
                callData: abi.encodeCall(IStandardizedYield.redeem, (account, shares, tokenOut, minTokenOut, false))
    71
            });
    72
        }
    73
    
                                                    
                                                
    74
        /*//////////////////////////////////////////////////////////////
    75
                                     EXTERNAL METHODS
    76
        //////////////////////////////////////////////////////////////*/
    77
    
                                                    
                                                
    78
        /// @inheritdoc ISuperHookInflowOutflow
    79
    ✓ 35.8K
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    80
    ✓ 35.8K
            return _decodeAmount(data);
    81
        }
    82
    
                                                    
                                                
    83
        /// @inheritdoc ISuperHookContextAware
    84
    ✓ 505
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    85
    ✓ 505
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    86
        }
    87
    
                                                    
                                                
    88
        /// @inheritdoc ISuperHookOutflow
    89
    ✓ 4.0K
        function replaceCalldataAmount(bytes memory data, uint256 amount) external pure returns (bytes memory) {
    90
    ✓ 4.0K
            return _replaceCalldataAmount(data, amount, AMOUNT_POSITION);
    91
        }
    92
    
                                                    
                                                
    93
        /// @inheritdoc ISuperHookInspector
    94
    ✓ 34.0K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    95
            return abi.encodePacked(
    96
    ✓ 34.0K
                data.extractYieldSource(),
    97
    ✓ 34.0K
                BytesLib.toAddress(data, 52) // tokenOut
    98
            );
    99
        }
    100
    
                                                    
                                                
    101
        /*//////////////////////////////////////////////////////////////
    102
                                     INTERNAL METHODS
    103
        //////////////////////////////////////////////////////////////*/
    104
        function _preExecute(address, address account, bytes calldata data) internal override {
    105
            asset = BytesLib.toAddress(data, 52); // tokenOut from data
    106
            _setOutAmount(_getBalance(account, data), account);
    107
            usedShares = _getSharesBalance(account, data);
    108
            spToken = data.extractYieldSource();
    109
        }
    110
    
                                                    
                                                
    111
        function _postExecute(address, address account, bytes calldata data) internal override {
    112
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    113
            usedShares = usedShares - _getSharesBalance(account, data);
    114
        }
    115
    
                                                    
                                                
    116
        /*//////////////////////////////////////////////////////////////
    117
                                     PRIVATE METHODS
    118
        //////////////////////////////////////////////////////////////*/
    119
    ✓ 40.3K
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    120
    ✓ 40.3K
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    121
        }
    122
    
                                                    
                                                
    123
        function _getBalance(address account, bytes memory) private view returns (uint256) {
    124
            return IERC20(asset).balanceOf(account);
    125
        }
    126
    
                                                    
                                                
    127
        function _getSharesBalance(address account, bytes memory data) private view returns (uint256) {
    128
            address yieldSource = data.extractYieldSource();
    129
            return IStandardizedYield(yieldSource).balanceOf(account);
    130
        }
    131
    }
    132
    
                                                    
                                                
    47.2% lib/v2-core/src/hooks/vaults/7540/ApproveAndRequestDeposit7540VaultHook.sol
    Lines covered: 17 / 36 (47.2%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
    7
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    8
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import {
    12
        ISuperHookResult,
    13
        ISuperHookInflowOutflow,
    14
        ISuperHookContextAware,
    15
        ISuperHookInspector
    16
    } from "../../../interfaces/ISuperHook.sol";
    17
    import { BaseHook } from "../../BaseHook.sol";
    18
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    19
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    20
    
                                                    
                                                
    21
    /// @title ApproveAndRequestDeposit7540VaultHook
    22
    /// @author Superform Labs
    23
    /// @notice This hook does not support tokens reverting on 0 approval
    24
    /// @dev data has the following structure
    25
    /// @notice         bytes32 placeholder = bytes32(BytesLib.slice(data, 0, 32), 0);
    26
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    27
    /// @notice         address token = BytesLib.toAddress(data, 52);
    28
    /// @notice         uint256 amount = BytesLib.toUint256(data, 72);
    29
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 104);
    30
    contract ApproveAndRequestDeposit7540VaultHook is BaseHook, ISuperHookInflowOutflow, ISuperHookContextAware {
    31
        using HookDataDecoder for bytes;
    32
    
                                                    
                                                
    33
    ✓ 948
        uint256 private constant AMOUNT_POSITION = 72;
    34
    ✓ 948
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 104;
    35
    
                                                    
                                                
    36
    ✓ 1
        constructor() BaseHook(HookType.NONACCOUNTING, HookSubTypes.ERC7540) { }
    37
    
                                                    
                                                
    38
        /*//////////////////////////////////////////////////////////////
    39
                                     VIEW METHODS
    40
        //////////////////////////////////////////////////////////////*/
    41
        /// @inheritdoc BaseHook
    42
        function _buildHookExecutions(
    43
            address prevHook,
    44
            address account,
    45
            bytes calldata data
    46
        )
    47
            internal
    48
            view
    49
            override
    50
    ✓ 948
            returns (Execution[] memory executions)
    51
        {
    52
    ✓ 948
            address yieldSource = data.extractYieldSource();
    53
    ✓ 948
            address token = BytesLib.toAddress(data, 52);
    54
    ✓ 948
            uint256 amount = _decodeAmount(data);
    55
    ✓ 948
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    56
    
                                                    
                                                
    57
    ✓ 948
            if (usePrevHookAmount) {
    58
    ✓ 948
                amount = ISuperHookResult(prevHook).getOutAmount(account);
    59
            }
    60
    
                                                    
                                                
    61
            if (amount == 0) revert AMOUNT_NOT_VALID();
    62
            if (yieldSource == address(0) || account == address(0) || token == address(0)) revert ADDRESS_NOT_VALID();
    63
    
                                                    
                                                
    64
            executions = new Execution[](4);
    65
            executions[0] =
    66
                Execution({ target: token, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, 0)) });
    67
            executions[1] =
    68
                Execution({ target: token, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, amount)) });
    69
            executions[2] = Execution({
    70
                target: yieldSource,
    71
                value: 0,
    72
                callData: abi.encodeCall(IERC7540.requestDeposit, (amount, account, account))
    73
            });
    74
            executions[3] =
    75
                Execution({ target: token, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, 0)) });
    76
        }
    77
    
                                                    
                                                
    78
        /*//////////////////////////////////////////////////////////////
    79
                                     EXTERNAL METHODS
    80
        //////////////////////////////////////////////////////////////*/
    81
    
                                                    
                                                
    82
        /// @inheritdoc ISuperHookInflowOutflow
    83
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    84
            return _decodeAmount(data);
    85
        }
    86
    
                                                    
                                                
    87
        /// @inheritdoc ISuperHookContextAware
    88
    ✓ 948
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    89
    ✓ 948
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    90
        }
    91
    
                                                    
                                                
    92
        /// @inheritdoc ISuperHookInspector
    93
    ✓ 7.9K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    94
            return abi.encodePacked(
    95
    ✓ 7.9K
                data.extractYieldSource(),
    96
    ✓ 7.9K
                BytesLib.toAddress(data, 52) //token
    97
            );
    98
        }
    99
    
                                                    
                                                
    100
        /*//////////////////////////////////////////////////////////////    
    101
                                     INTERNAL METHODS
    102
        //////////////////////////////////////////////////////////////*/
    103
        function _preExecute(address, address account, bytes calldata data) internal override {
    104
            _setOutAmount(_getBalance(account, data), account);
    105
        }
    106
    
                                                    
                                                
    107
        function _postExecute(address, address account, bytes calldata data) internal override {
    108
            _setOutAmount(getOutAmount(account) - _getBalance(account, data), account);
    109
        }
    110
    
                                                    
                                                
    111
        /*//////////////////////////////////////////////////////////////
    112
                                     PRIVATE METHODS
    113
        //////////////////////////////////////////////////////////////*/
    114
    ✓ 948
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    115
    ✓ 948
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    116
        }
    117
    
                                                    
                                                
    118
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    119
            return IERC20(IERC7540(data.extractYieldSource()).asset()).balanceOf(account);
    120
        }
    121
    }
    122
    
                                                    
                                                
    100.0% lib/v2-core/src/hooks/vaults/7540/CancelDepositRequest7540Hook.sol
    Lines covered: 11 / 11 (100.0%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    6
    import { IERC7540CancelDeposit } from "../../../vendor/standards/ERC7540/IERC7540Vault.sol";
    7
    
                                                    
                                                
    8
    // Superform
    9
    import { BaseHook } from "../../BaseHook.sol";
    10
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    11
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    12
    import { ISuperHookInspector } from "../../../interfaces/ISuperHook.sol";
    13
    
                                                    
                                                
    14
    /// @title CancelDepositRequest7540Hook
    15
    /// @author Superform Labs
    16
    /// @dev data has the following structure
    17
    /// @notice         bytes32 placeholder = bytes32(BytesLib.slice(data, 0, 32), 0);
    18
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    19
    contract CancelDepositRequest7540Hook is BaseHook {
    20
        using HookDataDecoder for bytes;
    21
    
                                                    
                                                
    22
    ✓ 1
        constructor() BaseHook(HookType.NONACCOUNTING, HookSubTypes.CANCEL_DEPOSIT_REQUEST) { }
    23
    
                                                    
                                                
    24
        /*//////////////////////////////////////////////////////////////
    25
                                     VIEW METHODS
    26
        //////////////////////////////////////////////////////////////*/
    27
        /// @inheritdoc BaseHook
    28
        function _buildHookExecutions(
    29
            address,
    30
            address account,
    31
            bytes calldata data
    32
        )
    33
            internal
    34
            pure
    35
            override
    36
    ✓ 1.8K
            returns (Execution[] memory executions)
    37
        {
    38
    ✓ 1.8K
            address yieldSource = data.extractYieldSource();
    39
    
                                                    
                                                
    40
    ✓ 1.8K
            if (yieldSource == address(0)) revert ADDRESS_NOT_VALID();
    41
    
                                                    
                                                
    42
    ✓ 1.8K
            executions = new Execution[](1);
    43
    ✓ 1.8K
            executions[0] = Execution({
    44
    ✓ 1.8K
                target: yieldSource,
    45
    ✓ 1.8K
                value: 0,
    46
    ✓ 1.8K
                callData: abi.encodeCall(IERC7540CancelDeposit.cancelDepositRequest, (0, account))
    47
            });
    48
        }
    49
    
                                                    
                                                
    50
        /// @inheritdoc ISuperHookInspector
    51
    ✓ 4.9K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    52
    ✓ 4.9K
            return abi.encodePacked(data.extractYieldSource());
    53
        }
    54
    }
    55
    
                                                    
                                                
    100.0% lib/v2-core/src/hooks/vaults/7540/CancelRedeemRequest7540Hook.sol
    Lines covered: 11 / 11 (100.0%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    6
    import { IERC7540CancelRedeem } from "../../../vendor/standards/ERC7540/IERC7540Vault.sol";
    7
    
                                                    
                                                
    8
    // Superform
    9
    import { BaseHook } from "../../BaseHook.sol";
    10
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    11
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    12
    import { ISuperHookInspector } from "../../../interfaces/ISuperHook.sol";
    13
    
                                                    
                                                
    14
    /// @title CancelRedeemRequest7540Hook
    15
    /// @author Superform Labs
    16
    /// @dev data has the following structure
    17
    /// @notice         bytes32 placeholder = bytes32(BytesLib.slice(data, 0, 32), 0);
    18
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    19
    contract CancelRedeemRequest7540Hook is BaseHook {
    20
        using HookDataDecoder for bytes;
    21
    
                                                    
                                                
    22
    ✓ 1
        constructor() BaseHook(HookType.NONACCOUNTING, HookSubTypes.CANCEL_REDEEM_REQUEST) { }
    23
    
                                                    
                                                
    24
        /*//////////////////////////////////////////////////////////////
    25
                                     VIEW METHODS
    26
        //////////////////////////////////////////////////////////////*/
    27
        /// @inheritdoc BaseHook
    28
        function _buildHookExecutions(
    29
            address,
    30
            address account,
    31
            bytes calldata data
    32
        )
    33
            internal
    34
            pure
    35
            override
    36
    ✓ 581
            returns (Execution[] memory executions)
    37
        {
    38
    ✓ 581
            address yieldSource = data.extractYieldSource();
    39
    
                                                    
                                                
    40
    ✓ 581
            if (yieldSource == address(0)) revert ADDRESS_NOT_VALID();
    41
    
                                                    
                                                
    42
    ✓ 581
            executions = new Execution[](1);
    43
    ✓ 581
            executions[0] = Execution({
    44
    ✓ 581
                target: yieldSource,
    45
    ✓ 581
                value: 0,
    46
    ✓ 581
                callData: abi.encodeCall(IERC7540CancelRedeem.cancelRedeemRequest, (0, account))
    47
            });
    48
        }
    49
    
                                                    
                                                
    50
        /// @inheritdoc ISuperHookInspector
    51
    ✓ 3.4K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    52
    ✓ 3.4K
            return abi.encodePacked(data.extractYieldSource());
    53
        }
    54
    }
    55
    
                                                    
                                                
    95.5% lib/v2-core/src/hooks/vaults/7540/ClaimCancelDepositRequest7540Hook.sol
    Lines covered: 21 / 22 (95.5%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC7540CancelDeposit } from "../../../vendor/standards/ERC7540/IERC7540Vault.sol";
    8
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    9
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    10
    
                                                    
                                                
    11
    // Superform
    12
    import { BaseHook } from "../../BaseHook.sol";
    13
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    14
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    15
    import { ISuperHookAsyncCancelations, ISuperHookInspector } from "../../../interfaces/ISuperHook.sol";
    16
    
                                                    
                                                
    17
    /// @title ClaimCancelDepositRequest7540Hook
    18
    /// @author Superform Labs
    19
    /// @dev data has the following structure
    20
    /// @notice         bytes32 placeholder = bytes32(BytesLib.slice(data, 0, 32), 0);
    21
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    22
    /// @notice         address receiver = BytesLib.toAddress(data, 52);
    23
    contract ClaimCancelDepositRequest7540Hook is BaseHook, ISuperHookAsyncCancelations {
    24
        using HookDataDecoder for bytes;
    25
    
                                                    
                                                
    26
    ✓ 1
        constructor() BaseHook(HookType.NONACCOUNTING, HookSubTypes.CLAIM_CANCEL_DEPOSIT_REQUEST) { }
    27
    
                                                    
                                                
    28
        /*//////////////////////////////////////////////////////////////
    29
                                     VIEW METHODS
    30
        //////////////////////////////////////////////////////////////*/
    31
        /// @inheritdoc BaseHook
    32
        function _buildHookExecutions(
    33
            address,
    34
            address account,
    35
            bytes calldata data
    36
        )
    37
            internal
    38
            pure
    39
            override
    40
    ✓ 667
            returns (Execution[] memory executions)
    41
        {
    42
    ✓ 667
            address yieldSource = data.extractYieldSource();
    43
    ✓ 667
            address receiver = BytesLib.toAddress(data, 52);
    44
    
                                                    
                                                
    45
    ✓ 667
            if (yieldSource == address(0) || receiver == address(0)) revert ADDRESS_NOT_VALID();
    46
    
                                                    
                                                
    47
    ✓ 667
            executions = new Execution[](1);
    48
    ✓ 667
            executions[0] = Execution({
    49
    ✓ 667
                target: yieldSource,
    50
    ✓ 667
                value: 0,
    51
    ✓ 667
                callData: abi.encodeCall(IERC7540CancelDeposit.claimCancelDepositRequest, (0, receiver, account))
    52
            });
    53
        }
    54
    
                                                    
                                                
    55
        /*//////////////////////////////////////////////////////////////
    56
                                     EXTERNAL METHODS
    57
        //////////////////////////////////////////////////////////////*/
    58
    
                                                    
                                                
    59
        /// @inheritdoc ISuperHookAsyncCancelations
    60
        function isAsyncCancelHook() external pure returns (CancelationType) {
    61
            return CancelationType.INFLOW;
    62
        }
    63
    
                                                    
                                                
    64
        /// @inheritdoc ISuperHookInspector
    65
    ✓ 3.9K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    66
            return abi.encodePacked(
    67
    ✓ 3.9K
                data.extractYieldSource(),
    68
    ✓ 3.9K
                BytesLib.toAddress(data, 52) //receiver
    69
            );
    70
        }
    71
    
                                                    
                                                
    72
        /*//////////////////////////////////////////////////////////////
    73
                                     INTERNAL METHODS
    74
        //////////////////////////////////////////////////////////////*/
    75
        function _preExecute(address, address account, bytes calldata data) internal override {
    76
    ✓ 667
            address yieldSource = data.extractYieldSource();
    77
    ✓ 667
            address receiver = BytesLib.toAddress(data, 52);
    78
    ✓ 667
            asset = IERC7540(yieldSource).asset();
    79
            // store current balance
    80
    ✓ 515
            _setOutAmount(_getBalance(receiver, data), account);
    81
        }
    82
    
                                                    
                                                
    83
        function _postExecute(address, address account, bytes calldata data) internal override {
    84
    ✓ 154
            address receiver = BytesLib.toAddress(data, 52);
    85
    
                                                    
                                                
    86
    ✓ 154
            _setOutAmount(_getBalance(receiver, data) - getOutAmount(account), account);
    87
        }
    88
    
                                                    
                                                
    89
        /*//////////////////////////////////////////////////////////////
    90
                                     PRIVATE METHODS
    91
        //////////////////////////////////////////////////////////////*/
    92
    
                                                    
                                                
    93
    ✓ 669
        function _getBalance(address account, bytes memory) private view returns (uint256) {
    94
    ✓ 669
            return IERC20(asset).balanceOf(account);
    95
        }
    96
    }
    97
    
                                                    
                                                
    95.0% lib/v2-core/src/hooks/vaults/7540/ClaimCancelRedeemRequest7540Hook.sol
    Lines covered: 19 / 20 (95.0%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    7
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    8
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    9
    import { IERC7540CancelRedeem } from "../../../vendor/standards/ERC7540/IERC7540Vault.sol";
    10
    
                                                    
                                                
    11
    // Superform
    12
    import { BaseHook } from "../../BaseHook.sol";
    13
    import { VaultBankLockableHook } from "../../VaultBankLockableHook.sol";
    14
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    15
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    16
    import { ISuperHookAsyncCancelations, ISuperHookInspector } from "../../../interfaces/ISuperHook.sol";
    17
    
                                                    
                                                
    18
    /// @title ClaimCancelRedeemRequest7540Hook
    19
    /// @author Superform Labs
    20
    /// @dev data has the following structure
    21
    /// @notice         bytes32 placeholder = bytes32(BytesLib.slice(data, 0, 32), 0);
    22
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    23
    /// @notice         address receiver = BytesLib.toAddress(data, 52);
    24
    contract ClaimCancelRedeemRequest7540Hook is BaseHook, VaultBankLockableHook, ISuperHookAsyncCancelations {
    25
        using HookDataDecoder for bytes;
    26
    
                                                    
                                                
    27
    ✓ 1
        constructor() BaseHook(HookType.NONACCOUNTING, HookSubTypes.CLAIM_CANCEL_REDEEM_REQUEST) { }
    28
    
                                                    
                                                
    29
        /*//////////////////////////////////////////////////////////////
    30
                                     VIEW METHODS
    31
        //////////////////////////////////////////////////////////////*/
    32
        /// @inheritdoc BaseHook
    33
        function _buildHookExecutions(
    34
            address,
    35
            address account,
    36
            bytes calldata data
    37
        )
    38
            internal
    39
            pure
    40
            override
    41
    ✓ 2.0K
            returns (Execution[] memory executions)
    42
        {
    43
    ✓ 2.0K
            address yieldSource = data.extractYieldSource();
    44
    ✓ 2.0K
            address receiver = BytesLib.toAddress(data, 52);
    45
    
                                                    
                                                
    46
    ✓ 2.0K
            if (yieldSource == address(0) || receiver == address(0)) revert ADDRESS_NOT_VALID();
    47
    
                                                    
                                                
    48
    ✓ 2.0K
            executions = new Execution[](1);
    49
    ✓ 2.0K
            executions[0] = Execution({
    50
    ✓ 2.0K
                target: yieldSource,
    51
    ✓ 2.0K
                value: 0,
    52
    ✓ 2.0K
                callData: abi.encodeCall(IERC7540CancelRedeem.claimCancelRedeemRequest, (0, receiver, account))
    53
            });
    54
        }
    55
    
                                                    
                                                
    56
        /*//////////////////////////////////////////////////////////////
    57
                                     EXTERNAL METHODS
    58
        //////////////////////////////////////////////////////////////*/
    59
    
                                                    
                                                
    60
        /// @inheritdoc ISuperHookAsyncCancelations
    61
        function isAsyncCancelHook() external pure returns (CancelationType) {
    62
            return CancelationType.OUTFLOW;
    63
        }
    64
    
                                                    
                                                
    65
        /// @inheritdoc ISuperHookInspector
    66
    ✓ 5.1K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    67
            return abi.encodePacked(
    68
    ✓ 5.1K
                data.extractYieldSource(),
    69
    ✓ 5.1K
                BytesLib.toAddress(data, 52) //receiver
    70
            );
    71
        }
    72
    
                                                    
                                                
    73
        /*//////////////////////////////////////////////////////////////
    74
                                     INTERNAL METHODS
    75
        //////////////////////////////////////////////////////////////*/
    76
        function _preExecute(address, address account, bytes calldata data) internal override {
    77
    ✓ 2.0K
            _setOutAmount(_getBalance(account, data), account);
    78
    ✓ 131
            spToken = IERC7540(data.extractYieldSource()).share();
    79
        }
    80
    
                                                    
                                                
    81
        function _postExecute(address, address account, bytes calldata data) internal override {
    82
    ✓ 131
            address receiver = BytesLib.toAddress(data, 52);
    83
    ✓ 131
            _setOutAmount(_getBalance(receiver, data) - getOutAmount(account), account);
    84
        }
    85
    
                                                    
                                                
    86
        /*//////////////////////////////////////////////////////////////
    87
                                     PRIVATE METHODS
    88
        //////////////////////////////////////////////////////////////*/
    89
    
                                                    
                                                
    90
    ✓ 2.1K
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    91
    ✓ 2.1K
            return IERC20(IERC7540(data.extractYieldSource()).share()).balanceOf(account);
    92
        }
    93
    }
    94
    
                                                    
                                                
    55.6% lib/v2-core/src/hooks/vaults/7540/Deposit7540VaultHook.sol
    Lines covered: 15 / 27 (55.6%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    8
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import { BaseHook } from "../../BaseHook.sol";
    12
    import { VaultBankLockableHook } from "../../VaultBankLockableHook.sol";
    13
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    14
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    15
    import {
    16
        ISuperHookResult,
    17
        ISuperHookInflowOutflow,
    18
        ISuperHookContextAware,
    19
        ISuperHookInspector
    20
    } from "../../../interfaces/ISuperHook.sol";
    21
    
                                                    
                                                
    22
    /// @title Deposit7540VaultHook
    23
    /// @author Superform Labs
    24
    /// @dev data has the following structure
    25
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    26
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    27
    /// @notice         uint256 amount = BytesLib.toUint256(data, 52);
    28
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 84);
    29
    contract Deposit7540VaultHook is BaseHook, VaultBankLockableHook, ISuperHookInflowOutflow, ISuperHookContextAware {
    30
        using HookDataDecoder for bytes;
    31
    
                                                    
                                                
    32
    ✓ 504
        uint256 private constant AMOUNT_POSITION = 52;
    33
    ✓ 504
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 84;
    34
    
                                                    
                                                
    35
    ✓ 1
        constructor() BaseHook(HookType.INFLOW, HookSubTypes.ERC7540) { }
    36
    
                                                    
                                                
    37
        /*//////////////////////////////////////////////////////////////
    38
                                     VIEW METHODS
    39
        //////////////////////////////////////////////////////////////*/
    40
        /// @inheritdoc BaseHook
    41
        function _buildHookExecutions(
    42
            address prevHook,
    43
            address account,
    44
            bytes calldata data
    45
        )
    46
            internal
    47
            view
    48
            override
    49
    ✓ 504
            returns (Execution[] memory executions)
    50
        {
    51
    ✓ 504
            address yieldSource = data.extractYieldSource();
    52
    ✓ 504
            uint256 amount = _decodeAmount(data);
    53
    ✓ 504
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    54
    
                                                    
                                                
    55
    ✓ 504
            if (usePrevHookAmount) {
    56
    ✓ 504
                amount = ISuperHookResult(prevHook).getOutAmount(account);
    57
            }
    58
    
                                                    
                                                
    59
            if (amount == 0) revert AMOUNT_NOT_VALID();
    60
            if (yieldSource == address(0) || account == address(0)) revert ADDRESS_NOT_VALID();
    61
    
                                                    
                                                
    62
            executions = new Execution[](1);
    63
            executions[0] = Execution({
    64
                target: yieldSource,
    65
                value: 0,
    66
                callData: abi.encodeCall(IERC7540.deposit, (amount, account, account))
    67
            });
    68
        }
    69
    
                                                    
                                                
    70
        /*//////////////////////////////////////////////////////////////
    71
                                     EXTERNAL METHODS
    72
        //////////////////////////////////////////////////////////////*/
    73
    
                                                    
                                                
    74
        /// @inheritdoc ISuperHookInflowOutflow
    75
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    76
            return _decodeAmount(data);
    77
        }
    78
    
                                                    
                                                
    79
        /// @inheritdoc ISuperHookContextAware
    80
    ✓ 504
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    81
    ✓ 504
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    82
        }
    83
    
                                                    
                                                
    84
        /// @inheritdoc ISuperHookInspector
    85
    ✓ 3.2K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    86
    ✓ 3.2K
            return abi.encodePacked(data.extractYieldSource());
    87
        }
    88
    
                                                    
                                                
    89
        /*//////////////////////////////////////////////////////////////
    90
                                     INTERNAL METHODS
    91
        //////////////////////////////////////////////////////////////*/
    92
        function _preExecute(address, address account, bytes calldata data) internal override {
    93
            // store current balance
    94
            _setOutAmount(_getBalance(account, data), account);
    95
            spToken = IERC7540(data.extractYieldSource()).share();
    96
        }
    97
    
                                                    
                                                
    98
        function _postExecute(address, address account, bytes calldata data) internal override {
    99
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    100
        }
    101
    
                                                    
                                                
    102
        /*//////////////////////////////////////////////////////////////
    103
                                     PRIVATE METHODS
    104
        //////////////////////////////////////////////////////////////*/
    105
    ✓ 504
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    106
    ✓ 504
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    107
        }
    108
    
                                                    
                                                
    109
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    110
            return IERC20(IERC7540(data.extractYieldSource()).share()).balanceOf(account);
    111
        }
    112
    }
    113
    
                                                    
                                                
    100.0% lib/v2-core/src/hooks/vaults/7540/Redeem7540VaultHook.sol
    Lines covered: 36 / 36 (100.0%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
    8
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import { BaseHook } from "../../BaseHook.sol";
    12
    import {
    13
        ISuperHookResultOutflow,
    14
        ISuperHookInflowOutflow,
    15
        ISuperHookOutflow,
    16
        ISuperHookContextAware,
    17
        ISuperHookInspector
    18
    } from "../../../interfaces/ISuperHook.sol";
    19
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    20
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    21
    
                                                    
                                                
    22
    /// @title Redeem7540VaultHook
    23
    /// @author Superform Labs
    24
    /// @notice This hook does not support tokens reverting on 0 approval
    25
    /// @dev data has the following structure
    26
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    27
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    28
    /// @notice         uint256 shares = BytesLib.toUint256(data, 52);
    29
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 84);
    30
    contract Redeem7540VaultHook is BaseHook, ISuperHookInflowOutflow, ISuperHookOutflow, ISuperHookContextAware {
    31
        using HookDataDecoder for bytes;
    32
    
                                                    
                                                
    33
    ✓ 43.0K
        uint256 private constant AMOUNT_POSITION = 52;
    34
    ✓ 6.2K
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 84;
    35
    
                                                    
                                                
    36
    ✓ 1
        constructor() BaseHook(HookType.OUTFLOW, HookSubTypes.ERC7540) { }
    37
    
                                                    
                                                
    38
        /*//////////////////////////////////////////////////////////////
    39
                                     VIEW METHODS
    40
        //////////////////////////////////////////////////////////////*/
    41
        /// @inheritdoc BaseHook
    42
        function _buildHookExecutions(
    43
            address prevHook,
    44
            address account,
    45
            bytes calldata data
    46
        )
    47
            internal
    48
            view
    49
            override
    50
    ✓ 6.2K
            returns (Execution[] memory executions)
    51
        {
    52
    ✓ 6.2K
            address yieldSource = data.extractYieldSource();
    53
    ✓ 6.2K
            uint256 shares = _decodeAmount(data);
    54
    ✓ 6.2K
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    55
    
                                                    
                                                
    56
    ✓ 6.2K
            if (usePrevHookAmount) {
    57
    ✓ 571
                shares = ISuperHookResultOutflow(prevHook).getOutAmount(account);
    58
            }
    59
    
                                                    
                                                
    60
    ✓ 5.6K
    ⟲ 1.7K
            if (shares == 0) revert AMOUNT_NOT_VALID();
    61
    ✓ 3.9K
            if (yieldSource == address(0) || account == address(0)) revert ADDRESS_NOT_VALID();
    62
    
                                                    
                                                
    63
    ✓ 3.9K
            executions = new Execution[](1);
    64
    ✓ 3.9K
            executions[0] = Execution({
    65
                target: yieldSource,
    66
                value: 0,
    67
    ✓ 3.9K
                callData: abi.encodeCall(IERC7540.redeem, (shares, account, account))
    68
            });
    69
        }
    70
    
                                                    
                                                
    71
        /*//////////////////////////////////////////////////////////////
    72
                                     EXTERNAL METHODS
    73
        //////////////////////////////////////////////////////////////*/
    74
        /// @inheritdoc ISuperHookInflowOutflow
    75
    ✓ 36.8K
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    76
    ✓ 36.8K
            return _decodeAmount(data);
    77
        }
    78
    
                                                    
                                                
    79
        /// @inheritdoc ISuperHookContextAware
    80
    ✓ 571
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    81
    ✓ 571
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    82
        }
    83
    
                                                    
                                                
    84
        /// @inheritdoc ISuperHookOutflow
    85
    ✓ 5.6K
        function replaceCalldataAmount(bytes memory data, uint256 amount) external pure returns (bytes memory) {
    86
    ✓ 5.6K
            return _replaceCalldataAmount(data, amount, AMOUNT_POSITION);
    87
        }
    88
    
                                                    
                                                
    89
        /// @inheritdoc ISuperHookInspector
    90
    ✓ 34.0K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    91
    ✓ 34.0K
            return abi.encodePacked(data.extractYieldSource());
    92
        }
    93
    
                                                    
                                                
    94
        /*//////////////////////////////////////////////////////////////    
    95
                                     INTERNAL METHODS
    96
        //////////////////////////////////////////////////////////////*/
    97
        function _preExecute(address, address account, bytes calldata data) internal override {
    98
    ✓ 3.9K
            address yieldSource = data.extractYieldSource();
    99
    ✓ 3.9K
            asset = IERC7540(yieldSource).asset();
    100
    ✓ 3.9K
            _setOutAmount(_getBalance(account, data), account);
    101
    ✓ 3.9K
            usedShares = _getSharesBalance(account, data);
    102
    ✓ 3.9K
            spToken = IERC7540(yieldSource).share();
    103
        }
    104
    
                                                    
                                                
    105
        function _postExecute(address, address account, bytes calldata data) internal override {
    106
    ✓ 3.2K
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    107
    ✓ 3.2K
            usedShares = usedShares - _getSharesBalance(account, data);
    108
        }
    109
    
                                                    
                                                
    110
        /*//////////////////////////////////////////////////////////////
    111
                                     PRIVATE METHODS
    112
        //////////////////////////////////////////////////////////////*/
    113
    ✓ 43.0K
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    114
    ✓ 43.0K
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    115
        }
    116
    
                                                    
                                                
    117
    ✓ 7.0K
        function _getBalance(address account, bytes memory) private view returns (uint256) {
    118
    ✓ 7.0K
            return IERC20(asset).balanceOf(account);
    119
        }
    120
    
                                                    
                                                
    121
    ✓ 7.0K
        function _getSharesBalance(address account, bytes memory data) private view returns (uint256) {
    122
    ✓ 7.0K
            address yieldSource = data.extractYieldSource();
    123
    ✓ 7.0K
            return IERC7540(yieldSource).claimableRedeemRequest(0, account);
    124
        }
    125
    }
    126
    
                                                    
                                                
    55.6% lib/v2-core/src/hooks/vaults/7540/RequestDeposit7540VaultHook.sol
    Lines covered: 15 / 27 (55.6%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    8
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import { BaseHook } from "../../BaseHook.sol";
    12
    import {
    13
        ISuperHookResult,
    14
        ISuperHookInflowOutflow,
    15
        ISuperHookAsyncCancelations,
    16
        ISuperHookContextAware,
    17
        ISuperHookInspector
    18
    } from "../../../interfaces/ISuperHook.sol";
    19
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    20
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    21
    
                                                    
                                                
    22
    /// @title RequestDeposit7540VaultHook
    23
    /// @author Superform Labs
    24
    /// @dev data has the following structure
    25
    /// @notice         bytes32 placeholder = bytes32(BytesLib.slice(data, 0, 32), 0);
    26
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    27
    /// @notice         uint256 amount = BytesLib.toUint256(data, 52);
    28
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 84);
    29
    contract RequestDeposit7540VaultHook is
    30
        BaseHook,
    31
        ISuperHookInflowOutflow,
    32
        ISuperHookAsyncCancelations,
    33
        ISuperHookContextAware
    34
    {
    35
        using HookDataDecoder for bytes;
    36
    
                                                    
                                                
    37
    ✓ 547
        uint256 private constant AMOUNT_POSITION = 52;
    38
    ✓ 547
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 84;
    39
    
                                                    
                                                
    40
    ✓ 1
        constructor() BaseHook(HookType.NONACCOUNTING, HookSubTypes.ERC7540) { }
    41
    
                                                    
                                                
    42
        /*//////////////////////////////////////////////////////////////
    43
                                     VIEW METHODS
    44
        //////////////////////////////////////////////////////////////*/
    45
        /// @inheritdoc BaseHook
    46
        function _buildHookExecutions(
    47
            address prevHook,
    48
            address account,
    49
            bytes calldata data
    50
        )
    51
            internal
    52
            view
    53
            override
    54
    ✓ 547
            returns (Execution[] memory executions)
    55
        {
    56
    ✓ 547
            address yieldSource = data.extractYieldSource();
    57
    ✓ 547
            uint256 amount = _decodeAmount(data);
    58
    ✓ 547
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    59
    
                                                    
                                                
    60
    ✓ 547
            if (usePrevHookAmount) {
    61
    ✓ 547
                amount = ISuperHookResult(prevHook).getOutAmount(account);
    62
            }
    63
    
                                                    
                                                
    64
            if (amount == 0) revert AMOUNT_NOT_VALID();
    65
            if (yieldSource == address(0) || account == address(0)) revert ADDRESS_NOT_VALID();
    66
    
                                                    
                                                
    67
            executions = new Execution[](1);
    68
            executions[0] = Execution({
    69
                target: yieldSource,
    70
                value: 0,
    71
                callData: abi.encodeCall(IERC7540.requestDeposit, (amount, account, account))
    72
            });
    73
        }
    74
    
                                                    
                                                
    75
        /// @inheritdoc ISuperHookAsyncCancelations
    76
        function isAsyncCancelHook() external pure returns (CancelationType) {
    77
            return CancelationType.NONE;
    78
        }
    79
    
                                                    
                                                
    80
        /*//////////////////////////////////////////////////////////////
    81
                                     EXTERNAL METHODS
    82
        //////////////////////////////////////////////////////////////*/
    83
    
                                                    
                                                
    84
        /// @inheritdoc ISuperHookInflowOutflow
    85
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    86
            return _decodeAmount(data);
    87
        }
    88
    
                                                    
                                                
    89
        /// @inheritdoc ISuperHookContextAware
    90
    ✓ 547
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    91
    ✓ 547
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    92
        }
    93
    
                                                    
                                                
    94
        /// @inheritdoc ISuperHookInspector
    95
    ✓ 3.8K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    96
    ✓ 3.8K
            return abi.encodePacked(data.extractYieldSource());
    97
        }
    98
    
                                                    
                                                
    99
        /*//////////////////////////////////////////////////////////////
    100
                                     INTERNAL METHODS
    101
        //////////////////////////////////////////////////////////////*/
    102
        function _preExecute(address, address account, bytes calldata data) internal override {
    103
            _setOutAmount(_getBalance(account, data), account);
    104
        }
    105
    
                                                    
                                                
    106
        function _postExecute(address, address account, bytes calldata data) internal override {
    107
            _setOutAmount(getOutAmount(account) - _getBalance(account, data), account);
    108
        }
    109
    
                                                    
                                                
    110
        /*//////////////////////////////////////////////////////////////
    111
                                     PRIVATE METHODS
    112
        //////////////////////////////////////////////////////////////*/
    113
    ✓ 547
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    114
    ✓ 547
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    115
        }
    116
    
                                                    
                                                
    117
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    118
            return IERC20(IERC7540(data.extractYieldSource()).asset()).balanceOf(account);
    119
        }
    120
    }
    121
    
                                                    
                                                
    55.6% lib/v2-core/src/hooks/vaults/7540/RequestRedeem7540VaultHook.sol
    Lines covered: 15 / 27 (55.6%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    8
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    9
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    10
    
                                                    
                                                
    11
    // Superform
    12
    import { BaseHook } from "../../BaseHook.sol";
    13
    import {
    14
        ISuperHookResult,
    15
        ISuperHookInflowOutflow,
    16
        ISuperHookAsyncCancelations,
    17
        ISuperHookContextAware,
    18
        ISuperHookInspector
    19
    } from "../../../interfaces/ISuperHook.sol";
    20
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    21
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    22
    
                                                    
                                                
    23
    /// @title RequestRedeem7540VaultHook
    24
    /// @author Superform Labs
    25
    /// @dev data has the following structure
    26
    /// @notice         bytes32 placeholder = bytes32(BytesLib.slice(data, 0, 32), 0);
    27
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    28
    /// @notice         uint256 shares = BytesLib.toUint256(data, 52);
    29
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 84);
    30
    contract RequestRedeem7540VaultHook is
    31
        BaseHook,
    32
        ISuperHookInflowOutflow,
    33
        ISuperHookAsyncCancelations,
    34
        ISuperHookContextAware
    35
    {
    36
        using HookDataDecoder for bytes;
    37
    
                                                    
                                                
    38
    ✓ 500
        uint256 private constant AMOUNT_POSITION = 52;
    39
    ✓ 500
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 84;
    40
    
                                                    
                                                
    41
    ✓ 1
        constructor() BaseHook(HookType.NONACCOUNTING, HookSubTypes.ERC7540) { }
    42
    
                                                    
                                                
    43
        /*//////////////////////////////////////////////////////////////
    44
                                     VIEW METHODS
    45
        //////////////////////////////////////////////////////////////*/
    46
        /// @inheritdoc BaseHook
    47
        function _buildHookExecutions(
    48
            address prevHook,
    49
            address account,
    50
            bytes calldata data
    51
        )
    52
            internal
    53
            view
    54
            override
    55
    ✓ 500
            returns (Execution[] memory executions)
    56
        {
    57
    ✓ 500
            address yieldSource = data.extractYieldSource();
    58
    ✓ 500
            uint256 shares = _decodeAmount(data);
    59
    ✓ 500
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    60
    
                                                    
                                                
    61
    ✓ 500
            if (usePrevHookAmount) {
    62
    ✓ 500
                shares = ISuperHookResult(prevHook).getOutAmount(account);
    63
            }
    64
    
                                                    
                                                
    65
            if (shares == 0) revert AMOUNT_NOT_VALID();
    66
            if (yieldSource == address(0) || account == address(0)) revert ADDRESS_NOT_VALID();
    67
    
                                                    
                                                
    68
            executions = new Execution[](1);
    69
            executions[0] = Execution({
    70
                target: yieldSource,
    71
                value: 0,
    72
                callData: abi.encodeCall(IERC7540.requestRedeem, (shares, account, account))
    73
            });
    74
        }
    75
    
                                                    
                                                
    76
        /// @inheritdoc ISuperHookAsyncCancelations
    77
        function isAsyncCancelHook() external pure returns (CancelationType) {
    78
            return CancelationType.NONE;
    79
        }
    80
    
                                                    
                                                
    81
        /*//////////////////////////////////////////////////////////////
    82
                                     EXTERNAL METHODS
    83
        //////////////////////////////////////////////////////////////*/
    84
    
                                                    
                                                
    85
        /// @inheritdoc ISuperHookInflowOutflow
    86
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    87
            return _decodeAmount(data);
    88
        }
    89
    
                                                    
                                                
    90
        /// @inheritdoc ISuperHookContextAware
    91
    ✓ 500
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    92
    ✓ 500
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    93
        }
    94
    
                                                    
                                                
    95
        /// @inheritdoc ISuperHookInspector
    96
    ✓ 3.0K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    97
    ✓ 3.0K
            return abi.encodePacked(data.extractYieldSource());
    98
        }
    99
    
                                                    
                                                
    100
        /*//////////////////////////////////////////////////////////////
    101
                                     INTERNAL METHODS
    102
        //////////////////////////////////////////////////////////////*/
    103
    
                                                    
                                                
    104
        function _preExecute(address, address account, bytes calldata data) internal override {
    105
            _setOutAmount(_getBalance(account, data), account);
    106
        }
    107
    
                                                    
                                                
    108
        function _postExecute(address, address account, bytes calldata data) internal override {
    109
            _setOutAmount(getOutAmount(account) - _getBalance(account, data), account);
    110
        }
    111
    
                                                    
                                                
    112
        /*//////////////////////////////////////////////////////////////
    113
                                     PRIVATE METHODS
    114
        //////////////////////////////////////////////////////////////*/
    115
    
                                                    
                                                
    116
    ✓ 500
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    117
    ✓ 500
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    118
        }
    119
    
                                                    
                                                
    120
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    121
            return IERC20(IERC7540(data.extractYieldSource()).share()).balanceOf(account);
    122
        }
    123
    }
    124
    
                                                    
                                                
    41.7% lib/v2-core/src/hooks/vaults/7540/Withdraw7540VaultHook.sol
    Lines covered: 15 / 36 (41.7%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
    8
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import { BaseHook } from "../../BaseHook.sol";
    12
    import {
    13
        ISuperHookResultOutflow,
    14
        ISuperHookInflowOutflow,
    15
        ISuperHookOutflow,
    16
        ISuperHookContextAware,
    17
        ISuperHookInspector
    18
    } from "../../../interfaces/ISuperHook.sol";
    19
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    20
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    21
    
                                                    
                                                
    22
    /// @title Withdraw7540VaultHook
    23
    /// @author Superform Labs
    24
    /// @notice Compatible only with ERC-7540 vaults where `requestId` is non-fungible
    25
    /// @dev data has the following structure
    26
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    27
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    28
    /// @notice         uint256 amount = BytesLib.toUint256(data, 52);
    29
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 84);
    30
    contract Withdraw7540VaultHook is BaseHook, ISuperHookInflowOutflow, ISuperHookOutflow, ISuperHookContextAware {
    31
        using HookDataDecoder for bytes;
    32
    
                                                    
                                                
    33
    ✓ 2.4K
        uint256 private constant AMOUNT_POSITION = 52;
    34
    ✓ 2.4K
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 84;
    35
    
                                                    
                                                
    36
    ✓ 2
        constructor() BaseHook(HookType.OUTFLOW, HookSubTypes.ERC7540) { }
    37
    
                                                    
                                                
    38
        /*//////////////////////////////////////////////////////////////
    39
                                     VIEW METHODS
    40
        //////////////////////////////////////////////////////////////*/
    41
        /// @inheritdoc BaseHook
    42
        function _buildHookExecutions(
    43
            address prevHook,
    44
            address account,
    45
            bytes calldata data
    46
        )
    47
            internal
    48
            view
    49
            override
    50
    ✓ 2.4K
            returns (Execution[] memory executions)
    51
        {
    52
    ✓ 2.4K
            address yieldSource = data.extractYieldSource();
    53
    ✓ 2.4K
            uint256 amount = _decodeAmount(data);
    54
    ✓ 2.4K
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    55
    
                                                    
                                                
    56
    ✓ 2.4K
            if (usePrevHookAmount) {
    57
    ✓ 2.4K
                amount = ISuperHookResultOutflow(prevHook).getOutAmount(account);
    58
            }
    59
    
                                                    
                                                
    60
            if (amount == 0) revert AMOUNT_NOT_VALID();
    61
            if (yieldSource == address(0) || account == address(0)) revert ADDRESS_NOT_VALID();
    62
    
                                                    
                                                
    63
            executions = new Execution[](1);
    64
            executions[0] = Execution({
    65
                target: yieldSource,
    66
                value: 0,
    67
                callData: abi.encodeCall(IERC7540.withdraw, (amount, account, account))
    68
            });
    69
        }
    70
    
                                                    
                                                
    71
        /*//////////////////////////////////////////////////////////////
    72
                                     EXTERNAL METHODS
    73
        //////////////////////////////////////////////////////////////*/
    74
    
                                                    
                                                
    75
        /// @inheritdoc ISuperHookInflowOutflow
    76
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    77
            return _decodeAmount(data);
    78
        }
    79
    
                                                    
                                                
    80
        /// @inheritdoc ISuperHookContextAware
    81
    ✓ 2.4K
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    82
    ✓ 2.4K
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    83
        }
    84
    
                                                    
                                                
    85
        /// @inheritdoc ISuperHookOutflow
    86
        function replaceCalldataAmount(bytes memory data, uint256 amount) external pure returns (bytes memory) {
    87
            return _replaceCalldataAmount(data, amount, AMOUNT_POSITION);
    88
        }
    89
    
                                                    
                                                
    90
        /// @inheritdoc ISuperHookInspector
    91
    ✓ 5.9K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    92
    ✓ 5.9K
            return abi.encodePacked(data.extractYieldSource());
    93
        }
    94
    
                                                    
                                                
    95
        /*//////////////////////////////////////////////////////////////
    96
                                     INTERNAL METHODS
    97
        //////////////////////////////////////////////////////////////*/
    98
        function _preExecute(address, address account, bytes calldata data) internal override {
    99
            address yieldSource = data.extractYieldSource();
    100
            asset = IERC7540(yieldSource).asset();
    101
            _setOutAmount(_getBalance(account, data), account);
    102
            usedShares = _getSharesBalance(account, data);
    103
            spToken = IERC7540(yieldSource).share();
    104
        }
    105
    
                                                    
                                                
    106
        function _postExecute(address, address account, bytes calldata data) internal override {
    107
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    108
            usedShares = usedShares - _getSharesBalance(account, data);
    109
        }
    110
    
                                                    
                                                
    111
        /*//////////////////////////////////////////////////////////////
    112
                                     PRIVATE METHODS
    113
        //////////////////////////////////////////////////////////////*/
    114
    ✓ 2.4K
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    115
    ✓ 2.4K
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    116
        }
    117
    
                                                    
                                                
    118
        function _getBalance(address account, bytes memory) private view returns (uint256) {
    119
            return IERC20(asset).balanceOf(account);
    120
        }
    121
    
                                                    
                                                
    122
        function _getSharesBalance(address account, bytes memory data) private view returns (uint256) {
    123
            address yieldSource = data.extractYieldSource();
    124
            return IERC7540(yieldSource).claimableRedeemRequest(0, account);
    125
        }
    126
    }
    127
    
                                                    
                                                
    93.3% lib/v2-core/src/hooks/vaults/super-vault/CancelRedeemHook.sol
    Lines covered: 14 / 15 (93.3%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    6
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    7
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    8
    
                                                    
                                                
    9
    // Superform
    10
    import { BaseHook } from "../../BaseHook.sol";
    11
    import { VaultBankLockableHook } from "../../VaultBankLockableHook.sol";
    12
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    13
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    14
    import { ISuperVault } from "../../../vendor/superform/ISuperVault.sol";
    15
    import { ISuperHookAsyncCancelations, ISuperHookInspector } from "../../../interfaces/ISuperHook.sol";
    16
    
                                                    
                                                
    17
    /// @title CancelRedeemHook
    18
    /// @author Superform Labs
    19
    /// @dev data has the following structure
    20
    /// @notice         bytes32 placeholder = bytes32(BytesLib.slice(data, 0, 32), 0);
    21
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    22
    contract CancelRedeemHook is BaseHook, VaultBankLockableHook, ISuperHookAsyncCancelations {
    23
        using HookDataDecoder for bytes;
    24
    
                                                    
                                                
    25
    ✓ 1
        constructor() BaseHook(HookType.NONACCOUNTING, HookSubTypes.CANCEL_REDEEM) { }
    26
    
                                                    
                                                
    27
        /*//////////////////////////////////////////////////////////////
    28
                                     VIEW METHODS
    29
        //////////////////////////////////////////////////////////////*/
    30
        function _buildHookExecutions(
    31
            address,
    32
            address account,
    33
            bytes calldata data
    34
        )
    35
            internal
    36
            pure
    37
            override
    38
    ✓ 600
            returns (Execution[] memory executions)
    39
        {
    40
    ✓ 600
            address yieldSource = data.extractYieldSource();
    41
    
                                                    
                                                
    42
    ✓ 600
            if (yieldSource == address(0) || account == address(0)) revert ADDRESS_NOT_VALID();
    43
    
                                                    
                                                
    44
    ✓ 600
            executions = new Execution[](1);
    45
    ✓ 600
            executions[0] =
    46
    ✓ 600
                Execution({ target: yieldSource, value: 0, callData: abi.encodeCall(ISuperVault.cancelRedeem, (account)) });
    47
        }
    48
    
                                                    
                                                
    49
        /*//////////////////////////////////////////////////////////////
    50
                                     EX`TERNAL METHODS
    51
        //////////////////////////////////////////////////////////////*/
    52
    
                                                    
                                                
    53
        /// @inheritdoc ISuperHookAsyncCancelations
    54
        function isAsyncCancelHook() external pure returns (CancelationType) {
    55
            return CancelationType.OUTFLOW;
    56
        }
    57
    
                                                    
                                                
    58
        /// @inheritdoc ISuperHookInspector
    59
    ✓ 4.1K
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    60
    ✓ 4.1K
            return abi.encodePacked(data.extractYieldSource());
    61
        }
    62
    
                                                    
                                                
    63
        /*//////////////////////////////////////////////////////////////
    64
                                     INTERNAL METHODS
    65
        //////////////////////////////////////////////////////////////*/
    66
        function _preExecute(address, address account, bytes calldata data) internal override {
    67
    ✓ 600
            _setOutAmount(_getBalance(account, data), account);
    68
    ✓ 141
            spToken = data.extractYieldSource();
    69
        }
    70
    
                                                    
                                                
    71
        function _postExecute(address, address account, bytes calldata data) internal override {
    72
    ✓ 141
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    73
        }
    74
        /*//////////////////////////////////////////////////////////////
    75
                                     PRIVATE METHODS
    76
        //////////////////////////////////////////////////////////////*/
    77
    
                                                    
                                                
    78
    ✓ 600
        function _getBalance(address account, bytes memory data) private view returns (uint256) {
    79
    ✓ 600
            return IERC20(IERC7540(data.extractYieldSource()).share()).balanceOf(account);
    80
        }
    81
    }
    82
    
                                                    
                                                
    2.8% lib/v2-core/src/hooks/vaults/super-vault/Withdraw7540VaultHook.sol
    Lines covered: 1 / 36 (2.8%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { BytesLib } from "../../../vendor/BytesLib.sol";
    6
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    7
    import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
    8
    import { IERC7540 } from "../../../vendor/vaults/7540/IERC7540.sol";
    9
    
                                                    
                                                
    10
    // Superform
    11
    import { BaseHook } from "../../BaseHook.sol";
    12
    import {
    13
        ISuperHookResultOutflow,
    14
        ISuperHookInflowOutflow,
    15
        ISuperHookOutflow,
    16
        ISuperHookContextAware,
    17
        ISuperHookInspector
    18
    } from "../../../interfaces/ISuperHook.sol";
    19
    import { HookSubTypes } from "../../../libraries/HookSubTypes.sol";
    20
    import { HookDataDecoder } from "../../../libraries/HookDataDecoder.sol";
    21
    
                                                    
                                                
    22
    /// @title Withdraw7540VaultHook
    23
    /// @author Superform Labs
    24
    /// @notice Compatible only with ERC-7540 vaults where `requestId` is non-fungible
    25
    /// @dev data has the following structure
    26
    /// @notice         bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0);
    27
    /// @notice         address yieldSource = BytesLib.toAddress(data, 32);
    28
    /// @notice         uint256 amount = BytesLib.toUint256(data, 52);
    29
    /// @notice         bool usePrevHookAmount = _decodeBool(data, 84);
    30
    contract Withdraw7540VaultHook is BaseHook, ISuperHookInflowOutflow, ISuperHookOutflow, ISuperHookContextAware {
    31
        using HookDataDecoder for bytes;
    32
    
                                                    
                                                
    33
        uint256 private constant AMOUNT_POSITION = 52;
    34
        uint256 private constant USE_PREV_HOOK_AMOUNT_POSITION = 84;
    35
    
                                                    
                                                
    36
    ✓ 2
        constructor() BaseHook(HookType.OUTFLOW, HookSubTypes.ERC7540) { }
    37
    
                                                    
                                                
    38
        /*//////////////////////////////////////////////////////////////
    39
                                     VIEW METHODS
    40
        //////////////////////////////////////////////////////////////*/
    41
        /// @inheritdoc BaseHook
    42
        function _buildHookExecutions(
    43
            address prevHook,
    44
            address account,
    45
            bytes calldata data
    46
        )
    47
            internal
    48
            view
    49
            override
    50
            returns (Execution[] memory executions)
    51
        {
    52
            address yieldSource = data.extractYieldSource();
    53
            uint256 amount = _decodeAmount(data);
    54
            bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    55
    
                                                    
                                                
    56
            if (usePrevHookAmount) {
    57
                amount = ISuperHookResultOutflow(prevHook).getOutAmount(account);
    58
            }
    59
    
                                                    
                                                
    60
            if (amount == 0) revert AMOUNT_NOT_VALID();
    61
            if (yieldSource == address(0) || account == address(0)) revert ADDRESS_NOT_VALID();
    62
    
                                                    
                                                
    63
            executions = new Execution[](1);
    64
            executions[0] = Execution({
    65
                target: yieldSource,
    66
                value: 0,
    67
                callData: abi.encodeCall(IERC7540.withdraw, (amount, account, account))
    68
            });
    69
        }
    70
    
                                                    
                                                
    71
        /*//////////////////////////////////////////////////////////////
    72
                                     EXTERNAL METHODS
    73
        //////////////////////////////////////////////////////////////*/
    74
    
                                                    
                                                
    75
        /// @inheritdoc ISuperHookInflowOutflow
    76
        function decodeAmount(bytes memory data) external pure returns (uint256) {
    77
            return _decodeAmount(data);
    78
        }
    79
    
                                                    
                                                
    80
        /// @inheritdoc ISuperHookContextAware
    81
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool) {
    82
            return _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION);
    83
        }
    84
    
                                                    
                                                
    85
        /// @inheritdoc ISuperHookOutflow
    86
        function replaceCalldataAmount(bytes memory data, uint256 amount) external pure returns (bytes memory) {
    87
            return _replaceCalldataAmount(data, amount, AMOUNT_POSITION);
    88
        }
    89
    
                                                    
                                                
    90
        /// @inheritdoc ISuperHookInspector
    91
        function inspect(bytes calldata data) external pure override returns (bytes memory) {
    92
            return abi.encodePacked(data.extractYieldSource());
    93
        }
    94
    
                                                    
                                                
    95
        /*//////////////////////////////////////////////////////////////
    96
                                     INTERNAL METHODS
    97
        //////////////////////////////////////////////////////////////*/
    98
        function _preExecute(address, address account, bytes calldata data) internal override {
    99
            address yieldSource = data.extractYieldSource();
    100
            asset = IERC7540(yieldSource).asset();
    101
            _setOutAmount(_getBalance(account, data), account);
    102
            usedShares = _getSharesBalance(account, data);
    103
            spToken = IERC7540(yieldSource).share();
    104
        }
    105
    
                                                    
                                                
    106
        function _postExecute(address, address account, bytes calldata data) internal override {
    107
            _setOutAmount(_getBalance(account, data) - getOutAmount(account), account);
    108
            usedShares = usedShares - _getSharesBalance(account, data);
    109
        }
    110
    
                                                    
                                                
    111
        /*//////////////////////////////////////////////////////////////
    112
                                     PRIVATE METHODS
    113
        //////////////////////////////////////////////////////////////*/
    114
        function _decodeAmount(bytes memory data) private pure returns (uint256) {
    115
            return BytesLib.toUint256(data, AMOUNT_POSITION);
    116
        }
    117
    
                                                    
                                                
    118
        function _getBalance(address account, bytes memory) private view returns (uint256) {
    119
            return IERC20(asset).balanceOf(account);
    120
        }
    121
    
                                                    
                                                
    122
        function _getSharesBalance(address account, bytes memory data) private view returns (uint256) {
    123
            address yieldSource = data.extractYieldSource();
    124
            return IERC7540(yieldSource).claimableRedeemRequest(0, account);
    125
        }
    126
    }
    127
    
                                                    
                                                
    0.0% lib/v2-core/src/interfaces/ISuperHook.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { Execution } from "modulekit/accounts/erc7579/lib/ExecutionLib.sol";
    6
    
                                                    
                                                
    7
    /**
    8
     * @title SuperHook System
    9
     * @author Superform Labs
    10
     * @notice The hook system provides a modular and composable way to execute operations on assets
    11
     * @dev The hook system architecture consists of several interfaces that work together:
    12
     *      - ISuperHook: The base interface all hooks implement, with lifecycle methods
    13
     *      - ISuperHookResult: Provides execution results and output information
    14
     *      - Specialized interfaces (ISuperHookOutflow, ISuperHookLoans, etc.) for specific behaviors
    15
     *
    16
     * Hooks are executed in sequence, where each hook can access the results from previous hooks.
    17
     * The three main types of hooks are:
    18
     *      - NONACCOUNTING: Utility hooks that don't update the accounting system
    19
     *      - INFLOW: Hooks that process deposits or additions to positions
    20
     *      - OUTFLOW: Hooks that process withdrawals or reductions to positions
    21
     */
    22
    interface ISuperLockableHook {
    23
        /// @notice The vault bank address used to lock SuperPositions
    24
        /// @dev Only relevant for cross-chain operations where positions are locked
    25
        /// @return The vault bank address, or address(0) if not applicable
    26
        function vaultBank() external view returns (address);
    27
    
                                                    
                                                
    28
        /// @notice The destination chain ID for cross-chain operations
    29
        /// @dev Used to identify the target chain for cross-chain position transfers
    30
        /// @return The destination chain ID, or 0 if not a cross-chain operation
    31
        function dstChainId() external view returns (uint256);
    32
    }
    33
    
                                                    
                                                
    34
    interface ISuperHookSetter {
    35
        /// @notice Sets the output amount for the hook
    36
        /// @dev Used for updating `outAmount` when fees were deducted
    37
        /// @param outAmount The amount of tokens processed by the hook
    38
        /// @param caller The caller address for context identification
    39
        function setOutAmount(uint256 outAmount, address caller) external;
    40
    }
    41
    /// @title ISuperHookInspector
    42
    /// @author Superform Labs
    43
    /// @notice Interface for the SuperHookInspector contract that manages hook inspection
    44
    
                                                    
                                                
    45
    interface ISuperHookInspector {
    46
        /// @notice Inspect the hook
    47
        /// @param data The hook data to inspect
    48
        /// @return argsEncoded The arguments of the hook encoded
    49
        function inspect(bytes calldata data) external view returns (bytes memory argsEncoded);
    50
    }
    51
    
                                                    
                                                
    52
    /// @title ISuperHookResult
    53
    /// @author Superform Labs
    54
    /// @notice Interface that exposes the result of a hook execution
    55
    /// @dev All hooks must implement this interface to provide standardized access to execution results.
    56
    ///      These results are used by subsequent hooks in the execution chain and by the executor.
    57
    interface ISuperHookResult {
    58
        /*//////////////////////////////////////////////////////////////
    59
                                     VIEW METHODS
    60
        //////////////////////////////////////////////////////////////*/
    61
    
                                                    
                                                
    62
        /// @notice The type of hook
    63
        /// @dev Used to determine how accounting should process this hook's results
    64
        /// @return The hook type (NONACCOUNTING, INFLOW, or OUTFLOW)
    65
        function hookType() external view returns (ISuperHook.HookType);
    66
    
                                                    
                                                
    67
        /// @notice The SuperPosition (SP) token associated with this hook
    68
        /// @dev For vault hooks, this would be the tokenized position representing shares
    69
        /// @return The address of the SP token, or address(0) if not applicable
    70
        function spToken() external view returns (address);
    71
    
                                                    
                                                
    72
        /// @notice The underlying asset token being processed
    73
        /// @dev For most hooks, this is the actual token being deposited or withdrawn
    74
        /// @return The address of the asset token, or address(0) for native assets
    75
        function asset() external view returns (address);
    76
    
                                                    
                                                
    77
        /// @notice The amount of tokens processed by the hook in a given caller context, subject to fees after update
    78
        /// @dev This is the primary output value used by subsequent hooks
    79
        /// @param caller The caller address for context identification
    80
        /// @return The amount of tokens (assets or shares) processed
    81
        function getOutAmount(address caller) external view returns (uint256);
    82
    }
    83
    
                                                    
                                                
    84
    /// @title ISuperHookContextAware
    85
    /// @author Superform Labs
    86
    /// @notice Interface for hooks that can use previous hook results in their execution
    87
    /// @dev Enables contextual awareness and data flow between hooks in a chain
    88
    interface ISuperHookContextAware {
    89
        /// @notice Determines if this hook should use the amount from the previous hook
    90
        /// @dev Used to create hook chains where output from one hook becomes input to the next
    91
        /// @param data The hook-specific data containing configuration
    92
        /// @return True if the hook should use the previous hook's output amount
    93
        function decodeUsePrevHookAmount(bytes memory data) external pure returns (bool);
    94
    }
    95
    
                                                    
                                                
    96
    /// @title ISuperHookInflowOutflow
    97
    /// @author Superform Labs
    98
    /// @notice Interface for hooks that handle both inflows and outflows
    99
    /// @dev Provides standardized amount extraction for both deposit and withdrawal operations
    100
    interface ISuperHookInflowOutflow {
    101
        /// @notice Extracts the amount from the hook's calldata
    102
        /// @dev Used to determine the quantity of assets or shares being processed
    103
        /// @param data The hook-specific calldata containing the amount
    104
        /// @return The amount of tokens to process
    105
        function decodeAmount(bytes memory data) external pure returns (uint256);
    106
    }
    107
    
                                                    
                                                
    108
    /// @title ISuperHookOutflow
    109
    /// @author Superform Labs
    110
    /// @notice Interface for hooks that specifically handle outflows (withdrawals)
    111
    /// @dev Provides additional functionality needed only for outflow operations
    112
    interface ISuperHookOutflow {
    113
        /// @notice Replace the amount in the calldata
    114
        /// @param data The data to replace the amount in
    115
        /// @param amount The amount to replace
    116
        /// @return data The data with the replaced amount
    117
        function replaceCalldataAmount(bytes memory data, uint256 amount) external pure returns (bytes memory);
    118
    }
    119
    
                                                    
                                                
    120
    /// @title ISuperHookResultOutflow
    121
    /// @author Superform Labs
    122
    /// @notice Extended result interface for outflow hook operations
    123
    /// @dev Extends the base result interface with outflow-specific information
    124
    interface ISuperHookResultOutflow is ISuperHookResult {
    125
        /// @notice The amount of shares consumed during outflow processing
    126
        /// @dev Used for cost basis calculation in the accounting system
    127
        /// @return The amount of shares consumed from the user's position
    128
        function usedShares() external view returns (uint256);
    129
    }
    130
    
                                                    
                                                
    131
    /// @title ISuperHookLoans
    132
    /// @author Superform Labs
    133
    /// @notice Interface for hooks that interact with lending protocols
    134
    /// @dev Extends context awareness to enable loan operations within hook chains
    135
    interface ISuperHookLoans is ISuperHookContextAware {
    136
        /// @notice Gets the address of the token being borrowed
    137
        /// @dev Used to identify which asset is being borrowed from the lending protocol
    138
        /// @param data The hook-specific data containing loan information
    139
        /// @return The address of the borrowed token
    140
        function getLoanTokenAddress(bytes memory data) external pure returns (address);
    141
    
                                                    
                                                
    142
        /// @notice Gets the address of the token used as collateral
    143
        /// @dev Used to identify which asset is being used to secure the loan
    144
        /// @param data The hook-specific data containing collateral information
    145
        /// @return The address of the collateral token
    146
        function getCollateralTokenAddress(bytes memory data) external view returns (address);
    147
    
                                                    
                                                
    148
        /// @notice Gets the current loan token balance for an account
    149
        /// @dev Used to track outstanding loan amounts
    150
        /// @param account The account to check the loan balance for
    151
        /// @param data The hook-specific data containing loan parameters
    152
        /// @return The amount of tokens currently borrowed
    153
        function getLoanTokenBalance(address account, bytes memory data) external view returns (uint256);
    154
    
                                                    
                                                
    155
        /// @notice Gets the current collateral token balance for an account
    156
        /// @dev Used to track collateral positions
    157
        /// @param account The account to check the collateral balance for
    158
        /// @param data The hook-specific data containing collateral parameters
    159
        /// @return The amount of tokens currently used as collateral
    160
        function getCollateralTokenBalance(address account, bytes memory data) external view returns (uint256);
    161
    }
    162
    
                                                    
                                                
    163
    /// @title ISuperHookAsyncCancelations
    164
    /// @author Superform Labs
    165
    /// @notice Interface for hooks that can cancel asynchronous operations
    166
    /// @dev Used to handle cancellation of pending operations that haven't completed
    167
    interface ISuperHookAsyncCancelations {
    168
        /// @notice Types of cancellations that can be performed
    169
        /// @dev Distinguishes between different operation types that can be canceled
    170
        enum CancelationType {
    171
            NONE, // Not a cancelation hook
    172
            INFLOW, // Cancels a pending deposit operation
    173
            OUTFLOW // Cancels a pending withdrawal operation
    174
    
                                                    
                                                
    175
        }
    176
    
                                                    
                                                
    177
        /// @notice Identifies the type of async operation this hook can cancel
    178
        /// @dev Used to verify the hook is appropriate for the operation being canceled
    179
        /// @return asyncType The type of cancellation this hook performs
    180
        function isAsyncCancelHook() external pure returns (CancelationType asyncType);
    181
    }
    182
    
                                                    
                                                
    183
    /// @title ISuperHook
    184
    /// @author Superform Labs
    185
    /// @notice The core hook interface that all hooks must implement
    186
    /// @dev Defines the lifecycle methods and execution flow for the hook system
    187
    ///      Hooks are executed in sequence with results passed between them
    188
    interface ISuperHook {
    189
        /*//////////////////////////////////////////////////////////////
    190
    
                                                    
                                                
    191
                                     ENUMS
    192
        //////////////////////////////////////////////////////////////*/
    193
        /// @notice Defines the possible types of hooks in the system
    194
        /// @dev Used to determine how the hook affects accounting and what operations it performs
    195
        enum HookType {
    196
            NONACCOUNTING, // Hook doesn't affect accounting (e.g., a swap or bridge)
    197
            INFLOW, // Hook processes deposits or positions being added
    198
            OUTFLOW // Hook processes withdrawals or positions being removed
    199
    
                                                    
                                                
    200
        }
    201
    
                                                    
                                                
    202
        /*//////////////////////////////////////////////////////////////
    203
                                     VIEW METHODS
    204
        //////////////////////////////////////////////////////////////*/
    205
        /// @notice Builds the execution array for the hook operation
    206
        /// @dev This is the core method where hooks define their on-chain interactions
    207
        ///      The returned executions are a sequence of contract calls to perform
    208
        ///      No state changes should occur in this method
    209
        /// @param prevHook The address of the previous hook in the chain, or address(0) if first
    210
        /// @param account The account to perform executions for (usually an ERC7579 account)
    211
        /// @param data The hook-specific parameters and configuration data
    212
        /// @return executions Array of Execution structs defining calls to make
    213
        function build(
    214
            address prevHook,
    215
            address account,
    216
            bytes calldata data
    217
        )
    218
            external
    219
            view
    220
            returns (Execution[] memory executions);
    221
    
                                                    
                                                
    222
        /*//////////////////////////////////////////////////////////////
    223
                                     PUBLIC METHODS
    224
        //////////////////////////////////////////////////////////////*/
    225
        /// @notice Prepares the hook for execution
    226
        /// @dev Called before the main execution, used to validate inputs and set execution context
    227
        ///      This method may perform state changes to set up the hook's execution state
    228
        /// @param prevHook The address of the previous hook in the chain, or address(0) if first
    229
        /// @param account The account to perform operations for
    230
        /// @param data The hook-specific parameters and configuration data
    231
        function preExecute(address prevHook, address account, bytes memory data) external;
    232
    
                                                    
                                                
    233
        /// @notice Finalizes the hook after execution
    234
        /// @dev Called after the main execution, used to update hook state and calculate results
    235
        ///      Sets output values (outAmount, usedShares, etc.) for subsequent hooks
    236
        /// @param prevHook The address of the previous hook in the chain, or address(0) if first
    237
        /// @param account The account operations were performed for
    238
        /// @param data The hook-specific parameters and configuration data
    239
        function postExecute(address prevHook, address account, bytes memory data) external;
    240
    
                                                    
                                                
    241
        /// @notice Returns the specific subtype identification for this hook
    242
        /// @dev Used to categorize hooks beyond the basic HookType
    243
        ///      For example, a hook might be of type INFLOW but subtype VAULT_DEPOSIT
    244
        /// @return A bytes32 identifier for the specific hook functionality
    245
        function subtype() external view returns (bytes32);
    246
    
                                                    
                                                
    247
        /// @notice Resets hook mutexes
    248
        /// @param caller The caller address for context identification
    249
        function resetExecutionState(address caller) external;
    250
    
                                                    
                                                
    251
        /// @notice Sets the caller address that initiated the execution
    252
        /// @dev Used for security validation between preExecute and postExecute calls
    253
        /// @param caller The caller address for context identification
    254
        function setExecutionContext(address caller) external;
    255
    
                                                    
                                                
    256
        /// @notice Returns the execution nonce for the current execution context
    257
        /// @dev Used to ensure unique execution contexts and prevent replay attacks
    258
        /// @return The execution nonce
    259
        function executionNonce() external view returns (uint256);
    260
    
                                                    
                                                
    261
        /// @notice Returns the last caller registered by `setExecutionContext`
    262
        /// @return The last caller address
    263
        function lastCaller() external view returns (address);
    264
    }
    265
    
                                                    
                                                
    0.0% lib/v2-core/src/interfaces/accounting/IYieldSourceOracle.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // Superform
    5
    import { IOracle } from "../../vendor/awesome-oracles/IOracle.sol";
    6
    
                                                    
                                                
    7
    /// @title IYieldSourceOracle
    8
    /// @author Superform Labs
    9
    /// @notice Interface for oracles that provide price and TVL data for yield-bearing assets
    10
    interface IYieldSourceOracle {
    11
        /*//////////////////////////////////////////////////////////////
    12
                                    ERRORS
    13
        //////////////////////////////////////////////////////////////*/
    14
    
                                                    
                                                
    15
        /// @notice Error when array lengths do not match in batch operations
    16
        /// @dev Thrown when the lengths of input arrays in multi-asset operations don't match
    17
        error ARRAY_LENGTH_MISMATCH();
    18
    
                                                    
                                                
    19
        /// @notice Error when base asset is not valid for the yield source
    20
        /// @dev Thrown when attempting to use an asset that isn't supported by the yield source
    21
        error INVALID_BASE_ASSET();
    22
    
                                                    
                                                
    23
        /*//////////////////////////////////////////////////////////////
    24
                                    STRUCTS
    25
        //////////////////////////////////////////////////////////////*/
    26
    
                                                    
                                                
    27
        /// @notice Struct to hold local variables for getTVLMultipleUSD
    28
        /// @dev Used to manage complex computation state without stack-too-deep errors
    29
        ///      These variables support the calculation of USD-denominated TVL values
    30
        ///      across multiple yield sources and owners
    31
        struct TVLMultipleUSDVars {
    32
            /// @notice Number of yield sources being processed
    33
            uint256 length;
    34
            /// @notice Number of share owners being processed
    35
            uint256 ownersLength;
    36
            /// @notice Base amount in the underlying asset's native units
    37
            uint256 baseAmount;
    38
            /// @notice Accumulated TVL in USD for a specific user
    39
            uint256 userTvlUSD;
    40
            /// @notice Accumulated total TVL in USD across all sources
    41
            uint256 totalTvlUSD;
    42
            /// @notice Current yield source being processed
    43
            address yieldSource;
    44
            /// @notice Array of addresses that own shares in the yield source
    45
            address[] owners;
    46
            /// @notice Oracle registry used for price conversions
    47
            IOracle registry;
    48
        }
    49
    
                                                    
                                                
    50
        /*//////////////////////////////////////////////////////////////
    51
                                VIEW METHODS
    52
        //////////////////////////////////////////////////////////////*/
    53
    
                                                    
                                                
    54
        /// @notice Returns the number of decimals of the yield source shares
    55
        /// @dev Critical for accurately interpreting share amounts and calculating prices
    56
        ///      Different yield sources may have different decimal precision
    57
        /// @param yieldSourceAddress The address of the yield-bearing token contract
    58
        /// @return decimals The number of decimals used by the yield source's share token
    59
        function decimals(address yieldSourceAddress) external view returns (uint8);
    60
    
                                                    
                                                
    61
        /// @notice Calculates the number of shares that would be received for a given amount of assets
    62
        /// @dev Used for deposit simulations and to calculate current exchange rates
    63
        /// @param yieldSourceAddress The yield-bearing token address (e.g., aUSDC, cDAI)
    64
        /// @param assetIn The underlying asset being deposited (e.g., USDC, DAI)
    65
        /// @param assetsIn The amount of underlying assets to deposit, in the asset's native units
    66
        /// @return shares The number of yield-bearing shares that would be received
    67
        function getShareOutput(
    68
            address yieldSourceAddress,
    69
            address assetIn,
    70
            uint256 assetsIn
    71
        )
    72
            external
    73
            view
    74
            returns (uint256);
    75
    
                                                    
                                                
    76
        /// @notice Calculates the number of underlying assets that would be received for a given amount of shares
    77
        /// @dev Used for withdrawal simulations and to calculate current yield
    78
        /// @param yieldSourceAddress The yield-bearing token address (e.g., aUSDC, cDAI)
    79
        /// @param assetIn The underlying asset to receive (e.g., USDC, DAI)
    80
        /// @param sharesIn The amount of yield-bearing shares to redeem
    81
        /// @return assets The number of underlying assets that would be received
    82
        function getAssetOutput(
    83
            address yieldSourceAddress,
    84
            address assetIn,
    85
            uint256 sharesIn
    86
        )
    87
            external
    88
            view
    89
            returns (uint256);
    90
    
                                                    
                                                
    91
        /// @notice Retrieves the current price per share in terms of the underlying asset
    92
        /// @dev Core function for calculating yields and determining returns
    93
        /// @param yieldSourceAddress The yield-bearing token address to get the price for
    94
        /// @return pricePerShare The current price per share in underlying asset terms, scaled by decimals
    95
        function getPricePerShare(address yieldSourceAddress) external view returns (uint256);
    96
    
                                                    
                                                
    97
        /// @notice Calculates the total value locked in a yield source by a specific owner
    98
        /// @dev Used to track individual position sizes within the system
    99
        /// @param yieldSourceAddress The yield-bearing token address to check
    100
        /// @param ownerOfShares The address owning the yield-bearing tokens
    101
        /// @return tvl The total value locked by the owner, in underlying asset terms
    102
        function getTVLByOwnerOfShares(address yieldSourceAddress, address ownerOfShares) external view returns (uint256);
    103
    
                                                    
                                                
    104
        /// @notice Gets the share balance of a specific owner in a yield source
    105
        /// @dev Returns raw share balance without converting to underlying assets
    106
        ///      Used to track participation in the system and for accounting
    107
        /// @param yieldSourceAddress The yield-bearing token address
    108
        /// @param ownerOfShares The address to check the balance for
    109
        /// @return balance The number of yield-bearing tokens owned by the address
    110
        function getBalanceOfOwner(address yieldSourceAddress, address ownerOfShares) external view returns (uint256);
    111
    
                                                    
                                                
    112
        /// @notice Calculates the total value locked across all users in a yield source
    113
        /// @dev Critical for monitoring the size of each yield source in the system
    114
        /// @param yieldSourceAddress The yield-bearing token address to check
    115
        /// @return tvl The total value locked in the yield source, in underlying asset terms
    116
        function getTVL(address yieldSourceAddress) external view returns (uint256);
    117
    
                                                    
                                                
    118
        /// @notice Batch version of getPricePerShare for multiple yield sources
    119
        /// @dev Efficiently retrieves current prices for multiple yield sources
    120
        /// @param yieldSourceAddresses Array of yield-bearing token addresses
    121
        /// @return pricesPerShare Array of current prices for each yield source
    122
        function getPricePerShareMultiple(address[] memory yieldSourceAddresses)
    123
            external
    124
            view
    125
            returns (uint256[] memory pricesPerShare);
    126
    
                                                    
                                                
    127
        /// @notice Batch version of getTVLByOwnerOfShares for multiple yield sources and owners
    128
        /// @dev Efficiently calculates TVL for multiple owners across multiple yield sources
    129
        /// @param yieldSourceAddresses Array of yield-bearing token addresses
    130
        /// @param ownersOfShares 2D array where each sub-array contains owner addresses for a yield source
    131
        /// @return userTvls 2D array of TVL values for each owner in each yield source
    132
        function getTVLByOwnerOfSharesMultiple(
    133
            address[] memory yieldSourceAddresses,
    134
            address[][] memory ownersOfShares
    135
        )
    136
            external
    137
            view
    138
            returns (uint256[][] memory userTvls);
    139
    
                                                    
                                                
    140
        /// @notice Batch version of getTVL for multiple yield sources
    141
        /// @dev Efficiently calculates total TVL across multiple yield sources
    142
        /// @param yieldSourceAddresses Array of yield-bearing token addresses
    143
        /// @return tvls Array containing the total TVL for each yield source
    144
        function getTVLMultiple(address[] memory yieldSourceAddresses) external view returns (uint256[] memory tvls);
    145
    
                                                    
                                                
    146
        /// @notice Calculates the asset output with fees added for an outflow operation
    147
        /// @dev Gets the asset output from the oracle and adds any applicable fees
    148
        ///      Uses try/catch to handle cases where oracle configuration doesn't exist
    149
        /// @param yieldSourceOracleId Identifier for the yield source oracle configuration
    150
        /// @param yieldSourceAddress Address of the yield-bearing asset
    151
        /// @param assetOut Address of the output asset
    152
        /// @param user Address of the user performing the outflow
    153
        /// @param usedShares Amount of shares being withdrawn
    154
        /// @return Total asset amount including fees
    155
        function getAssetOutputWithFees(
    156
            bytes32 yieldSourceOracleId,
    157
            address yieldSourceAddress,
    158
            address assetOut,
    159
            address user,
    160
            uint256 usedShares
    161
        )
    162
            external
    163
            view
    164
            returns (uint256);
    165
    }
    166
    
                                                    
                                                
    100.0% lib/v2-core/src/libraries/HookDataDecoder.sol
    Lines covered: 3 / 3 (100.0%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    import { BytesLib } from "../vendor/BytesLib.sol";
    5
    
                                                    
                                                
    6
    /// @title HookDataDecoder
    7
    /// @author Superform Labs
    8
    /// @notice Library for decoding hook data
    9
    ✓ 18.4K
    ⟲ 18.4K
    library HookDataDecoder {
    10
        function extractYieldSourceOracleId(bytes memory data) internal pure returns (bytes32) {
    11
            return bytes32(BytesLib.slice(data, 0, 32));
    12
        }
    13
    
                                                    
                                                
    14
    ✓ 2.2M
        function extractYieldSource(bytes memory data) internal pure returns (address) {
    15
    ✓ 2.2M
            return BytesLib.toAddress(data, 32);
    16
        }
    17
    }
    18
    
                                                    
                                                
    100.0% lib/v2-core/src/libraries/HookSubTypes.sol
    Lines covered: 23 / 23 (100.0%)
    1
    // SPDX-License-Identifier: Apache-2.0
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    /// @title HookSubTypes
    5
    /// @author Superform Labs
    6
    /// @notice Library for hook subtypes
    7
    ✓ 1
    library HookSubTypes {
    8
    ✓ 413
        bytes32 public constant BRIDGE = keccak256(bytes("Bridge"));
    9
    ✓ 414
        bytes32 public constant CANCEL_DEPOSIT = keccak256(bytes("CancelDeposit"));
    10
    ✓ 393
        bytes32 public constant CANCEL_DEPOSIT_REQUEST = keccak256(bytes("CancelDepositRequest"));
    11
    ✓ 451
        bytes32 public constant CANCEL_REDEEM = keccak256(bytes("CancelRedeem"));
    12
    ✓ 411
        bytes32 public constant CANCEL_REDEEM_REQUEST = keccak256(bytes("CancelRedeemRequest"));
    13
    ✓ 479
        bytes32 public constant CLAIM = keccak256(bytes("Claim"));
    14
    ✓ 457
        bytes32 public constant CLAIM_CANCEL_DEPOSIT_REQUEST = keccak256(bytes("ClaimCancelDepositRequest"));
    15
    ✓ 774
        bytes32 public constant CLAIM_CANCEL_REDEEM_REQUEST = keccak256(bytes("ClaimCancelRedeemRequest"));
    16
    ✓ 1.0K
        bytes32 public constant COOLDOWN = keccak256(bytes("Cooldown"));
    17
    ✓ 46.9K
        bytes32 public constant ETHENA = keccak256(bytes("Ethena"));
    18
    ✓ 491
        bytes32 public constant ERC4626 = keccak256(bytes("ERC4626"));
    19
    ✓ 1.2K
        bytes32 public constant ERC5115 = keccak256(bytes("ERC5115"));
    20
    ✓ 465
        bytes32 public constant ERC7540 = keccak256(bytes("ERC7540"));
    21
    ✓ 448
        bytes32 public constant LOAN = keccak256(bytes("Loan"));
    22
    ✓ 737
        bytes32 public constant LOAN_REPAY = keccak256(bytes("LoanRepay"));
    23
    ✓ 399
        bytes32 public constant MISC = keccak256(bytes("Misc"));
    24
    ✓ 452
        bytes32 public constant STAKE = keccak256(bytes("Stake"));
    25
    ✓ 456
        bytes32 public constant SWAP = keccak256(bytes("Swap"));
    26
    ✓ 1.2K
        bytes32 public constant TOKEN = keccak256(bytes("Token"));
    27
    ✓ 906
        bytes32 public constant UNSTAKE = keccak256(bytes("Unstake"));
    28
    ✓ 406
        bytes32 public constant PTYT = keccak256(bytes("PTYT"));
    29
    ✓ 451
        bytes32 public constant VAULT_BANK = keccak256(bytes("VaultBank"));
    30
    
                                                    
                                                
    31
        function getHookSubType(string memory hookSubtype) internal pure returns (bytes32) {
    32
            return keccak256(bytes(hookSubtype));
    33
        }
    34
    }
    35
    
                                                    
                                                
    100.0% lib/v2-core/src/vendor/BytesLib.sol
    Lines covered: 7 / 7 (100.0%)
    1
    // SPDX-License-Identifier: Unlicense
    2
    /*
    3
     * @title Solidity Bytes Arrays Utils
    4
     * @author Gonçalo Sá <goncalo.sa@consensys.net>
    5
     *
    6
     * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
    7
     *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
    8
     */
    9
    pragma solidity 0.8.30;
    10
    
                                                    
                                                
    11
    ✓ 22.0K
    ⟲ 22.0K
    library BytesLib {
    12
        function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) {
    13
            bytes memory tempBytes;
    14
    
                                                    
                                                
    15
            assembly {
    16
                // Get a location of some free memory and store it in tempBytes as
    17
                // Solidity does for memory variables.
    18
                tempBytes := mload(0x40)
    19
    
                                                    
                                                
    20
                // Store the length of the first bytes array at the beginning of
    21
                // the memory for tempBytes.
    22
                let length := mload(_preBytes)
    23
                mstore(tempBytes, length)
    24
    
                                                    
                                                
    25
                // Maintain a memory counter for the current write location in the
    26
                // temp bytes array by adding the 32 bytes for the array length to
    27
                // the starting location.
    28
                let mc := add(tempBytes, 0x20)
    29
                // Stop copying when the memory counter reaches the length of the
    30
                // first bytes array.
    31
                let end := add(mc, length)
    32
    
                                                    
                                                
    33
                for {
    34
                    // Initialize a copy counter to the start of the _preBytes data,
    35
                    // 32 bytes into its memory.
    36
                    let cc := add(_preBytes, 0x20)
    37
                } lt(mc, end) {
    38
                    // Increase both counters by 32 bytes each iteration.
    39
                    mc := add(mc, 0x20)
    40
                    cc := add(cc, 0x20)
    41
                } {
    42
                    // Write the _preBytes data into the tempBytes memory 32 bytes
    43
                    // at a time.
    44
                    mstore(mc, mload(cc))
    45
                }
    46
    
                                                    
                                                
    47
                // Add the length of _postBytes to the current length of tempBytes
    48
                // and store it as the new length in the first 32 bytes of the
    49
                // tempBytes memory.
    50
                length := mload(_postBytes)
    51
                mstore(tempBytes, add(length, mload(tempBytes)))
    52
    
                                                    
                                                
    53
                // Move the memory counter back from a multiple of 0x20 to the
    54
                // actual end of the _preBytes data.
    55
                mc := end
    56
                // Stop copying when the memory counter reaches the new combined
    57
                // length of the arrays.
    58
                end := add(mc, length)
    59
    
                                                    
                                                
    60
                for { let cc := add(_postBytes, 0x20) } lt(mc, end) {
    61
                    mc := add(mc, 0x20)
    62
                    cc := add(cc, 0x20)
    63
                } { mstore(mc, mload(cc)) }
    64
    
                                                    
                                                
    65
                // Update the free-memory pointer by padding our last write location
    66
                // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
    67
                // next 32 byte block, then round down to the nearest multiple of
    68
                // 32. If the sum of the length of the two arrays is zero then add
    69
                // one before rounding down to leave a blank 32 bytes (the length block with 0).
    70
                mstore(
    71
                    0x40,
    72
                    and(
    73
                        add(add(end, iszero(add(length, mload(_preBytes)))), 31),
    74
                        not(31) // Round down to the nearest 32 bytes.
    75
                    )
    76
                )
    77
            }
    78
    
                                                    
                                                
    79
            return tempBytes;
    80
        }
    81
    
                                                    
                                                
    82
        function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
    83
            assembly {
    84
                // Read the first 32 bytes of _preBytes storage, which is the length
    85
                // of the array. (We don't need to use the offset into the slot
    86
                // because arrays use the entire slot.)
    87
                let fslot := sload(_preBytes.slot)
    88
                // Arrays of 31 bytes or less have an even value in their slot,
    89
                // while longer arrays have an odd value. The actual length is
    90
                // the slot divided by two for odd values, and the lowest order
    91
                // byte divided by two for even values.
    92
                // If the slot is even, bitwise and the slot with 255 and divide by
    93
                // two to get the length. If the slot is odd, bitwise and the slot
    94
                // with -1 and divide by two.
    95
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
    96
                let mlength := mload(_postBytes)
    97
                let newlength := add(slength, mlength)
    98
                // slength can contain both the length and contents of the array
    99
                // if length < 32 bytes so let's prepare for that
    100
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
    101
                switch add(lt(slength, 32), lt(newlength, 32))
    102
                case 2 {
    103
                    // Since the new array still fits in the slot, we just need to
    104
                    // update the contents of the slot.
    105
                    // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
    106
                    sstore(
    107
                        _preBytes.slot,
    108
                        // all the modifications to the slot are inside this
    109
                        // next block
    110
                        add(
    111
                            // we can just add to the slot contents because the
    112
                            // bytes we want to change are the LSBs
    113
                            fslot,
    114
                            add(
    115
                                mul(
    116
                                    div(
    117
                                        // load the bytes from memory
    118
                                        mload(add(_postBytes, 0x20)),
    119
                                        // zero all bytes to the right
    120
                                        exp(0x100, sub(32, mlength))
    121
                                    ),
    122
                                    // and now shift left the number of bytes to
    123
                                    // leave space for the length in the slot
    124
                                    exp(0x100, sub(32, newlength))
    125
                                ),
    126
                                // increase length by the double of the memory
    127
                                // bytes length
    128
                                mul(mlength, 2)
    129
                            )
    130
                        )
    131
                    )
    132
                }
    133
                case 1 {
    134
                    // The stored value fits in the slot, but the combined value
    135
                    // will exceed it.
    136
                    // get the keccak hash to get the contents of the array
    137
                    mstore(0x0, _preBytes.slot)
    138
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
    139
    
                                                    
                                                
    140
                    // save new length
    141
                    sstore(_preBytes.slot, add(mul(newlength, 2), 1))
    142
    
                                                    
                                                
    143
                    // The contents of the _postBytes array start 32 bytes into
    144
                    // the structure. Our first read should obtain the `submod`
    145
                    // bytes that can fit into the unused space in the last word
    146
                    // of the stored array. To get this, we read 32 bytes starting
    147
                    // from `submod`, so the data we read overlaps with the array
    148
                    // contents by `submod` bytes. Masking the lowest-order
    149
                    // `submod` bytes allows us to add that value directly to the
    150
                    // stored value.
    151
    
                                                    
                                                
    152
                    let submod := sub(32, slength)
    153
                    let mc := add(_postBytes, submod)
    154
                    let end := add(_postBytes, mlength)
    155
                    let mask := sub(exp(0x100, submod), 1)
    156
    
                                                    
                                                
    157
                    sstore(
    158
                        sc,
    159
                        add(
    160
                            and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00),
    161
                            and(mload(mc), mask)
    162
                        )
    163
                    )
    164
    
                                                    
                                                
    165
                    for {
    166
                        mc := add(mc, 0x20)
    167
                        sc := add(sc, 1)
    168
                    } lt(mc, end) {
    169
                        sc := add(sc, 1)
    170
                        mc := add(mc, 0x20)
    171
                    } { sstore(sc, mload(mc)) }
    172
    
                                                    
                                                
    173
                    mask := exp(0x100, sub(mc, end))
    174
    
                                                    
                                                
    175
                    sstore(sc, mul(div(mload(mc), mask), mask))
    176
                }
    177
                default {
    178
                    // get the keccak hash to get the contents of the array
    179
                    mstore(0x0, _preBytes.slot)
    180
                    // Start copying to the last used word of the stored array.
    181
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
    182
    
                                                    
                                                
    183
                    // save new length
    184
                    sstore(_preBytes.slot, add(mul(newlength, 2), 1))
    185
    
                                                    
                                                
    186
                    // Copy over the first `submod` bytes of the new data as in
    187
                    // case 1 above.
    188
                    let slengthmod := mod(slength, 32)
    189
                    let mlengthmod := mod(mlength, 32)
    190
                    let submod := sub(32, slengthmod)
    191
                    let mc := add(_postBytes, submod)
    192
                    let end := add(_postBytes, mlength)
    193
                    let mask := sub(exp(0x100, submod), 1)
    194
    
                                                    
                                                
    195
                    sstore(sc, add(sload(sc), and(mload(mc), mask)))
    196
    
                                                    
                                                
    197
                    for {
    198
                        sc := add(sc, 1)
    199
                        mc := add(mc, 0x20)
    200
                    } lt(mc, end) {
    201
                        sc := add(sc, 1)
    202
                        mc := add(mc, 0x20)
    203
                    } { sstore(sc, mload(mc)) }
    204
    
                                                    
                                                
    205
                    mask := exp(0x100, sub(mc, end))
    206
    
                                                    
                                                
    207
                    sstore(sc, mul(div(mload(mc), mask), mask))
    208
                }
    209
            }
    210
        }
    211
    
                                                    
                                                
    212
        function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
    213
            // We're using the unchecked block below because otherwise execution ends
    214
            // with the native overflow error code.
    215
            unchecked {
    216
                require(_length + 31 >= _length, "slice_overflow");
    217
            }
    218
            require(_bytes.length >= _start + _length, "slice_outOfBounds");
    219
    
                                                    
                                                
    220
            bytes memory tempBytes;
    221
    
                                                    
                                                
    222
            assembly {
    223
                switch iszero(_length)
    224
                case 0 {
    225
                    // Get a location of some free memory and store it in tempBytes as
    226
                    // Solidity does for memory variables.
    227
                    tempBytes := mload(0x40)
    228
    
                                                    
                                                
    229
                    // The first word of the slice result is potentially a partial
    230
                    // word read from the original array. To read it, we calculate
    231
                    // the length of that partial word and start copying that many
    232
                    // bytes into the array. The first word we copy will start with
    233
                    // data we don't care about, but the last `lengthmod` bytes will
    234
                    // land at the beginning of the contents of the new array. When
    235
                    // we're done copying, we overwrite the full first word with
    236
                    // the actual length of the slice.
    237
                    let lengthmod := and(_length, 31)
    238
    
                                                    
                                                
    239
                    // The multiplication in the next line is necessary
    240
                    // because when slicing multiples of 32 bytes (lengthmod == 0)
    241
                    // the following copy loop was copying the origin's length
    242
                    // and then ending prematurely not copying everything it should.
    243
                    let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
    244
                    let end := add(mc, _length)
    245
    
                                                    
                                                
    246
                    for {
    247
                        // The multiplication in the next line has the same exact purpose
    248
                        // as the one above.
    249
                        let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
    250
                    } lt(mc, end) {
    251
                        mc := add(mc, 0x20)
    252
                        cc := add(cc, 0x20)
    253
                    } { mstore(mc, mload(cc)) }
    254
    
                                                    
                                                
    255
                    mstore(tempBytes, _length)
    256
    
                                                    
                                                
    257
                    //update free-memory pointer
    258
                    //allocating the array padded to 32 bytes like the compiler does now
    259
                    mstore(0x40, and(add(mc, 31), not(31)))
    260
                }
    261
                //if we want a zero-length slice let's just return a zero-length array
    262
                default {
    263
                    tempBytes := mload(0x40)
    264
                    //zero out the 32 bytes slice we are about to return
    265
                    //we need to do it because Solidity does not garbage collect
    266
                    mstore(tempBytes, 0)
    267
    
                                                    
                                                
    268
                    mstore(0x40, add(tempBytes, 0x20))
    269
                }
    270
            }
    271
    
                                                    
                                                
    272
            return tempBytes;
    273
        }
    274
    
                                                    
                                                
    275
    ✓ 4.0M
        function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
    276
    ✓ 4.0M
            require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
    277
            address tempAddress;
    278
    
                                                    
                                                
    279
            assembly {
    280
    ✓ 4.0M
                tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
    281
            }
    282
    
                                                    
                                                
    283
            return tempAddress;
    284
        }
    285
    
                                                    
                                                
    286
        function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
    287
            require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
    288
            uint8 tempUint;
    289
    
                                                    
                                                
    290
            assembly {
    291
                tempUint := mload(add(add(_bytes, 0x1), _start))
    292
            }
    293
    
                                                    
                                                
    294
            return tempUint;
    295
        }
    296
    
                                                    
                                                
    297
        function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
    298
            require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
    299
            uint16 tempUint;
    300
    
                                                    
                                                
    301
            assembly {
    302
                tempUint := mload(add(add(_bytes, 0x2), _start))
    303
            }
    304
    
                                                    
                                                
    305
            return tempUint;
    306
        }
    307
    
                                                    
                                                
    308
        function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
    309
            require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
    310
            uint32 tempUint;
    311
    
                                                    
                                                
    312
            assembly {
    313
                tempUint := mload(add(add(_bytes, 0x4), _start))
    314
            }
    315
    
                                                    
                                                
    316
            return tempUint;
    317
        }
    318
    
                                                    
                                                
    319
        function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
    320
            require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
    321
            uint64 tempUint;
    322
    
                                                    
                                                
    323
            assembly {
    324
                tempUint := mload(add(add(_bytes, 0x8), _start))
    325
            }
    326
    
                                                    
                                                
    327
            return tempUint;
    328
        }
    329
    
                                                    
                                                
    330
        function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
    331
            require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
    332
            uint96 tempUint;
    333
    
                                                    
                                                
    334
            assembly {
    335
                tempUint := mload(add(add(_bytes, 0xc), _start))
    336
            }
    337
    
                                                    
                                                
    338
            return tempUint;
    339
        }
    340
    
                                                    
                                                
    341
        function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
    342
            require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
    343
            uint128 tempUint;
    344
    
                                                    
                                                
    345
            assembly {
    346
                tempUint := mload(add(add(_bytes, 0x10), _start))
    347
            }
    348
    
                                                    
                                                
    349
            return tempUint;
    350
        }
    351
    
                                                    
                                                
    352
    ✓ 1.5M
        function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
    353
    ✓ 1.5M
            require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
    354
            uint256 tempUint;
    355
    
                                                    
                                                
    356
            assembly {
    357
    ✓ 1.5M
                tempUint := mload(add(add(_bytes, 0x20), _start))
    358
            }
    359
    
                                                    
                                                
    360
            return tempUint;
    361
        }
    362
    
                                                    
                                                
    363
        function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
    364
            require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
    365
            bytes32 tempBytes32;
    366
    
                                                    
                                                
    367
            assembly {
    368
                tempBytes32 := mload(add(add(_bytes, 0x20), _start))
    369
            }
    370
    
                                                    
                                                
    371
            return tempBytes32;
    372
        }
    373
    
                                                    
                                                
    374
        function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
    375
            bool success = true;
    376
    
                                                    
                                                
    377
            assembly {
    378
                let length := mload(_preBytes)
    379
    
                                                    
                                                
    380
                // if lengths don't match the arrays are not equal
    381
                switch eq(length, mload(_postBytes))
    382
                case 1 {
    383
                    // cb is a circuit breaker in the for loop since there's
    384
                    //  no said feature for inline assembly loops
    385
                    // cb = 1 - don't breaker
    386
                    // cb = 0 - break
    387
                    let cb := 1
    388
    
                                                    
                                                
    389
                    let mc := add(_preBytes, 0x20)
    390
                    let end := add(mc, length)
    391
    
                                                    
                                                
    392
                    for { let cc := add(_postBytes, 0x20) }
    393
                    // the next line is the loop condition:
    394
                    // while(uint256(mc < end) + cb == 2)
    395
                    eq(add(lt(mc, end), cb), 2) {
    396
                        mc := add(mc, 0x20)
    397
                        cc := add(cc, 0x20)
    398
                    } {
    399
                        // if any of these checks fails then arrays are not equal
    400
                        if iszero(eq(mload(mc), mload(cc))) {
    401
                            // unsuccess:
    402
                            success := 0
    403
                            cb := 0
    404
                        }
    405
                    }
    406
                }
    407
                default {
    408
                    // unsuccess:
    409
                    success := 0
    410
                }
    411
            }
    412
    
                                                    
                                                
    413
            return success;
    414
        }
    415
    
                                                    
                                                
    416
        function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) {
    417
            bool success = true;
    418
    
                                                    
                                                
    419
            assembly {
    420
                // we know _preBytes_offset is 0
    421
                let fslot := sload(_preBytes.slot)
    422
                // Decode the length of the stored array like in concatStorage().
    423
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
    424
                let mlength := mload(_postBytes)
    425
    
                                                    
                                                
    426
                // if lengths don't match the arrays are not equal
    427
                switch eq(slength, mlength)
    428
                case 1 {
    429
                    // slength can contain both the length and contents of the array
    430
                    // if length < 32 bytes so let's prepare for that
    431
                    // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
    432
                    if iszero(iszero(slength)) {
    433
                        switch lt(slength, 32)
    434
                        case 1 {
    435
                            // blank the last byte which is the length
    436
                            fslot := mul(div(fslot, 0x100), 0x100)
    437
    
                                                    
                                                
    438
                            if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
    439
                                // unsuccess:
    440
                                success := 0
    441
                            }
    442
                        }
    443
                        default {
    444
                            // cb is a circuit breaker in the for loop since there's
    445
                            //  no said feature for inline assembly loops
    446
                            // cb = 1 - don't breaker
    447
                            // cb = 0 - break
    448
                            let cb := 1
    449
    
                                                    
                                                
    450
                            // get the keccak hash to get the contents of the array
    451
                            mstore(0x0, _preBytes.slot)
    452
                            let sc := keccak256(0x0, 0x20)
    453
    
                                                    
                                                
    454
                            let mc := add(_postBytes, 0x20)
    455
                            let end := add(mc, mlength)
    456
    
                                                    
                                                
    457
                            // the next line is the loop condition:
    458
                            // while(uint256(mc < end) + cb == 2)
    459
                            for { } eq(add(lt(mc, end), cb), 2) {
    460
                                sc := add(sc, 1)
    461
                                mc := add(mc, 0x20)
    462
                            } {
    463
                                if iszero(eq(sload(sc), mload(mc))) {
    464
                                    // unsuccess:
    465
                                    success := 0
    466
                                    cb := 0
    467
                                }
    468
                            }
    469
                        }
    470
                    }
    471
                }
    472
                default {
    473
                    // unsuccess:
    474
                    success := 0
    475
                }
    476
            }
    477
    
                                                    
                                                
    478
            return success;
    479
        }
    480
    }
    481
    
                                                    
                                                
    0.0% lib/v2-core/src/vendor/awesome-oracles/IOracle.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity ^0.8.20;
    3
    
                                                    
                                                
    4
    /// @title Common interface for price oracles.
    5
    /// @dev Implements the spec at https://eips.ethereum.org/EIPS/eip-7726
    6
    interface IOracle {
    7
        /// @notice The oracle does not support the given base/quote pair.
    8
        /// @param base The asset that the user needs to know the value or price for.
    9
        /// @param quote The asset in which the user needs to value or price the base.
    10
        error OracleUnsupportedPair(address base, address quote);
    11
    
                                                    
                                                
    12
        /// @notice The oracle is not capable to provide data within a degree of confidence.
    13
        /// @param base The asset that the user needs to know the value or price for.
    14
        /// @param quote The asset in which the user needs to value or price the base.
    15
        error OracleUntrustedData(address base, address quote);
    16
    
                                                    
                                                
    17
        /// @notice Returns the value of `baseAmount` of `base` in `quote` terms.
    18
        /// @dev MUST round down towards 0.
    19
        /// MUST revert with `OracleUnsupportedPair` if not capable to provide data for the specified `base` and `quote`
    20
        /// pair.
    21
        /// MUST revert with `OracleUntrustedData` if not capable to provide data within a degree of confidence publicly
    22
        /// specified.
    23
        /// @param baseAmount The amount of `base` to convert.
    24
        /// @param base The asset that the user needs to know the value for.
    25
        /// @param quote The asset in which the user needs to value the base.
    26
        /// @return quoteAmount The value of `baseAmount` of `base` in `quote` terms
    27
        function getQuote(uint256 baseAmount, address base, address quote) external view returns (uint256 quoteAmount);
    28
    }
    29
    
                                                    
                                                
    0.0% lib/v2-core/src/vendor/pendle/IStandardizedYield.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: GPL-3.0-or-later
    2
    /*
    3
     * MIT License
    4
     * ===========
    5
     *
    6
     * Permission is hereby granted, free of charge, to any person obtaining a copy
    7
     * of this software and associated documentation files (the "Software"), to deal
    8
     * in the Software without restriction, including without limitation the rights
    9
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    10
     * copies of the Software, and to permit persons to whom the Software is
    11
     * furnished to do so, subject to the following conditions:
    12
     *
    13
     * The above copyright notice and this permission notice shall be included in all
    14
     * copies or substantial portions of the Software.
    15
     *
    16
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    21
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    22
     */
    23
    
                                                    
                                                
    24
    pragma solidity ^0.8.0;
    25
    
                                                    
                                                
    26
    import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
    27
    
                                                    
                                                
    28
    interface IStandardizedYield is IERC20Metadata {
    29
        /// @dev Emitted when any base tokens is deposited to mint shares
    30
        event Deposit(
    31
            address indexed caller,
    32
            address indexed receiver,
    33
            address indexed tokenIn,
    34
            uint256 amountDeposited,
    35
            uint256 amountSyOut
    36
        );
    37
    
                                                    
                                                
    38
        /// @dev Emitted when any shares are redeemed for base tokens
    39
        event Redeem(
    40
            address indexed caller,
    41
            address indexed receiver,
    42
            address indexed tokenOut,
    43
            uint256 amountSyToRedeem,
    44
            uint256 amountTokenOut
    45
        );
    46
    
                                                    
                                                
    47
        /// @dev check `assetInfo()` for more information
    48
        enum AssetType {
    49
            TOKEN,
    50
            LIQUIDITY
    51
        }
    52
    
                                                    
                                                
    53
        /// @dev Emitted when (`user`) claims their rewards
    54
        event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts);
    55
    
                                                    
                                                
    56
        /**
    57
         * @notice mints an amount of shares by depositing a base token.
    58
         * @param receiver shares recipient address
    59
         * @param tokenIn address of the base tokens to mint shares
    60
         * @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`)
    61
         * @param minSharesOut reverts if amount of shares minted is lower than this
    62
         * @return amountSharesOut amount of shares minted
    63
         * @dev Emits a {Deposit} event
    64
         *
    65
         * Requirements:
    66
         * - (`tokenIn`) must be a valid base token.
    67
         */
    68
        function deposit(
    69
            address receiver,
    70
            address tokenIn,
    71
            uint256 amountTokenToDeposit,
    72
            uint256 minSharesOut
    73
        )
    74
            external
    75
            payable
    76
            returns (uint256 amountSharesOut);
    77
    
                                                    
                                                
    78
        /**
    79
         * @notice redeems an amount of base tokens by burning some shares
    80
         * @param receiver recipient address
    81
         * @param amountSharesToRedeem amount of shares to be burned
    82
         * @param tokenOut address of the base token to be redeemed
    83
         * @param minTokenOut reverts if amount of base token redeemed is lower than this
    84
         * @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender`
    85
         * @return amountTokenOut amount of base tokens redeemed
    86
         * @dev Emits a {Redeem} event
    87
         *
    88
         * Requirements:
    89
         * - (`tokenOut`) must be a valid base token.
    90
         */
    91
        function redeem(
    92
            address receiver,
    93
            uint256 amountSharesToRedeem,
    94
            address tokenOut,
    95
            uint256 minTokenOut,
    96
            bool burnFromInternalBalance
    97
        )
    98
            external
    99
            returns (uint256 amountTokenOut);
    100
    
                                                    
                                                
    101
        /**
    102
         * @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account
    103
         * @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy
    104
         *  he can mint must be X * exchangeRate / 1e18
    105
         * @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication
    106
         *  & division
    107
         */
    108
        function exchangeRate() external view returns (uint256 res);
    109
    
                                                    
                                                
    110
        /**
    111
         * @notice claims reward for (`user`)
    112
         * @param user the user receiving their rewards
    113
         * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
    114
         * @dev
    115
         * Emits a `ClaimRewards` event
    116
         * See {getRewardTokens} for list of reward tokens
    117
         */
    118
        function claimRewards(address user) external returns (uint256[] memory rewardAmounts);
    119
    
                                                    
                                                
    120
        /**
    121
         * @notice get the amount of unclaimed rewards for (`user`)
    122
         * @param user the user to check for
    123
         * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
    124
         */
    125
        function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts);
    126
    
                                                    
                                                
    127
        function rewardIndexesCurrent() external returns (uint256[] memory indexes);
    128
    
                                                    
                                                
    129
        function rewardIndexesStored() external view returns (uint256[] memory indexes);
    130
    
                                                    
                                                
    131
        /**
    132
         * @notice returns the list of reward token addresses
    133
         */
    134
        function getRewardTokens() external view returns (address[] memory);
    135
    
                                                    
                                                
    136
        /**
    137
         * @notice returns the address of the underlying yield token
    138
         */
    139
        function yieldToken() external view returns (address);
    140
    
                                                    
                                                
    141
        /**
    142
         * @notice returns all tokens that can mint this SY
    143
         */
    144
        function getTokensIn() external view returns (address[] memory res);
    145
    
                                                    
                                                
    146
        /**
    147
         * @notice returns all tokens that can be redeemed by this SY
    148
         */
    149
        function getTokensOut() external view returns (address[] memory res);
    150
    
                                                    
                                                
    151
        function isValidTokenIn(address token) external view returns (bool);
    152
    
                                                    
                                                
    153
        function isValidTokenOut(address token) external view returns (bool);
    154
    
                                                    
                                                
    155
        function previewDeposit(
    156
            address tokenIn,
    157
            uint256 amountTokenToDeposit
    158
        )
    159
            external
    160
            view
    161
            returns (uint256 amountSharesOut);
    162
    
                                                    
                                                
    163
        function previewRedeem(
    164
            address tokenOut,
    165
            uint256 amountSharesToRedeem
    166
        )
    167
            external
    168
            view
    169
            returns (uint256 amountTokenOut);
    170
    
                                                    
                                                
    171
        /**
    172
         * @notice This function contains information to interpret what the asset is
    173
         * @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens,
    174
         *     2 for bridged yield bearing tokens like wstETH, rETH on Arbi whose the underlying asset doesn't exist on the
    175
         * chain)
    176
         * @return assetAddress the address of the asset
    177
         * @return assetDecimals the decimals of the asset
    178
         */
    179
        function assetInfo() external view returns (AssetType assetType, address assetAddress, uint8 assetDecimals);
    180
    }
    181
    
                                                    
                                                
    0.0% lib/v2-core/src/vendor/standards/ERC7540/IERC7540Vault.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: AGPL-3.0-only
    2
    pragma solidity >=0.8.0;
    3
    
                                                    
                                                
    4
    import { IERC7741 } from "../ERC7741/IERC7741.sol";
    5
    
                                                    
                                                
    6
    interface IERC7540Operator {
    7
        /**
    8
         * @dev The event emitted when an operator is set.
    9
         *
    10
         * @param controller The address of the controller.
    11
         * @param operator The address of the operator.
    12
         * @param approved The approval status.
    13
         */
    14
        event OperatorSet(address indexed controller, address indexed operator, bool approved);
    15
    
                                                    
                                                
    16
        /**
    17
         * @dev Sets or removes an operator for the caller.
    18
         *
    19
         * @param operator The address of the operator.
    20
         * @param approved The approval status.
    21
         * @return Whether the call was executed successfully or not
    22
         */
    23
        function setOperator(address operator, bool approved) external returns (bool);
    24
    
                                                    
                                                
    25
        /**
    26
         * @dev Returns `true` if the `operator` is approved as an operator for an `controller`.
    27
         *
    28
         * @param controller The address of the controller.
    29
         * @param operator The address of the operator.
    30
         * @return status The approval status
    31
         */
    32
        function isOperator(address controller, address operator) external view returns (bool status);
    33
    }
    34
    
                                                    
                                                
    35
    interface IERC7540Deposit is IERC7540Operator {
    36
        event DepositRequest(
    37
            address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    38
        );
    39
        /**
    40
         * @dev Transfers assets from sender into the Vault and submits a Request for asynchronous deposit.
    41
         *
    42
         * - MUST support ERC-20 approve / transferFrom on asset as a deposit Request flow.
    43
         * - MUST revert if all of assets cannot be requested for deposit.
    44
         * - owner MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    45
         *    approval of ERC-20 tokens from owner to sender is NOT enough.
    46
         *
    47
         * @param assets the amount of deposit assets to transfer from owner
    48
         * @param controller the controller of the request who will be able to operate the request
    49
         * @param owner the source of the deposit assets
    50
         *
    51
         * NOTE: most implementations will require pre-approval of the Vault with the Vault's underlying asset token.
    52
         */
    53
    
                                                    
                                                
    54
        function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256 requestId);
    55
    
                                                    
                                                
    56
        /**
    57
         * @dev Returns the amount of requested assets in Pending state.
    58
         *
    59
         * - MUST NOT include any assets in Claimable state for deposit or mint.
    60
         * - MUST NOT show any variations depending on the caller.
    61
         * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
    62
         */
    63
        function pendingDepositRequest(
    64
            uint256 requestId,
    65
            address controller
    66
        )
    67
            external
    68
            view
    69
            returns (uint256 pendingAssets);
    70
    
                                                    
                                                
    71
        /**
    72
         * @dev Returns the amount of requested assets in Claimable state for the controller to deposit or mint.
    73
         *
    74
         * - MUST NOT include any assets in Pending state.
    75
         * - MUST NOT show any variations depending on the caller.
    76
         * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
    77
         */
    78
        function claimableDepositRequest(
    79
            uint256 requestId,
    80
            address controller
    81
        )
    82
            external
    83
            view
    84
            returns (uint256 claimableAssets);
    85
    
                                                    
                                                
    86
        /**
    87
         * @dev Mints shares Vault shares to receiver by claiming the Request of the controller.
    88
         *
    89
         * - MUST emit the Deposit event.
    90
         * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator.
    91
         */
    92
        function deposit(uint256 assets, address receiver, address controller) external returns (uint256 shares);
    93
    
                                                    
                                                
    94
        /**
    95
         * @dev Mints exactly shares Vault shares to receiver by claiming the Request of the controller.
    96
         *
    97
         * - MUST emit the Deposit event.
    98
         * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator.
    99
         */
    100
        function mint(uint256 shares, address receiver, address controller) external returns (uint256 assets);
    101
    }
    102
    
                                                    
                                                
    103
    interface IERC7540Redeem is IERC7540Operator {
    104
        event RedeemRequest(
    105
            address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    106
        );
    107
    
                                                    
                                                
    108
        /**
    109
         * @dev Assumes control of shares from sender into the Vault and submits a Request for asynchronous redeem.
    110
         *
    111
         * - MUST support a redeem Request flow where the control of shares is taken from sender directly
    112
         *   where msg.sender has ERC-20 approval over the shares of owner.
    113
         * - MUST revert if all of shares cannot be requested for redeem.
    114
         *
    115
         * @param shares the amount of shares to be redeemed to transfer from owner
    116
         * @param controller the controller of the request who will be able to operate the request
    117
         * @param owner the source of the shares to be redeemed
    118
         *
    119
         * NOTE: most implementations will require pre-approval of the Vault with the Vault's share token.
    120
         */
    121
        function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256 requestId);
    122
    
                                                    
                                                
    123
        /**
    124
         * @dev Returns the amount of requested shares in Pending state.
    125
         *
    126
         * - MUST NOT include any shares in Claimable state for redeem or withdraw.
    127
         * - MUST NOT show any variations depending on the caller.
    128
         * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
    129
         */
    130
        function pendingRedeemRequest(
    131
            uint256 requestId,
    132
            address controller
    133
        )
    134
            external
    135
            view
    136
            returns (uint256 pendingShares);
    137
    
                                                    
                                                
    138
        /**
    139
         * @dev Returns the amount of requested shares in Claimable state for the controller to redeem or withdraw.
    140
         *
    141
         * - MUST NOT include any shares in Pending state for redeem or withdraw.
    142
         * - MUST NOT show any variations depending on the caller.
    143
         * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
    144
         */
    145
        function claimableRedeemRequest(
    146
            uint256 requestId,
    147
            address controller
    148
        )
    149
            external
    150
            view
    151
            returns (uint256 claimableShares);
    152
    }
    153
    
                                                    
                                                
    154
    interface IERC7540CancelDeposit {
    155
        event CancelDepositRequest(address indexed controller, uint256 indexed requestId, address sender);
    156
        event CancelDepositClaim(
    157
            address indexed receiver, address indexed controller, uint256 indexed requestId, address sender, uint256 assets
    158
        );
    159
    
                                                    
                                                
    160
        /**
    161
         * @dev Submits a Request for cancelling the pending deposit Request
    162
         *
    163
         * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    164
         *    approval of ERC-20 tokens from controller to sender is NOT enough.
    165
         * - MUST set pendingCancelDepositRequest to `true` for the returned requestId after request
    166
         * - MUST increase claimableCancelDepositRequest for the returned requestId after fulfillment
    167
         * - SHOULD be claimable using `claimCancelDepositRequest`
    168
         * Note: while `pendingCancelDepositRequest` is `true`, `requestDeposit` cannot be called
    169
         */
    170
        function cancelDepositRequest(uint256 requestId, address controller) external;
    171
    
                                                    
                                                
    172
        /**
    173
         * @dev Returns whether the deposit Request is pending cancelation
    174
         *
    175
         * - MUST NOT show any variations depending on the caller.
    176
         */
    177
        function pendingCancelDepositRequest(
    178
            uint256 requestId,
    179
            address controller
    180
        )
    181
            external
    182
            view
    183
            returns (bool isPending);
    184
    
                                                    
                                                
    185
        /**
    186
         * @dev Returns the amount of assets that were canceled from a deposit Request, and can now be claimed.
    187
         *
    188
         * - MUST NOT show any variations depending on the caller.
    189
         */
    190
        function claimableCancelDepositRequest(
    191
            uint256 requestId,
    192
            address controller
    193
        )
    194
            external
    195
            view
    196
            returns (uint256 claimableAssets);
    197
    
                                                    
                                                
    198
        /**
    199
         * @dev Claims the canceled deposit assets, and removes the pending cancelation Request
    200
         *
    201
         * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    202
         *    approval of ERC-20 tokens from controller to sender is NOT enough.
    203
         * - MUST set pendingCancelDepositRequest to `false` for the returned requestId after request
    204
         * - MUST set claimableCancelDepositRequest to 0 for the returned requestId after fulfillment
    205
         */
    206
        function claimCancelDepositRequest(
    207
            uint256 requestId,
    208
            address receiver,
    209
            address controller
    210
        )
    211
            external
    212
            returns (uint256 assets);
    213
    }
    214
    
                                                    
                                                
    215
    interface IERC7540CancelRedeem {
    216
        event CancelRedeemRequest(address indexed controller, uint256 indexed requestId, address sender);
    217
        event CancelRedeemClaim(
    218
            address indexed receiver, address indexed controller, uint256 indexed requestId, address sender, uint256 shares
    219
        );
    220
    
                                                    
                                                
    221
        /**
    222
         * @dev Submits a Request for cancelling the pending redeem Request
    223
         *
    224
         * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    225
         *    approval of ERC-20 tokens from controller to sender is NOT enough.
    226
         * - MUST set pendingCancelRedeemRequest to `true` for the returned requestId after request
    227
         * - MUST increase claimableCancelRedeemRequest for the returned requestId after fulfillment
    228
         * - SHOULD be claimable using `claimCancelRedeemRequest`
    229
         * Note: while `pendingCancelRedeemRequest` is `true`, `requestRedeem` cannot be called
    230
         */
    231
        function cancelRedeemRequest(uint256 requestId, address controller) external;
    232
    
                                                    
                                                
    233
        /**
    234
         * @dev Returns whether the redeem Request is pending cancelation
    235
         *
    236
         * - MUST NOT show any variations depending on the caller.
    237
         */
    238
        function pendingCancelRedeemRequest(uint256 requestId, address controller) external view returns (bool isPending);
    239
    
                                                    
                                                
    240
        /**
    241
         * @dev Returns the amount of shares that were canceled from a redeem Request, and can now be claimed.
    242
         *
    243
         * - MUST NOT show any variations depending on the caller.
    244
         */
    245
        function claimableCancelRedeemRequest(
    246
            uint256 requestId,
    247
            address controller
    248
        )
    249
            external
    250
            view
    251
            returns (uint256 claimableShares);
    252
    
                                                    
                                                
    253
        /**
    254
         * @dev Claims the canceled redeem shares, and removes the pending cancelation Request
    255
         *
    256
         * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    257
         *    approval of ERC-20 tokens from controller to sender is NOT enough.
    258
         * - MUST set pendingCancelRedeemRequest to `false` for the returned requestId after request
    259
         * - MUST set claimableCancelRedeemRequest to 0 for the returned requestId after fulfillment
    260
         */
    261
        function claimCancelRedeemRequest(
    262
            uint256 requestId,
    263
            address receiver,
    264
            address controller
    265
        )
    266
            external
    267
            returns (uint256 shares);
    268
    }
    269
    
                                                    
                                                
    270
    /**
    271
     * @title  IERC7540
    272
     * @dev    Fully async ERC7540 implementation according to the standard
    273
     * @dev    Adapted from Centrifuge's IERC7540 implementation
    274
     */
    275
    interface IERC7540 is IERC7540Deposit, IERC7540Redeem { }
    276
    
                                                    
                                                
    277
    /**
    278
     * @title  IERC7540Vault
    279
     * @dev    This is the specific set of interfaces used by the SuperVaults
    280
     */
    281
    interface IERC7540Vault is IERC7540, IERC7741 {
    282
        event DepositClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
    283
        event RedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
    284
    }
    285
    
                                                    
                                                
    0.0% lib/v2-core/src/vendor/standards/ERC7741/IERC7741.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: AGPL-3.0-only
    2
    pragma solidity >=0.8.0;
    3
    
                                                    
                                                
    4
    interface IERC7741 {
    5
        /**
    6
         * @dev Grants or revokes permissions for `operator` to manage Requests on behalf of the
    7
         *      `msg.sender`, using an [EIP-712](./eip-712.md) signature.
    8
         */
    9
        function authorizeOperator(
    10
            address controller,
    11
            address operator,
    12
            bool approved,
    13
            bytes32 nonce,
    14
            uint256 deadline,
    15
            bytes memory signature
    16
        )
    17
            external
    18
            returns (bool);
    19
    
                                                    
                                                
    20
        /**
    21
         * @dev Revokes the given `nonce` for `msg.sender` as the `owner`.
    22
         */
    23
        function invalidateNonce(bytes32 nonce) external;
    24
    
                                                    
                                                
    25
        /**
    26
         * @dev Returns whether the given `nonce` has been used for the `controller`.
    27
         */
    28
        function authorizations(address controller, bytes32 nonce) external view returns (bool used);
    29
    
                                                    
                                                
    30
        /**
    31
         * @dev Returns the `DOMAIN_SEPARATOR` as defined according to EIP-712. The `DOMAIN_SEPARATOR
    32
         *      should be unique to the contract and chain to prevent replay attacks from other domains,
    33
         *      and satisfy the requirements of EIP-712, but is otherwise unconstrained.
    34
         */
    35
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    36
    }
    37
    
                                                    
                                                
    0.0% lib/v2-core/src/vendor/superform/ISuperVault.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    /// @title ISuperVault
    5
    /// @notice Interface for SuperVault core contract that manages share minting
    6
    /// @author Superform Labs
    7
    interface ISuperVault {
    8
        /*//////////////////////////////////////////////////////////////
    9
                                    ERRORS
    10
        //////////////////////////////////////////////////////////////*/
    11
        error ALREADY_INITIALIZED();
    12
        error INVALID_ASSET();
    13
        error INVALID_STRATEGY();
    14
        error INVALID_ESCROW();
    15
        error ZERO_ADDRESS();
    16
        error ZERO_AMOUNT();
    17
        error INVALID_OWNER_OR_OPERATOR();
    18
        error INVALID_AMOUNT();
    19
        error REQUEST_NOT_FOUND();
    20
        error UNAUTHORIZED();
    21
        error TIMELOCK_NOT_EXPIRED();
    22
        error INVALID_SIGNATURE();
    23
        error NOT_IMPLEMENTED();
    24
        error INVALID_NONCE();
    25
        error INVALID_WITHDRAW_PRICE();
    26
        error TRANSFER_FAILED();
    27
        error CAP_EXCEEDED();
    28
        error INVALID_PPS();
    29
        error INVALID_CONTROLLER();
    30
    
                                                    
                                                
    31
        /*//////////////////////////////////////////////////////////////
    32
                                    EVENTS
    33
        //////////////////////////////////////////////////////////////*/
    34
    
                                                    
                                                
    35
        event RedeemClaimable(
    36
            address indexed user,
    37
            uint256 indexed requestId,
    38
            uint256 assets,
    39
            uint256 shares,
    40
            uint256 averageWithdrawPrice,
    41
            uint256 accumulatorShares,
    42
            uint256 accumulatorCostBasis
    43
        );
    44
    
                                                    
                                                
    45
        event NonceInvalidated(address indexed sender, bytes32 indexed nonce);
    46
    
                                                    
                                                
    47
        event RedeemRequestCancelled(address indexed controller, address indexed sender);
    48
    
                                                    
                                                
    49
        /*//////////////////////////////////////////////////////////////
    50
                                EXTERNAL METHODS
    51
        //////////////////////////////////////////////////////////////*/
    52
    
                                                    
                                                
    53
        function cancelRedeem(address controller) external;
    54
    
                                                    
                                                
    55
        /// @notice Mint new shares, only callable by strategy
    56
        /// @param amount The amount of shares to mint
    57
        function mintShares(uint256 amount) external;
    58
    
                                                    
                                                
    59
        /// @notice Burn shares, only callable by strategy
    60
        /// @param amount The amount of shares to burn
    61
        function burnShares(uint256 amount) external;
    62
    
                                                    
                                                
    63
        /// @notice Callback function for when a redeem becomes claimable
    64
        /// @param user The user whose redeem is claimable
    65
        /// @param assets The amount of assets to be received
    66
        /// @param shares The amount of shares redeemed
    67
        /// @param averageWithdrawPrice The average price of the redeem
    68
        /// @param accumulatorShares The amount of shares in the accumulator
    69
        /// @param accumulatorCostBasis The cost basis of the accumulator
    70
        function onRedeemClaimable(
    71
            address user,
    72
            uint256 assets,
    73
            uint256 shares,
    74
            uint256 averageWithdrawPrice,
    75
            uint256 accumulatorShares,
    76
            uint256 accumulatorCostBasis
    77
        )
    78
            external;
    79
    }
    80
    
                                                    
                                                
    0.0% lib/v2-core/src/vendor/vaults/7540/IERC7540.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    interface IERC7575 {
    5
        event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
    6
        event Withdraw(
    7
            address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
    8
        );
    9
    
                                                    
                                                
    10
        /**
    11
         * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
    12
         *
    13
         * - MUST be an ERC-20 token contract.
    14
         * - MUST NOT revert.
    15
         */
    16
        function asset() external view returns (address assetTokenAddress);
    17
    
                                                    
                                                
    18
        /**
    19
         * @dev Returns the address of the share token
    20
         *
    21
         * - MUST be an ERC-20 token contract.
    22
         * - MUST NOT revert.
    23
         */
    24
        function share() external view returns (address shareTokenAddress);
    25
    
                                                    
                                                
    26
        /**
    27
         * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
    28
         * scenario where all the conditions are met.
    29
         *
    30
         * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    31
         * - MUST NOT show any variations depending on the caller.
    32
         * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    33
         * - MUST NOT revert.
    34
         *
    35
         * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    36
         * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    37
         * from.
    38
         */
    39
        function convertToShares(uint256 assets) external view returns (uint256 shares);
    40
    
                                                    
                                                
    41
        /**
    42
         * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
    43
         * scenario where all the conditions are met.
    44
         *
    45
         * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    46
         * - MUST NOT show any variations depending on the caller.
    47
         * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    48
         * - MUST NOT revert.
    49
         *
    50
         * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    51
         * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    52
         * from.
    53
         */
    54
        function convertToAssets(uint256 shares) external view returns (uint256 assets);
    55
    
                                                    
                                                
    56
        /**
    57
         * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
    58
         *
    59
         * - SHOULD include any compounding that occurs from yield.
    60
         * - MUST be inclusive of any fees that are charged against assets in the Vault.
    61
         * - MUST NOT revert.
    62
         */
    63
        function totalAssets() external view returns (uint256 totalManagedAssets);
    64
    
                                                    
                                                
    65
        /**
    66
         * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
    67
         * through a deposit call.
    68
         *
    69
         * - MUST return a limited value if receiver is subject to some deposit limit.
    70
         * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
    71
         * - MUST NOT revert.
    72
         */
    73
        function maxDeposit(address receiver) external view returns (uint256 maxAssets);
    74
    
                                                    
                                                
    75
        /**
    76
         * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
    77
         * current on-chain conditions.
    78
         *
    79
         * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
    80
         *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
    81
         *   in the same transaction.
    82
         * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
    83
         *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
    84
         * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    85
         * - MUST NOT revert.
    86
         *
    87
         * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
    88
         * share price or some other type of condition, meaning the depositor will lose assets by depositing.
    89
         */
    90
        function previewDeposit(uint256 assets) external view returns (uint256 shares);
    91
    
                                                    
                                                
    92
        /**
    93
         * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
    94
         *
    95
         * - MUST emit the Deposit event.
    96
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    97
         *   deposit execution, and are accounted for during deposit.
    98
         * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
    99
         *   approving enough underlying tokens to the Vault contract, etc).
    100
         *
    101
         * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    102
         */
    103
        function deposit(uint256 assets, address receiver) external returns (uint256 shares);
    104
    
                                                    
                                                
    105
        /**
    106
         * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
    107
         * - MUST return a limited value if receiver is subject to some mint limit.
    108
         * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
    109
         * - MUST NOT revert.
    110
         */
    111
        function maxMint(address receiver) external view returns (uint256 maxShares);
    112
    
                                                    
                                                
    113
        /**
    114
         * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
    115
         * current on-chain conditions.
    116
         *
    117
         * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
    118
         *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
    119
         *   same transaction.
    120
         * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
    121
         *   would be accepted, regardless if the user has enough tokens approved, etc.
    122
         * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    123
         * - MUST NOT revert.
    124
         *
    125
         * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
    126
         * share price or some other type of condition, meaning the depositor will lose assets by minting.
    127
         */
    128
        function previewMint(uint256 shares) external view returns (uint256 assets);
    129
    
                                                    
                                                
    130
        /**
    131
         * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
    132
         *
    133
         * - MUST emit the Deposit event.
    134
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
    135
         *   execution, and are accounted for during mint.
    136
         * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
    137
         *   approving enough underlying tokens to the Vault contract, etc).
    138
         *
    139
         * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    140
         */
    141
        function mint(uint256 shares, address receiver) external returns (uint256 assets);
    142
    
                                                    
                                                
    143
        /**
    144
         * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
    145
         * Vault, through a withdraw call.
    146
         *
    147
         * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    148
         * - MUST NOT revert.
    149
         */
    150
        function maxWithdraw(address owner) external view returns (uint256 maxAssets);
    151
    
                                                    
                                                
    152
        /**
    153
         * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
    154
         * given current on-chain conditions.
    155
         *
    156
         * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
    157
         *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
    158
         *   called
    159
         *   in the same transaction.
    160
         * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
    161
         *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
    162
         * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    163
         * - MUST NOT revert.
    164
         *
    165
         * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
    166
         * share price or some other type of condition, meaning the depositor will lose assets by depositing.
    167
         */
    168
        function previewWithdraw(uint256 assets) external view returns (uint256 shares);
    169
    
                                                    
                                                
    170
        /**
    171
         * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
    172
         *
    173
         * - MUST emit the Withdraw event.
    174
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    175
         *   withdraw execution, and are accounted for during withdraw.
    176
         * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
    177
         *   not having enough shares, etc).
    178
         *
    179
         * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    180
         * Those methods should be performed separately.
    181
         */
    182
        function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
    183
    
                                                    
                                                
    184
        /**
    185
         * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
    186
         * through a redeem call.
    187
         *
    188
         * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    189
         * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
    190
         * - MUST NOT revert.
    191
         */
    192
        function maxRedeem(address owner) external view returns (uint256 maxShares);
    193
    
                                                    
                                                
    194
        /**
    195
         * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
    196
         * given current on-chain conditions.
    197
         *
    198
         * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
    199
         *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
    200
         *   same transaction.
    201
         * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
    202
         *   redemption would be accepted, regardless if the user has enough shares, etc.
    203
         * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    204
         * - MUST NOT revert.
    205
         *
    206
         * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
    207
         * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
    208
         */
    209
        function previewRedeem(uint256 shares) external view returns (uint256 assets);
    210
    
                                                    
                                                
    211
        /**
    212
         * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
    213
         *
    214
         * - MUST emit the Withdraw event.
    215
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    216
         *   redeem execution, and are accounted for during redeem.
    217
         * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
    218
         *   not having enough shares, etc).
    219
         *
    220
         * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    221
         * Those methods should be performed separately.
    222
         */
    223
        function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
    224
    }
    225
    // As defined by https://eips.ethereum.org/EIPS/eip-7540#request-flows
    226
    
                                                    
                                                
    227
    interface IERC7540 is IERC7575 {
    228
        event DepositRequest(
    229
            address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    230
        );
    231
    
                                                    
                                                
    232
        event RedeemRequest(
    233
            address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    234
        );
    235
    
                                                    
                                                
    236
        /*//////////////////////////////////////////////////////////////
    237
                                     VIEW METHODS
    238
        //////////////////////////////////////////////////////////////*/
    239
        /// @notice Check if the contract supports an interface
    240
        /// @param interfaceId The selector of the interface to check
    241
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    242
    
                                                    
                                                
    243
        /// @notice Preview the amount of assets that would be received for a given amount of shares
    244
        /// @param shares The amount of shares to redeem
    245
        function previewRedeem(uint256 shares) external view returns (uint256 assets);
    246
    
                                                    
                                                
    247
        /// @notice Check if a deposit request is pending
    248
        /// @param requestId The id of the request to check
    249
        /// @param controller The address of the controller
    250
        function pendingDepositRequest(uint256 requestId, address controller) external view returns (uint256);
    251
    
                                                    
                                                
    252
        /// @notice Check if a deposit request is pending cancellation
    253
        /// @param requestId The id of the request to check
    254
        /// @param controller The address of the controller
    255
        function pendingCancelDepositRequest(uint256 requestId, address controller) external view returns (bool);
    256
    
                                                    
                                                
    257
        /// @notice Check if a redeem request is pending
    258
        /// @param requestId The id of the request to check
    259
        /// @param controller The address of the controller
    260
        function pendingRedeemRequest(uint256 requestId, address controller) external view returns (uint256);
    261
    
                                                    
                                                
    262
        /// @notice Get the amount of assets that can be claimed from a deposit request
    263
        /// @param requestId The id of the request to check
    264
        /// @param controller The address of the controller
    265
        function claimableDepositRequest(uint256 requestId, address controller) external view returns (uint256);
    266
    
                                                    
                                                
    267
        /// @notice Get the amount of shares that can be claimed from a redeem request
    268
        /// @param requestId The id of the request to check
    269
        /// @param controller The address of the controller
    270
        function claimableRedeemRequest(uint256 requestId, address controller) external view returns (uint256);
    271
    
                                                    
                                                
    272
        /// @notice Check if a redeem request is pending cancellation
    273
        /// @param requestId The id of the request to check
    274
        /// @param controller The address of the controller
    275
        function pendingCancelRedeemRequest(uint256 requestId, address controller) external view returns (bool);
    276
    
                                                    
                                                
    277
        /*//////////////////////////////////////////////////////////////
    278
                                     EXTERNAL METHODS
    279
        //////////////////////////////////////////////////////////////*/
    280
        /// @notice Mints amount of shares by claiming the controller's request
    281
        /// @dev sender must be the controller or an operator approved by the controller
    282
        /// @param assets The amount of assets to deposit
    283
        /// @param receiver The address of the receiver
    284
        /// @param controller The address of the controller
    285
        function deposit(uint256 assets, address receiver, address controller) external returns (uint256 shares);
    286
    
                                                    
                                                
    287
        /// @notice Mint exact amount of shares into the vault by claiming the controller's request
    288
        /// @param shares The amount of shares to mint
    289
        /// @param receiver The address of the receiver
    290
        /// @param controller The address of the controller
    291
        function mint(uint256 shares, address receiver, address controller) external returns (uint256 assets);
    292
    
                                                    
                                                
    293
        /// @notice Burn shares from the vault and sends exact amount of assets to the receiver
    294
        /// @param assets The amount of assets to withdraw
    295
        /// @param receiver The address of the receiver
    296
        /// @param owner The address of the owner
    297
        function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
    298
    
                                                    
                                                
    299
        /// @notice Burns exact shares from the vault and sends assets to the receiver
    300
        /// @param shares The amount of shares to redeem
    301
        /// @param receiver The address of the receiver
    302
        /// @param owner The address of the owner
    303
        function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
    304
    
                                                    
                                                
    305
        /// @notice Request a deposit of assets
    306
        /// @param assets The amount of assets to deposit
    307
        /// @param controller The address of the controller
    308
        /// @param owner The address of the owner
    309
        function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256 requestId);
    310
    
                                                    
                                                
    311
        /// @notice Request a redeem of shares
    312
        /// @param shares The amount of shares to redeem
    313
        /// @param controller The address of the controller
    314
        /// @param owner The address of the owner
    315
        function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256 requestId);
    316
    
                                                    
                                                
    317
        /// @notice Cancel a deposit request
    318
        /// @param requestId The id of the request to cancel
    319
        /// @param controller The address of the controller
    320
        function cancelDepositRequest(uint256 requestId, address controller) external;
    321
    
                                                    
                                                
    322
        /// @notice Claims the canceled deposit assets, and removes the pending cancelation Request
    323
        /// @dev sender must be the controller
    324
        /// @param requestId The id of the request to claim
    325
        /// @param receiver The address of the receiver
    326
        /// @param controller The address of the controller
    327
        function claimCancelDepositRequest(
    328
            uint256 requestId,
    329
            address receiver,
    330
            address controller
    331
        )
    332
            external
    333
            returns (uint256 assets);
    334
    
                                                    
                                                
    335
        /// @notice Cancel a redeem request
    336
        /// @param requestId The id of the request to cancel
    337
        /// @param controller The address of the controller
    338
        function cancelRedeemRequest(uint256 requestId, address controller) external;
    339
    
                                                    
                                                
    340
        /// @notice Claims the canceled redeem shares, and removes the pending cancelation Request
    341
        /// @dev sender must be the controller
    342
        /// @param requestId The id of the request to claim
    343
        /// @param receiver The address of the receiver
    344
        /// @param controller The address of the controller
    345
        function claimCancelRedeemRequest(
    346
            uint256 requestId,
    347
            address receiver,
    348
            address controller
    349
        )
    350
            external
    351
            returns (uint256 shares);
    352
    
                                                    
                                                
    353
        /// @notice Get the pool id
    354
        function poolId() external view returns (uint64);
    355
    
                                                    
                                                
    356
        /// @notice Get the tranche id
    357
        function trancheId() external view returns (bytes16);
    358
    
                                                    
                                                
    359
        /*//////////////////////////////////////////////////////////////
    360
                                     OWNER METHODS
    361
        //////////////////////////////////////////////////////////////*/
    362
        function setOperator(address operator, bool approved) external returns (bool);
    363
        function isOperator(address controller, address operator) external view returns (bool status);
    364
    }
    365
    
                                                    
                                                
    31.8% src/SuperGovernor.sol
    Lines covered: 121 / 381 (31.8%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // external
    5
    import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
    6
    import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    7
    import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
    8
    
                                                    
                                                
    9
    // Superform
    10
    import { ISuperGovernor, FeeType } from "./interfaces/ISuperGovernor.sol";
    11
    import { ISuperVaultAggregator } from "./interfaces/SuperVault/ISuperVaultAggregator.sol";
    12
    import { ISuperAssetFactory } from "./interfaces/SuperAsset/ISuperAssetFactory.sol";
    13
    import { ISuperOracle } from "./interfaces/oracles/ISuperOracle.sol";
    14
    import { ISuperOracleL2 } from "./interfaces/oracles/ISuperOracleL2.sol";
    15
    
                                                    
                                                
    16
    /// @title SuperGovernor
    17
    /// @author Superform Labs
    18
    /// @notice Central registry for all deployed contracts in the Superform periphery
    19
    contract SuperGovernor is ISuperGovernor, AccessControl {
    20
        using EnumerableSet for EnumerableSet.AddressSet;
    21
    
                                                    
                                                
    22
        /*//////////////////////////////////////////////////////////////
    23
                                     STORAGE
    24
        //////////////////////////////////////////////////////////////*/
    25
        // Address registry
    26
        mapping(bytes32 id => address address_) private _addressRegistry;
    27
    
                                                    
                                                
    28
        // PPS Oracle management
    29
        // Active PPS Oracle quorum requirement
    30
        uint256 private _activePPSOracleQuorum;
    31
        // Current active PPS Oracle
    32
        address private _activePPSOracle;
    33
        // Proposed new active PPS Oracle
    34
        address private _proposedActivePPSOracle;
    35
        // Effective time for proposed active PPS Oracle change
    36
        uint256 private _activePPSOracleEffectiveTime;
    37
    
                                                    
                                                
    38
        // Hook registry
    39
        EnumerableSet.AddressSet private _registeredHooks;
    40
        EnumerableSet.AddressSet private _registeredFulfillRequestsHooks;
    41
    
                                                    
                                                
    42
        // SuperBank Hook Target validation
    43
        mapping(address hook => ISuperGovernor.HookMerkleRootData merkleData) private superBankHooksMerkleRoots;
    44
    
                                                    
                                                
    45
        // VaultBank registry
    46
        EnumerableSet.AddressSet private _vaultBanks;
    47
        mapping(uint64 chainId => address vaultBank) private _vaultBanksByChainId;
    48
    
                                                    
                                                
    49
        // VaultBank Hook Target validation
    50
        mapping(address hook => ISuperGovernor.HookMerkleRootData merkleData) private vaultBankHooksMerkleRoots;
    51
    
                                                    
                                                
    52
        // Global freeze for manager takeovers
    53
        bool private _managerTakeoversFrozen;
    54
    
                                                    
                                                
    55
        // Validator registry
    56
        EnumerableSet.AddressSet private _validators;
    57
    
                                                    
                                                
    58
        // Relayer registry
    59
        EnumerableSet.AddressSet private _relayers;
    60
    
                                                    
                                                
    61
        // Protected keepers registry (cannot be added as authorized callers by managers)
    62
        EnumerableSet.AddressSet private _protectedKeepers;
    63
    
                                                    
                                                
    64
        // Executor registry
    65
        EnumerableSet.AddressSet private _executors;
    66
    
                                                    
                                                
    67
        // Polymer prover
    68
        address private _prover;
    69
    
                                                    
                                                
    70
        // Whitelisted incentive tokens
    71
        mapping(address token => bool isWhitelisted) private _isWhitelistedIncentiveToken;
    72
        EnumerableSet.AddressSet private _proposedWhitelistedIncentiveTokens;
    73
        EnumerableSet.AddressSet private _proposedRemoveWhitelistedIncentiveTokens;
    74
        uint256 private _proposedAddWhitelistedIncentiveTokensEffectiveTime;
    75
        uint256 private _proposedRemoveWhitelistedIncentiveTokensEffectiveTime;
    76
    
                                                    
                                                
    77
        // Fee management
    78
        // Current fee values
    79
        mapping(FeeType type_ => uint256 value) private _feeValues;
    80
        // Proposed fee values
    81
        mapping(FeeType type_ => uint256 proposedValue) private _proposedFeeValues;
    82
        // Effective times for proposed fee updates
    83
        mapping(FeeType type_ => uint256 effectiveTime) private _feeEffectiveTimes;
    84
    
                                                    
                                                
    85
        mapping(address _oracle => GasInfo info) private _oracleGasInfo;
    86
    
                                                    
                                                
    87
        // Upkeep control
    88
        bool private _upkeepPaymentsEnabled;
    89
        bool private _proposedUpkeepPaymentsEnabled;
    90
        uint256 private _upkeepPaymentsChangeEffectiveTime;
    91
    
                                                    
                                                
    92
        // Superform managers (exempt from upkeep costs)
    93
        EnumerableSet.AddressSet private _superformManagers;
    94
    
                                                    
                                                
    95
        // Min staleness configuration to prevent maxStaleness from being set too low
    96
        uint256 private _minStaleness;
    97
        uint256 private _proposedMinStaleness;
    98
        uint256 private _minStalenesEffectiveTime;
    99
    
                                                    
                                                
    100
        // Oracle constants
    101
        address private constant NATIVE_TOKEN = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
    102
        address private constant USD_TOKEN = address(840);
    103
        address private constant GAS_QUOTE =
    104
            address(uint160(uint256(keccak256("GAS_QUOTE"))));
    105
        address private constant GWEI_QUOTE =
    106
            address(uint160(uint256(keccak256("GWEI_QUOTE"))));
    107
        bytes32 private constant AVERAGE_PROVIDER = keccak256("AVERAGE_PROVIDER");
    108
    
                                                    
                                                
    109
        // Timelock configuration
    110
    ✓ 225.7K
        uint256 private constant TIMELOCK = 7 days;
    111
    ✓ 142.0K
        uint256 private constant BPS_MAX = 10_000; // 100% in basis points
    112
    
                                                    
                                                
    113
        // Role definitions
    114
        bytes32 private constant _SUPER_GOVERNOR_ROLE = keccak256("SUPER_GOVERNOR_ROLE");
    115
        bytes32 private constant _GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE");
    116
        bytes32 private constant _BANK_MANAGER_ROLE = keccak256("BANK_MANAGER_ROLE");
    117
    ✓ 1
        bytes32 private constant _GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
    118
        bytes32 private constant _SUPER_ASSET_FACTORY = keccak256("SUPER_ASSET_FACTORY");
    119
        bytes32 private constant _GAS_MANAGER_ROLE = keccak256("GAS_MANAGER_ROLE");
    120
    
                                                    
                                                
    121
        // Common contract keys
    122
    ✓ 314.2K
        bytes32 public constant UP = keccak256("UP");
    123
        bytes32 public constant SUP = keccak256("SUP");
    124
    ✓ 253.0K
        bytes32 public constant TREASURY = keccak256("TREASURY");
    125
        bytes32 public constant VAULT_BANK = keccak256("VAULT_BANK");
    126
    ✓ 2.4K
        bytes32 public constant SUPER_BANK = keccak256("SUPER_BANK");
    127
        bytes32 public constant SUPER_ORACLE = keccak256("SUPER_ORACLE");
    128
        bytes32 public constant BANK_MANAGER = keccak256("BANK_MANAGER");
    129
        bytes32 public constant ECDSAPPSORACLE = keccak256("ECDSAPPSORACLE");
    130
    ✓ 19.5M
        bytes32 public constant SUPER_VAULT_AGGREGATOR = keccak256("SUPER_VAULT_AGGREGATOR");
    131
    
                                                    
                                                
    132
        /*//////////////////////////////////////////////////////////////
    133
                                  CONSTRUCTOR
    134
        //////////////////////////////////////////////////////////////*/
    135
        /// @notice Initializes the SuperGovernor contract
    136
        /// @param superGovernor Address of the default admin (will have SUPER_GOVERNOR_ROLE)
    137
        /// @param governor Address that will have the GOVERNOR_ROLE for daily operations
    138
        /// @param bankManager Address that will have the BANK_MANAGER_ROLE for daily operations
    139
        /// @param treasury_ Address of the treasury
    140
        /// @param prover_ Address of the prover
    141
        constructor(address superGovernor, address governor, address bankManager, address gasManager, address treasury_, address prover_) {
    142
            if (
    143
    ✓ 1
                superGovernor == address(0) || treasury_ == address(0) || governor == address(0)
    144
    ✓ 1
                    || bankManager == address(0) || prover_ == address(0) || gasManager == address(0)
    145
            ) revert INVALID_ADDRESS();
    146
    
                                                    
                                                
    147
            // Set up roles
    148
    ✓ 1
            _grantRole(DEFAULT_ADMIN_ROLE, superGovernor);
    149
    ✓ 1
            _grantRole(_SUPER_GOVERNOR_ROLE, superGovernor);
    150
    ✓ 1
            _grantRole(_GOVERNOR_ROLE, governor);
    151
    ✓ 1
            _grantRole(_BANK_MANAGER_ROLE, bankManager);
    152
    ✓ 1
            _grantRole(_GAS_MANAGER_ROLE, gasManager);
    153
            // Setup GUARDIAN_ROLE without assigning any address
    154
    ✓ 1
            _setRoleAdmin(_GUARDIAN_ROLE, DEFAULT_ADMIN_ROLE);
    155
    
                                                    
                                                
    156
            // Set role admins
    157
    ✓ 1
            _setRoleAdmin(_GOVERNOR_ROLE, DEFAULT_ADMIN_ROLE);
    158
    ✓ 1
            _setRoleAdmin(_SUPER_GOVERNOR_ROLE, DEFAULT_ADMIN_ROLE);
    159
    ✓ 1
            _setRoleAdmin(_BANK_MANAGER_ROLE, DEFAULT_ADMIN_ROLE);
    160
    ✓ 1
            _setRoleAdmin(_GAS_MANAGER_ROLE, DEFAULT_ADMIN_ROLE);
    161
    
                                                    
                                                
    162
            // Initialize with default fees
    163
    ✓ 1
            _feeValues[FeeType.REVENUE_SHARE] = 2000; // 20% revenue share
    164
    ✓ 1
            _feeValues[FeeType.SUPER_VAULT_PERFORMANCE_FEE] = 2000; // 20% performance fee
    165
    ✓ 1
            _feeValues[FeeType.SUPER_ASSET_SWAP_FEE] = 4000; // 40% swap fee
    166
    ✓ 1
            emit FeeUpdated(FeeType.REVENUE_SHARE, _feeValues[FeeType.REVENUE_SHARE]);
    167
    ✓ 1
            emit FeeUpdated(FeeType.SUPER_VAULT_PERFORMANCE_FEE, _feeValues[FeeType.SUPER_VAULT_PERFORMANCE_FEE]);
    168
    ✓ 1
            emit FeeUpdated(FeeType.SUPER_ASSET_SWAP_FEE, _feeValues[FeeType.SUPER_ASSET_SWAP_FEE]);
    169
    
                                                    
                                                
    170
            // Set treasury in address registry
    171
    ✓ 1
            _addressRegistry[TREASURY] = treasury_;
    172
    ✓ 1
            emit AddressSet(TREASURY, treasury_);
    173
    
                                                    
                                                
    174
            // Initialize minimum staleness (5 minutes to prevent extremely low staleness values)
    175
    ✓ 1
            _minStaleness = 300; // 5 minutes in seconds
    176
    
                                                    
                                                
    177
            // Initialize prover
    178
    ✓ 1
            _prover = prover_;
    179
    ✓ 1
            emit ProverSet(prover_);
    180
        }
    181
    
                                                    
                                                
    182
        /*//////////////////////////////////////////////////////////////
    183
                           CONTRACT REGISTRY FUNCTIONS
    184
        //////////////////////////////////////////////////////////////*/
    185
        /// @inheritdoc ISuperGovernor
    186
        function setAddress(bytes32 key, address value) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    187
    ✓ 3
            if (value == address(0)) revert INVALID_ADDRESS();
    188
    
                                                    
                                                
    189
    ✓ 3
            _addressRegistry[key] = value;
    190
    ✓ 3
            emit AddressSet(key, value);
    191
        }
    192
    
                                                    
                                                
    193
        /*//////////////////////////////////////////////////////////////
    194
                        PERIPHERY CONFIGURATIONS
    195
        //////////////////////////////////////////////////////////////*/
    196
        /// @inheritdoc ISuperGovernor
    197
        function setProver(address prover) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    198
            if (prover == address(0)) revert INVALID_ADDRESS();
    199
    
                                                    
                                                
    200
            _prover = prover;
    201
            emit ProverSet(prover);
    202
        }
    203
    
                                                    
                                                
    204
        /// @inheritdoc ISuperGovernor
    205
        function changePrimaryManager(address strategy, address newManager) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    206
            // Check if takeovers are globally frozen
    207
    ✓ 149.6K
            if (_managerTakeoversFrozen) revert MANAGER_TAKEOVERS_FROZEN();
    208
    
                                                    
                                                
    209
    ✓ 149.6K
            address aggregator = _addressRegistry[SUPER_VAULT_AGGREGATOR];
    210
            if (aggregator == address(0)) revert CONTRACT_NOT_FOUND();
    211
    
                                                    
                                                
    212
            // Call the interface method to change the manager
    213
            // This function can only be called by the SuperGovernor and bypasses the timelock
    214
    ✓ 149.6K
            ISuperVaultAggregator(aggregator).changePrimaryManager(strategy, newManager);
    215
        }
    216
    
                                                    
                                                
    217
        /// @inheritdoc ISuperGovernor
    218
        function freezeManagerTakeover() external onlyRole(_SUPER_GOVERNOR_ROLE) {
    219
            if (_managerTakeoversFrozen) revert MANAGER_TAKEOVERS_FROZEN();
    220
    
                                                    
                                                
    221
            // Set frozen status to true (permanent, cannot be undone)
    222
            _managerTakeoversFrozen = true;
    223
    
                                                    
                                                
    224
            // Emit event for the frozen status
    225
            emit ManagerTakeoversFrozen();
    226
        }
    227
    
                                                    
                                                
    228
        /// @inheritdoc ISuperGovernor
    229
        function changeHooksRootUpdateTimelock(uint256 newTimelock) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    230
            address aggregator = _addressRegistry[SUPER_VAULT_AGGREGATOR];
    231
            if (aggregator == address(0)) revert CONTRACT_NOT_FOUND();
    232
    
                                                    
                                                
    233
            // Call the SuperVaultAggregator to change the hooks root update timelock
    234
            ISuperVaultAggregator(aggregator).setHooksRootUpdateTimelock(newTimelock);
    235
        }
    236
    
                                                    
                                                
    237
        /// @inheritdoc ISuperGovernor
    238
        function proposeGlobalHooksRoot(bytes32 newRoot) external onlyRole(_GOVERNOR_ROLE) {
    239
    ✓ 218.2K
            address aggregator = _addressRegistry[SUPER_VAULT_AGGREGATOR];
    240
            if (aggregator == address(0)) revert CONTRACT_NOT_FOUND();
    241
    
                                                    
                                                
    242
    ✓ 218.2K
            ISuperVaultAggregator(aggregator).proposeGlobalHooksRoot(newRoot);
    243
        }
    244
    
                                                    
                                                
    245
        /// @inheritdoc ISuperGovernor
    246
        function setGlobalHooksRootVetoStatus(bool vetoed) external onlyRole(_GUARDIAN_ROLE) {
    247
            address aggregator = _addressRegistry[SUPER_VAULT_AGGREGATOR];
    248
            if (aggregator == address(0)) revert CONTRACT_NOT_FOUND();
    249
    
                                                    
                                                
    250
            ISuperVaultAggregator(aggregator).setGlobalHooksRootVetoStatus(vetoed);
    251
        }
    252
    
                                                    
                                                
    253
        /// @inheritdoc ISuperGovernor
    254
        function setStrategyHooksRootVetoStatus(address strategy, bool vetoed) external onlyRole(_GUARDIAN_ROLE) {
    255
            if (strategy == address(0)) revert INVALID_ADDRESS();
    256
    
                                                    
                                                
    257
            address aggregator = _addressRegistry[SUPER_VAULT_AGGREGATOR];
    258
            if (aggregator == address(0)) revert CONTRACT_NOT_FOUND();
    259
    
                                                    
                                                
    260
            ISuperVaultAggregator(aggregator).setStrategyHooksRootVetoStatus(strategy, vetoed);
    261
        }
    262
    
                                                    
                                                
    263
        /// @inheritdoc ISuperGovernor
    264
        function setSuperAssetManager(address superAsset, address superAssetManager) external onlyRole(_GOVERNOR_ROLE) {
    265
            if (superAsset == address(0) || superAssetManager == address(0)) revert INVALID_ADDRESS();
    266
            address value = _addressRegistry[_SUPER_ASSET_FACTORY];
    267
            if (value == address(0)) revert CONTRACT_NOT_FOUND();
    268
            ISuperAssetFactory factory = ISuperAssetFactory(value);
    269
            factory.setSuperAssetManager(superAsset, superAssetManager);
    270
        }
    271
    
                                                    
                                                
    272
        /// @inheritdoc ISuperGovernor
    273
        function addICCToWhitelist(address icc) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    274
            if (icc == address(0)) revert INVALID_ADDRESS();
    275
            address value = _addressRegistry[_SUPER_ASSET_FACTORY];
    276
            if (value == address(0)) revert CONTRACT_NOT_FOUND();
    277
            ISuperAssetFactory factory = ISuperAssetFactory(value);
    278
            factory.addICCToWhitelist(icc);
    279
        }
    280
    
                                                    
                                                
    281
        /// @inheritdoc ISuperGovernor
    282
        function removeICCFromWhitelist(address icc) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    283
            if (icc == address(0)) revert INVALID_ADDRESS();
    284
            address value = _addressRegistry[_SUPER_ASSET_FACTORY];
    285
            if (value == address(0)) revert CONTRACT_NOT_FOUND();
    286
            ISuperAssetFactory factory = ISuperAssetFactory(value);
    287
            factory.removeICCFromWhitelist(icc);
    288
        }
    289
    
                                                    
                                                
    290
        /// @inheritdoc ISuperGovernor
    291
        function setOracleMaxStaleness(uint256 newMaxStaleness) external onlyRole(_GOVERNOR_ROLE) {
    292
            if (newMaxStaleness < _minStaleness) revert MAX_STALENESS_TOO_LOW();
    293
            address oracle = _addressRegistry[SUPER_ORACLE];
    294
            if (oracle == address(0)) revert CONTRACT_NOT_FOUND();
    295
    
                                                    
                                                
    296
            ISuperOracle(oracle).setMaxStaleness(newMaxStaleness);
    297
        }
    298
    
                                                    
                                                
    299
        /// @inheritdoc ISuperGovernor
    300
        function setOracleFeedMaxStaleness(address feed, uint256 newMaxStaleness) external onlyRole(_GOVERNOR_ROLE) {
    301
            if (feed == address(0)) revert INVALID_ADDRESS();
    302
            if (newMaxStaleness < _minStaleness) revert MAX_STALENESS_TOO_LOW();
    303
            address oracle = _addressRegistry[SUPER_ORACLE];
    304
            if (oracle == address(0)) revert CONTRACT_NOT_FOUND();
    305
    
                                                    
                                                
    306
            ISuperOracle(oracle).setFeedMaxStaleness(feed, newMaxStaleness);
    307
        }
    308
    
                                                    
                                                
    309
        /// @inheritdoc ISuperGovernor
    310
        function setOracleFeedMaxStalenessBatch(
    311
            address[] calldata feeds_,
    312
            uint256[] calldata newMaxStalenessList_
    313
        )
    314
            external
    315
            onlyRole(_GOVERNOR_ROLE)
    316
        {
    317
            // Validate all staleness values before proceeding
    318
            for (uint256 i; i < newMaxStalenessList_.length; i++) {
    319
                if (newMaxStalenessList_[i] < _minStaleness) revert MAX_STALENESS_TOO_LOW();
    320
            }
    321
    
                                                    
                                                
    322
            address oracle = _addressRegistry[SUPER_ORACLE];
    323
            if (oracle == address(0)) revert CONTRACT_NOT_FOUND();
    324
    
                                                    
                                                
    325
            ISuperOracle(oracle).setFeedMaxStalenessBatch(feeds_, newMaxStalenessList_);
    326
        }
    327
    
                                                    
                                                
    328
        /// @inheritdoc ISuperGovernor
    329
        function queueOracleUpdate(
    330
            address[] calldata bases_,
    331
            address[] calldata quotes_,
    332
            bytes32[] calldata providers_,
    333
            address[] calldata feeds_
    334
        )
    335
            external
    336
            onlyRole(_GOVERNOR_ROLE)
    337
        {
    338
            address oracle = _addressRegistry[SUPER_ORACLE];
    339
            if (oracle == address(0)) revert CONTRACT_NOT_FOUND();
    340
    
                                                    
                                                
    341
            ISuperOracle(oracle).queueOracleUpdate(bases_, quotes_, providers_, feeds_);
    342
        }
    343
    
                                                    
                                                
    344
        /// @inheritdoc ISuperGovernor
    345
        function queueOracleProviderRemoval(bytes32[] calldata providers) external onlyRole(_GOVERNOR_ROLE) {
    346
            address oracle = _addressRegistry[SUPER_ORACLE];
    347
            if (oracle == address(0)) revert CONTRACT_NOT_FOUND();
    348
    
                                                    
                                                
    349
            ISuperOracle(oracle).queueProviderRemoval(providers);
    350
        }
    351
    
                                                    
                                                
    352
        /// @inheritdoc ISuperGovernor
    353
        function batchSetOracleUptimeFeed(
    354
            address[] calldata dataOracles_,
    355
            address[] calldata uptimeOracles_,
    356
            uint256[] calldata gracePeriods_
    357
        )
    358
            external
    359
            onlyRole(_GOVERNOR_ROLE)
    360
        {
    361
            address oracleL2 = _addressRegistry[SUPER_ORACLE];
    362
            if (oracleL2 == address(0)) revert CONTRACT_NOT_FOUND();
    363
    
                                                    
                                                
    364
            ISuperOracleL2(oracleL2).batchSetUptimeFeed(dataOracles_, uptimeOracles_, gracePeriods_);
    365
        }
    366
    
                                                    
                                                
    367
        /// @inheritdoc ISuperGovernor
    368
        function setEmergencyPrice(address token, uint256 price) external onlyRole(_GOVERNOR_ROLE) {
    369
            address oracle = _addressRegistry[SUPER_ORACLE];
    370
            if (oracle == address(0)) revert CONTRACT_NOT_FOUND();
    371
    
                                                    
                                                
    372
            ISuperOracle(oracle).setEmergencyPrice(token, price);
    373
        }
    374
    
                                                    
                                                
    375
        /// @inheritdoc ISuperGovernor
    376
        function batchSetEmergencyPrices(
    377
            address[] calldata tokens_,
    378
            uint256[] calldata prices_
    379
        )
    380
            external
    381
            onlyRole(_GOVERNOR_ROLE)
    382
        {
    383
            address oracle = _addressRegistry[SUPER_ORACLE];
    384
            if (oracle == address(0)) revert CONTRACT_NOT_FOUND();
    385
    
                                                    
                                                
    386
            ISuperOracle(oracle).batchSetEmergencyPrice(tokens_, prices_);
    387
        }
    388
    
                                                    
                                                
    389
        /*//////////////////////////////////////////////////////////////
    390
                                HOOK MANAGEMENT
    391
        //////////////////////////////////////////////////////////////*/
    392
        /// @inheritdoc ISuperGovernor
    393
        function registerHook(address hook, bool isFulfillRequestsHook) external onlyRole(_GOVERNOR_ROLE) {
    394
    ✓ 18
            if (hook == address(0)) revert INVALID_ADDRESS();
    395
    
                                                    
                                                
    396
    ✓ 18
            if (isFulfillRequestsHook && _registeredFulfillRequestsHooks.add(hook)) {
    397
    ✓ 5
                emit FulfillRequestsHookRegistered(hook);
    398
            }
    399
    ✓ 18
            if (_registeredHooks.add(hook)) {
    400
    ✓ 18
                emit HookApproved(hook);
    401
            }
    402
        }
    403
    
                                                    
                                                
    404
        /// @inheritdoc ISuperGovernor
    405
        function unregisterHook(address hook) external onlyRole(_GOVERNOR_ROLE) {
    406
            if (_registeredFulfillRequestsHooks.remove(hook)) {
    407
                emit FulfillRequestsHookUnregistered(hook);
    408
            }
    409
            if (_registeredHooks.remove(hook)) {
    410
                // Clear merkle root data for the unregistered hook to prevent stale data
    411
                delete superBankHooksMerkleRoots[hook];
    412
                delete vaultBankHooksMerkleRoots[hook];
    413
    
                                                    
                                                
    414
                emit HookRemoved(hook);
    415
            }
    416
        }
    417
    
                                                    
                                                
    418
        /*//////////////////////////////////////////////////////////////
    419
                            EXECUTORS MANAGEMENT
    420
        //////////////////////////////////////////////////////////////*/
    421
        /// @inheritdoc ISuperGovernor
    422
        function addExecutor(address executor) external onlyRole(_GOVERNOR_ROLE) {
    423
            if (executor == address(0)) revert INVALID_ADDRESS();
    424
            if (!_executors.add(executor)) revert EXECUTOR_ALREADY_REGISTERED();
    425
    
                                                    
                                                
    426
            emit ExecutorAdded(executor);
    427
        }
    428
    
                                                    
                                                
    429
        /// @inheritdoc ISuperGovernor
    430
        function removeExecutor(address executor) external onlyRole(_GOVERNOR_ROLE) {
    431
            if (!_executors.remove(executor)) revert EXECUTOR_NOT_REGISTERED();
    432
    
                                                    
                                                
    433
            emit ExecutorRemoved(executor);
    434
        }
    435
    
                                                    
                                                
    436
        /*//////////////////////////////////////////////////////////////
    437
                              RELAYER MANAGEMENT
    438
        //////////////////////////////////////////////////////////////*/
    439
        /// @inheritdoc ISuperGovernor
    440
        function addRelayer(address relayer) external onlyRole(_GOVERNOR_ROLE) {
    441
            if (relayer == address(0)) revert INVALID_ADDRESS();
    442
            if (!_relayers.add(relayer)) revert RELAYER_ALREADY_REGISTERED();
    443
    
                                                    
                                                
    444
            emit RelayerAdded(relayer);
    445
        }
    446
    
                                                    
                                                
    447
        /// @inheritdoc ISuperGovernor
    448
        function removeRelayer(address relayer) external onlyRole(_GOVERNOR_ROLE) {
    449
            if (!_relayers.remove(relayer)) revert RELAYER_NOT_REGISTERED();
    450
    
                                                    
                                                
    451
            emit RelayerRemoved(relayer);
    452
        }
    453
    
                                                    
                                                
    454
        /*//////////////////////////////////////////////////////////////
    455
                            VALIDATOR MANAGEMENT
    456
        //////////////////////////////////////////////////////////////*/
    457
        /// @inheritdoc ISuperGovernor
    458
        function addValidator(address validator) external onlyRole(_GOVERNOR_ROLE) {
    459
            if (validator == address(0)) revert INVALID_ADDRESS();
    460
            if (!_validators.add(validator)) revert VALIDATOR_ALREADY_REGISTERED();
    461
    
                                                    
                                                
    462
            emit ValidatorAdded(validator);
    463
        }
    464
    
                                                    
                                                
    465
        /// @inheritdoc ISuperGovernor
    466
        function removeValidator(address validator) external onlyRole(_GOVERNOR_ROLE) {
    467
            if (!_validators.remove(validator)) revert VALIDATOR_NOT_REGISTERED();
    468
    
                                                    
                                                
    469
            emit ValidatorRemoved(validator);
    470
        }
    471
    
                                                    
                                                
    472
        /*//////////////////////////////////////////////////////////////
    473
                             PPS ORACLE MANAGEMENT
    474
        //////////////////////////////////////////////////////////////*/
    475
        /// @inheritdoc ISuperGovernor
    476
        function setActivePPSOracle(address oracle) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    477
    ✓ 1
            if (oracle == address(0)) revert INVALID_ADDRESS();
    478
    
                                                    
                                                
    479
            // If this is the first oracle or replacing a zero oracle, set it immediately
    480
    ✓ 1
            if (_activePPSOracle == address(0)) {
    481
    ✓ 1
                _activePPSOracle = oracle;
    482
    ✓ 1
                emit ActivePPSOracleSet(oracle);
    483
            } else {
    484
                // Otherwise require the timelock process
    485
                revert MUST_USE_TIMELOCK_FOR_CHANGE();
    486
            }
    487
        }
    488
    
                                                    
                                                
    489
        /// @inheritdoc ISuperGovernor
    490
        function proposeActivePPSOracle(address oracle) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    491
            if (oracle == address(0)) revert INVALID_ADDRESS();
    492
    
                                                    
                                                
    493
            _proposedActivePPSOracle = oracle;
    494
            _activePPSOracleEffectiveTime = block.timestamp + TIMELOCK;
    495
    
                                                    
                                                
    496
    ✓ 1.9K
            emit ActivePPSOracleProposed(oracle, _activePPSOracleEffectiveTime);
    497
        }
    498
    
                                                    
                                                
    499
        /// @inheritdoc ISuperGovernor
    500
        function executeActivePPSOracleChange() external {
    501
            if (_proposedActivePPSOracle == address(0)) revert NO_PROPOSED_PPS_ORACLE();
    502
    
                                                    
                                                
    503
            if (block.timestamp < _activePPSOracleEffectiveTime) {
    504
                revert TIMELOCK_NOT_EXPIRED();
    505
            }
    506
    
                                                    
                                                
    507
            address oldOracle = _activePPSOracle;
    508
            _activePPSOracle = _proposedActivePPSOracle;
    509
    
                                                    
                                                
    510
            // Reset proposal data
    511
            _proposedActivePPSOracle = address(0);
    512
            _activePPSOracleEffectiveTime = 0;
    513
    
                                                    
                                                
    514
            emit ActivePPSOracleChanged(oldOracle, _activePPSOracle);
    515
        }
    516
    
                                                    
                                                
    517
        /// @inheritdoc ISuperGovernor
    518
        function setPPSOracleQuorum(uint256 quorum) external onlyRole(_GOVERNOR_ROLE) {
    519
            if (quorum == 0) revert INVALID_QUORUM();
    520
    
                                                    
                                                
    521
            _activePPSOracleQuorum = quorum;
    522
            emit PPSOracleQuorumUpdated(quorum);
    523
        }
    524
    
                                                    
                                                
    525
        /*//////////////////////////////////////////////////////////////
    526
                          REVENUE SHARE MANAGEMENT
    527
        //////////////////////////////////////////////////////////////*/
    528
        /// @inheritdoc ISuperGovernor
    529
        function proposeFee(FeeType feeType, uint256 value) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    530
    ✓ 142.0K
    ⟲ 121.2K
            if (value > BPS_MAX) revert INVALID_FEE_VALUE();
    531
    
                                                    
                                                
    532
    ✓ 20.8K
            _proposedFeeValues[feeType] = value;
    533
    ✓ 20.8K
            _feeEffectiveTimes[feeType] = block.timestamp + TIMELOCK;
    534
    
                                                    
                                                
    535
    ✓ 20.8K
            emit FeeProposed(feeType, value, _feeEffectiveTimes[feeType]);
    536
        }
    537
    
                                                    
                                                
    538
        /// @inheritdoc ISuperGovernor
    539
        function executeFeeUpdate(FeeType feeType) external {
    540
    ✓ 118.0K
            uint256 effectiveTime = _feeEffectiveTimes[feeType];
    541
    ✓ 118.0K
            if (effectiveTime == 0) revert NO_PROPOSED_FEE(feeType);
    542
    ✓ 2.0K
            if (block.timestamp < effectiveTime) {
    543
    ✓ 142
    ⟲ 142
                revert TIMELOCK_NOT_EXPIRED();
    544
            }
    545
    
                                                    
                                                
    546
            // Update the fee value
    547
    ✓ 1.9K
            _feeValues[feeType] = _proposedFeeValues[feeType];
    548
    
                                                    
                                                
    549
            // Reset proposal data
    550
    ✓ 1.9K
            delete _proposedFeeValues[feeType];
    551
    ✓ 1.9K
            delete _feeEffectiveTimes[feeType];
    552
    
                                                    
                                                
    553
    ✓ 1.9K
            emit FeeUpdated(feeType, _feeValues[feeType]);
    554
        }
    555
    
                                                    
                                                
    556
        /// @inheritdoc ISuperGovernor
    557
        function executeUpkeepClaim(uint256 amount) external onlyRole(_GOVERNOR_ROLE) {
    558
    ✓ 148.0K
            address aggregator = _addressRegistry[SUPER_VAULT_AGGREGATOR];
    559
            if (aggregator == address(0)) revert CONTRACT_NOT_FOUND();
    560
    
                                                    
                                                
    561
    ✓ 148.0K
            ISuperVaultAggregator(aggregator).claimUpkeep(amount);
    562
        }
    563
    
                                                    
                                                
    564
        /*//////////////////////////////////////////////////////////////
    565
                            UPKEEP COST MANAGEMENT
    566
        //////////////////////////////////////////////////////////////*/
    567
        /// @inheritdoc ISuperGovernor
    568
        function setGasInfo(address oracle, uint256 baseGasBatch, uint256 gasIncreasePerEntryBatch) external onlyRole(_GAS_MANAGER_ROLE) {
    569
            if (oracle == address(0)) revert INVALID_ADDRESS();
    570
            if (baseGasBatch == 0 || gasIncreasePerEntryBatch == 0) revert INVALID_GAS_INFO();
    571
    
                                                    
                                                
    572
            _oracleGasInfo[oracle] = GasInfo({baseGasBatch: baseGasBatch, gasIncreasePerEntryBatch: gasIncreasePerEntryBatch});
    573
            emit GasInfoSet(oracle, baseGasBatch, gasIncreasePerEntryBatch);
    574
        }
    575
    
                                                    
                                                
    576
        /// @notice Proposes a change to the upkeep payments enabled status
    577
        /// @param enabled The proposed new status for upkeep payments
    578
        function proposeUpkeepPaymentsChange(bool enabled) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    579
    ✓ 225.7K
            _proposedUpkeepPaymentsEnabled = enabled;
    580
    ✓ 225.7K
            _upkeepPaymentsChangeEffectiveTime = block.timestamp + TIMELOCK;
    581
    
                                                    
                                                
    582
    ✓ 225.7K
            emit UpkeepPaymentsChangeProposed(enabled, _upkeepPaymentsChangeEffectiveTime);
    583
        }
    584
    
                                                    
                                                
    585
        /// @notice Executes a previously proposed change to upkeep payments status after timelock expires
    586
        function executeUpkeepPaymentsChange() external {
    587
    ✓ 165.7K
    ⟲ 120.6K
            if (_upkeepPaymentsChangeEffectiveTime == 0) revert NO_PENDING_CHANGE();
    588
    ✓ 45.1K
    ⟲ 9.1K
            if (block.timestamp < _upkeepPaymentsChangeEffectiveTime) revert TIMELOCK_NOT_EXPIRED();
    589
    
                                                    
                                                
    590
    ✓ 36.0K
            _upkeepPaymentsEnabled = _proposedUpkeepPaymentsEnabled;
    591
    ✓ 36.0K
            _upkeepPaymentsChangeEffectiveTime = 0;
    592
    ✓ 36.0K
            _proposedUpkeepPaymentsEnabled = false;
    593
    
                                                    
                                                
    594
    ✓ 36.0K
            emit UpkeepPaymentsChanged(_upkeepPaymentsEnabled);
    595
        }
    596
    
                                                    
                                                
    597
        /*//////////////////////////////////////////////////////////////
    598
                            MIN STALENESS MANAGEMENT
    599
        //////////////////////////////////////////////////////////////*/
    600
        /// @inheritdoc ISuperGovernor
    601
        function proposeMinStaleness(uint256 newMinStaleness) external onlyRole(_SUPER_GOVERNOR_ROLE) {
    602
    ✓ 105.4K
            _proposedMinStaleness = newMinStaleness;
    603
    ✓ 105.4K
            _minStalenesEffectiveTime = block.timestamp + TIMELOCK;
    604
    
                                                    
                                                
    605
    ✓ 105.4K
            emit MinStalenesProposed(newMinStaleness, _minStalenesEffectiveTime);
    606
        }
    607
    
                                                    
                                                
    608
        /// @inheritdoc ISuperGovernor
    609
        function executeMinStalenesChange() external {
    610
    ✓ 170.2K
            uint256 minStalenesEffectiveTime = _minStalenesEffectiveTime;
    611
    ✓ 170.2K
    ⟲ 149.1K
            if (minStalenesEffectiveTime == 0) revert NO_PROPOSED_MIN_STALENESS();
    612
    ✓ 21.1K
    ⟲ 1.4K
            if (block.timestamp < minStalenesEffectiveTime) revert TIMELOCK_NOT_EXPIRED();
    613
    
                                                    
                                                
    614
    ✓ 19.8K
            _minStaleness = _proposedMinStaleness;
    615
    
                                                    
                                                
    616
            // Reset proposal data
    617
    ✓ 19.8K
            _proposedMinStaleness = 0;
    618
    ✓ 19.8K
            _minStalenesEffectiveTime = 0;
    619
    
                                                    
                                                
    620
    ✓ 19.8K
            emit MinStalenesChanged(_minStaleness);
    621
        }
    622
    
                                                    
                                                
    623
        /*//////////////////////////////////////////////////////////////
    624
                          SUPERFORM MANAGER MANAGEMENT
    625
        //////////////////////////////////////////////////////////////*/
    626
        /// @inheritdoc ISuperGovernor
    627
        function addSuperformManager(address manager) external onlyRole(_GOVERNOR_ROLE) {
    628
            if (manager == address(0)) revert INVALID_ADDRESS();
    629
            if (!_superformManagers.add(manager)) revert MANAGER_ALREADY_REGISTERED();
    630
    
                                                    
                                                
    631
            emit SuperformManagerAdded(manager);
    632
        }
    633
    
                                                    
                                                
    634
        /// @inheritdoc ISuperGovernor
    635
        function removeSuperformManager(address manager) external onlyRole(_GOVERNOR_ROLE) {
    636
            if (!_superformManagers.remove(manager)) revert MANAGER_NOT_REGISTERED();
    637
    
                                                    
                                                
    638
            emit SuperformManagerRemoved(manager);
    639
        }
    640
    
                                                    
                                                
    641
        /*//////////////////////////////////////////////////////////////
    642
                               VAULT HOOKS MGMT
    643
        //////////////////////////////////////////////////////////////*/
    644
    
                                                    
                                                
    645
        /// @inheritdoc ISuperGovernor
    646
        function proposeVaultBankHookMerkleRoot(address hook, bytes32 proposedRoot) external onlyRole(_GOVERNOR_ROLE) {
    647
            if (!_registeredHooks.contains(hook)) revert HOOK_NOT_APPROVED();
    648
            if (proposedRoot == bytes32(0)) revert ZERO_PROPOSED_MERKLE_ROOT();
    649
    
                                                    
                                                
    650
            uint256 effectiveTime = block.timestamp + TIMELOCK;
    651
            ISuperGovernor.HookMerkleRootData storage data = vaultBankHooksMerkleRoots[hook];
    652
            data.proposedRoot = proposedRoot;
    653
            data.effectiveTime = effectiveTime;
    654
    
                                                    
                                                
    655
            emit VaultBankHookMerkleRootProposed(hook, proposedRoot, effectiveTime);
    656
        }
    657
    
                                                    
                                                
    658
        /// @inheritdoc ISuperGovernor
    659
        function executeVaultBankHookMerkleRootUpdate(address hook) external {
    660
            if (!_registeredHooks.contains(hook)) revert HOOK_NOT_APPROVED();
    661
    
                                                    
                                                
    662
            ISuperGovernor.HookMerkleRootData storage data = vaultBankHooksMerkleRoots[hook];
    663
    
                                                    
                                                
    664
            // Check if there's a proposed update
    665
            bytes32 proposedRoot = data.proposedRoot;
    666
            if (proposedRoot == bytes32(0)) revert NO_PROPOSED_MERKLE_ROOT();
    667
    
                                                    
                                                
    668
            // Check if the effective time has passed
    669
            if (block.timestamp < data.effectiveTime) revert TIMELOCK_NOT_EXPIRED();
    670
    
                                                    
                                                
    671
            // Update the Merkle root
    672
            data.currentRoot = proposedRoot;
    673
    
                                                    
                                                
    674
            // Reset the proposal
    675
            data.proposedRoot = bytes32(0);
    676
            data.effectiveTime = 0;
    677
    
                                                    
                                                
    678
            emit VaultBankHookMerkleRootUpdated(hook, proposedRoot);
    679
        }
    680
    
                                                    
                                                
    681
        /*//////////////////////////////////////////////////////////////
    682
                               SUPERBANK HOOKS MGMT
    683
        //////////////////////////////////////////////////////////////*/
    684
        /// @inheritdoc ISuperGovernor
    685
        function proposeSuperBankHookMerkleRoot(address hook, bytes32 proposedRoot) external onlyRole(_GOVERNOR_ROLE) {
    686
            if (!_registeredHooks.contains(hook)) revert HOOK_NOT_APPROVED();
    687
            if (proposedRoot == bytes32(0)) revert ZERO_PROPOSED_MERKLE_ROOT();
    688
    
                                                    
                                                
    689
            uint256 effectiveTime = block.timestamp + TIMELOCK;
    690
            ISuperGovernor.HookMerkleRootData storage data = superBankHooksMerkleRoots[hook];
    691
            data.proposedRoot = proposedRoot;
    692
            data.effectiveTime = effectiveTime;
    693
    
                                                    
                                                
    694
            emit SuperBankHookMerkleRootProposed(hook, proposedRoot, effectiveTime);
    695
        }
    696
    
                                                    
                                                
    697
        /// @inheritdoc ISuperGovernor
    698
        function executeSuperBankHookMerkleRootUpdate(address hook) external {
    699
            if (!_registeredHooks.contains(hook)) revert HOOK_NOT_APPROVED();
    700
    
                                                    
                                                
    701
            ISuperGovernor.HookMerkleRootData storage data = superBankHooksMerkleRoots[hook];
    702
    
                                                    
                                                
    703
            // Check if there's a proposed update
    704
            bytes32 proposedRoot = data.proposedRoot;
    705
            if (proposedRoot == bytes32(0)) revert NO_PROPOSED_MERKLE_ROOT();
    706
    
                                                    
                                                
    707
            // Check if the effective time has passed
    708
            if (block.timestamp < data.effectiveTime) revert TIMELOCK_NOT_EXPIRED();
    709
    
                                                    
                                                
    710
            // Update the Merkle root
    711
            data.currentRoot = proposedRoot;
    712
    
                                                    
                                                
    713
            // Reset the proposal
    714
            data.proposedRoot = bytes32(0);
    715
            data.effectiveTime = 0;
    716
    
                                                    
                                                
    717
            emit SuperBankHookMerkleRootUpdated(hook, proposedRoot);
    718
        }
    719
    
                                                    
                                                
    720
        /*//////////////////////////////////////////////////////////////
    721
                            VAULT BANK MANAGEMENT
    722
        //////////////////////////////////////////////////////////////*/
    723
        /// @inheritdoc ISuperGovernor
    724
        function addVaultBank(uint64 chainId, address vaultBank) external onlyRole(_GOVERNOR_ROLE) {
    725
            if (chainId == 0) revert INVALID_CHAIN_ID();
    726
            if (vaultBank == address(0)) revert INVALID_ADDRESS();
    727
    
                                                    
                                                
    728
            if (_vaultBanksByChainId[chainId] != address(0)) {
    729
                _vaultBanks.remove(_vaultBanksByChainId[chainId]);
    730
            }
    731
    
                                                    
                                                
    732
            _vaultBanks.add(vaultBank);
    733
            _vaultBanksByChainId[chainId] = vaultBank;
    734
    
                                                    
                                                
    735
            emit VaultBankAddressAdded(chainId, vaultBank);
    736
        }
    737
    
                                                    
                                                
    738
        /// @inheritdoc ISuperGovernor
    739
        function getVaultBank(uint64 chainId) external view returns (address) {
    740
            return _vaultBanksByChainId[chainId];
    741
        }
    742
    
                                                    
                                                
    743
        /*//////////////////////////////////////////////////////////////
    744
                          INCENTIVE TOKEN MANAGEMENT
    745
        //////////////////////////////////////////////////////////////*/
    746
        /// @inheritdoc ISuperGovernor
    747
        function proposeAddIncentiveTokens(address[] memory tokens) external onlyRole(_GOVERNOR_ROLE) {
    748
    ✓ 5.7M
            for (uint256 i; i < tokens.length; i++) {
    749
    ✓ 5.6M
    ⟲ 79.2K
                if (tokens[i] == address(0)) revert INVALID_ADDRESS();
    750
    ✓ 5.5M
                _proposedWhitelistedIncentiveTokens.add(tokens[i]);
    751
            }
    752
    
                                                    
                                                
    753
    ✓ 144.1K
            _proposedAddWhitelistedIncentiveTokensEffectiveTime = block.timestamp + TIMELOCK;
    754
    
                                                    
                                                
    755
            emit WhitelistedIncentiveTokensProposed(
    756
    ✓ 144.1K
                _proposedWhitelistedIncentiveTokens.values(), _proposedAddWhitelistedIncentiveTokensEffectiveTime
    757
            );
    758
        }
    759
    
                                                    
                                                
    760
        /// @inheritdoc ISuperGovernor
    761
        function executeAddIncentiveTokens() external {
    762
            if (
    763
    ✓ 101.2K
                _proposedAddWhitelistedIncentiveTokensEffectiveTime == 0
    764
    ✓ 21.2K
                    || block.timestamp < _proposedAddWhitelistedIncentiveTokensEffectiveTime
    765
    ✓ 80.7K
    ⟲ 80.7K
            ) revert TIMELOCK_NOT_EXPIRED();
    766
    
                                                    
                                                
    767
            // Get all proposed tokens before modifying the set
    768
    ✓ 20.5K
            address[] memory tokensToAdd = _proposedWhitelistedIncentiveTokens.values();
    769
    ✓ 20.5K
            uint256 len = tokensToAdd.length;
    770
            address token;
    771
    ✓ 522.4K
            for (uint256 i; i < len; i++) {
    772
    ✓ 501.9K
                token = tokensToAdd[i];
    773
    ✓ 501.9K
                _isWhitelistedIncentiveToken[token] = true;
    774
                // Remove from proposed whitelisted tokens
    775
    ✓ 501.9K
                _proposedWhitelistedIncentiveTokens.remove(token);
    776
            }
    777
    
                                                    
                                                
    778
            // Emit event once with all tokens
    779
    ✓ 20.5K
            emit WhitelistedIncentiveTokensAdded(tokensToAdd);
    780
    
                                                    
                                                
    781
            // Reset proposal timestamp
    782
    ✓ 20.5K
            _proposedAddWhitelistedIncentiveTokensEffectiveTime = 0;
    783
        }
    784
    
                                                    
                                                
    785
        /// @inheritdoc ISuperGovernor
    786
        function proposeRemoveIncentiveTokens(address[] memory tokens) external onlyRole(_GOVERNOR_ROLE) {
    787
            for (uint256 i; i < tokens.length; i++) {
    788
                if (tokens[i] == address(0)) revert INVALID_ADDRESS();
    789
                if (!_isWhitelistedIncentiveToken[tokens[i]]) revert NOT_WHITELISTED_INCENTIVE_TOKEN();
    790
    
                                                    
                                                
    791
                _proposedRemoveWhitelistedIncentiveTokens.add(tokens[i]);
    792
            }
    793
    
                                                    
                                                
    794
            _proposedRemoveWhitelistedIncentiveTokensEffectiveTime = block.timestamp + TIMELOCK;
    795
    
                                                    
                                                
    796
            emit WhitelistedIncentiveTokensProposed(
    797
                _proposedRemoveWhitelistedIncentiveTokens.values(), _proposedRemoveWhitelistedIncentiveTokensEffectiveTime
    798
            );
    799
        }
    800
    
                                                    
                                                
    801
        /// @inheritdoc ISuperGovernor
    802
        function executeRemoveIncentiveTokens() external {
    803
            if (
    804
    ✓ 98.8K
                _proposedRemoveWhitelistedIncentiveTokensEffectiveTime == 0
    805
                    || block.timestamp < _proposedRemoveWhitelistedIncentiveTokensEffectiveTime
    806
    ✓ 98.8K
    ⟲ 98.8K
            ) revert TIMELOCK_NOT_EXPIRED();
    807
    
                                                    
                                                
    808
            // Get all proposed tokens before modifying the set
    809
            address[] memory tokensToRemove = _proposedRemoveWhitelistedIncentiveTokens.values();
    810
            uint256 len = tokensToRemove.length;
    811
            address token;
    812
            for (uint256 i; i < len; i++) {
    813
                token = tokensToRemove[i];
    814
                if (_isWhitelistedIncentiveToken[token]) {
    815
                    _isWhitelistedIncentiveToken[token] = false;
    816
                }
    817
                // Remove from proposed whitelisted tokens to be removed
    818
                _proposedRemoveWhitelistedIncentiveTokens.remove(token);
    819
            }
    820
    
                                                    
                                                
    821
            // Emit event once with all tokens
    822
            emit WhitelistedIncentiveTokensRemoved(tokensToRemove);
    823
    
                                                    
                                                
    824
            // Reset proposal timestamp
    825
            _proposedRemoveWhitelistedIncentiveTokensEffectiveTime = 0;
    826
        }
    827
    
                                                    
                                                
    828
        /*//////////////////////////////////////////////////////////////
    829
                             EXTERNAL VIEW FUNCTIONS
    830
        //////////////////////////////////////////////////////////////*/
    831
        /// @inheritdoc ISuperGovernor
    832
        function SUPER_GOVERNOR_ROLE() external pure returns (bytes32) {
    833
            return _SUPER_GOVERNOR_ROLE;
    834
        }
    835
    
                                                    
                                                
    836
        /// @inheritdoc ISuperGovernor
    837
        function GOVERNOR_ROLE() external pure returns (bytes32) {
    838
            return _GOVERNOR_ROLE;
    839
        }
    840
    
                                                    
                                                
    841
        /// @inheritdoc ISuperGovernor
    842
        function BANK_MANAGER_ROLE() external pure returns (bytes32) {
    843
            return _BANK_MANAGER_ROLE;
    844
        }
    845
    
                                                    
                                                
    846
        /// @inheritdoc ISuperGovernor
    847
        function GAS_MANAGER_ROLE() external pure returns (bytes32) {
    848
            return _GAS_MANAGER_ROLE;
    849
        }
    850
    
                                                    
                                                
    851
        /// @inheritdoc ISuperGovernor
    852
        function GUARDIAN_ROLE() external pure returns (bytes32) {
    853
            return _GUARDIAN_ROLE;
    854
        }
    855
    
                                                    
                                                
    856
        /// @inheritdoc ISuperGovernor
    857
        function SUPER_ASSET_FACTORY() external pure returns (bytes32) {
    858
            return _SUPER_ASSET_FACTORY;
    859
        }
    860
    
                                                    
                                                
    861
        /// @inheritdoc ISuperGovernor
    862
    ✓ 20.1M
        function getAddress(bytes32 key) external view returns (address) {
    863
    ✓ 20.1M
            address value = _addressRegistry[key];
    864
            if (value == address(0)) revert CONTRACT_NOT_FOUND();
    865
            return value;
    866
        }
    867
    
                                                    
                                                
    868
        /// @inheritdoc ISuperGovernor
    869
        function isManagerTakeoverFrozen() external view returns (bool) {
    870
            return _managerTakeoversFrozen;
    871
        }
    872
    
                                                    
                                                
    873
        /// @inheritdoc ISuperGovernor
    874
    ✓ 127.3K
        function isHookRegistered(address hook) external view returns (bool) {
    875
    ✓ 127.3K
            return _registeredHooks.contains(hook);
    876
        }
    877
    
                                                    
                                                
    878
        /// @inheritdoc ISuperGovernor
    879
    ✓ 602.7K
        function isFulfillRequestsHookRegistered(address hook) external view returns (bool) {
    880
    ✓ 602.7K
            return _registeredFulfillRequestsHooks.contains(hook);
    881
        }
    882
    
                                                    
                                                
    883
        /// @inheritdoc ISuperGovernor
    884
        function getRegisteredHooks() external view returns (address[] memory) {
    885
            return _registeredHooks.values();
    886
        }
    887
    
                                                    
                                                
    888
        /// @inheritdoc ISuperGovernor
    889
        function getRegisteredFulfillRequestsHooks() external view returns (address[] memory) {
    890
            return _registeredFulfillRequestsHooks.values();
    891
        }
    892
    
                                                    
                                                
    893
        /// @inheritdoc ISuperGovernor
    894
        function isValidator(address validator) external view returns (bool) {
    895
            return _validators.contains(validator);
    896
        }
    897
    
                                                    
                                                
    898
        /// @inheritdoc ISuperGovernor
    899
        function isGuardian(address guardian) external view returns (bool) {
    900
            return hasRole(_GUARDIAN_ROLE, guardian);
    901
        }
    902
    
                                                    
                                                
    903
        /// @inheritdoc ISuperGovernor
    904
        function isRelayer(address relayer) external view returns (bool) {
    905
            return _relayers.contains(relayer);
    906
        }
    907
    
                                                    
                                                
    908
        /// @inheritdoc ISuperGovernor
    909
        function isExecutor(address executor) external view returns (bool) {
    910
            return _executors.contains(executor);
    911
        }
    912
    
                                                    
                                                
    913
        /// @inheritdoc ISuperGovernor
    914
        function getValidators() external view returns (address[] memory) {
    915
            return _validators.values();
    916
        }
    917
    
                                                    
                                                
    918
        /// @inheritdoc ISuperGovernor
    919
        function getRelayers() external view returns (address[] memory) {
    920
            return _relayers.values();
    921
        }
    922
    
                                                    
                                                
    923
        /// @inheritdoc ISuperGovernor
    924
        function getExecutors() external view returns (address[] memory) {
    925
            return _executors.values();
    926
        }
    927
    
                                                    
                                                
    928
        /// @inheritdoc ISuperGovernor
    929
        function getProposedActivePPSOracle() external view returns (address proposedOracle, uint256 effectiveTime) {
    930
            return (_proposedActivePPSOracle, _activePPSOracleEffectiveTime);
    931
        }
    932
    
                                                    
                                                
    933
        /// @inheritdoc ISuperGovernor
    934
        function getPPSOracleQuorum() external view returns (uint256) {
    935
            return _activePPSOracleQuorum;
    936
        }
    937
    
                                                    
                                                
    938
        /// @inheritdoc ISuperGovernor
    939
        function getActivePPSOracle() external view returns (address) {
    940
            if (_activePPSOracle == address(0)) revert NO_ACTIVE_PPS_ORACLE();
    941
            return _activePPSOracle;
    942
        }
    943
    
                                                    
                                                
    944
        /// @inheritdoc ISuperGovernor
    945
        function isActivePPSOracle(address oracle) external view returns (bool) {
    946
    ✓ 529.1K
            return oracle == _activePPSOracle;
    947
        }
    948
    
                                                    
                                                
    949
        /// @inheritdoc ISuperGovernor
    950
    ✓ 253.0K
        function getFee(FeeType feeType) external view returns (uint256) {
    951
    ✓ 253.0K
            return _feeValues[feeType];
    952
        }
    953
    
                                                    
                                                
    954
        /// @inheritdoc ISuperGovernor
    955
        function getGasInfo(address oracle_) external view returns (GasInfo memory) {
    956
            return _oracleGasInfo[oracle_];
    957
        }
    958
    
                                                    
                                                
    959
         /// @inheritdoc ISuperGovernor
    960
    ✓ 7.6K
        function getUpkeepCostPerBatchUpdate(address oracle_, uint256 chargeableEntries_) external view returns (uint256) {
    961
            // Calculate total gas cost
    962
    ✓ 7.6K
            uint256 totalGas = _oracleGasInfo[oracle_].baseGasBatch + 
    963
    ✓ 7.6K
                (_oracleGasInfo[oracle_].gasIncreasePerEntryBatch * chargeableEntries_);
    964
    
                                                    
                                                
    965
    ✓ 7.6K
            return _convertGasToUp(totalGas);
    966
        }
    967
    
                                                    
                                                
    968
        /// @inheritdoc ISuperGovernor
    969
        function getMinStaleness() external view returns (uint256) {
    970
    ✓ 81.3K
            return _minStaleness;
    971
        }
    972
    
                                                    
                                                
    973
        /// @inheritdoc ISuperGovernor
    974
        function getProposedMinStaleness() external view returns (uint256 proposedMinStaleness, uint256 effectiveTime) {
    975
            return (_proposedMinStaleness, _minStalenesEffectiveTime);
    976
        }
    977
    
                                                    
                                                
    978
        /// @inheritdoc ISuperGovernor
    979
        function getSuperBankHookMerkleRoot(address hook) external view returns (bytes32) {
    980
            if (!_registeredHooks.contains(hook)) revert HOOK_NOT_APPROVED();
    981
            return superBankHooksMerkleRoots[hook].currentRoot;
    982
        }
    983
    
                                                    
                                                
    984
        /// @inheritdoc ISuperGovernor
    985
        function getVaultBankHookMerkleRoot(address hook) external view returns (bytes32) {
    986
            if (!_registeredHooks.contains(hook)) revert HOOK_NOT_APPROVED();
    987
            return vaultBankHooksMerkleRoots[hook].currentRoot;
    988
        }
    989
    
                                                    
                                                
    990
        /// @inheritdoc ISuperGovernor
    991
        function getProposedSuperBankHookMerkleRoot(address hook)
    992
            external
    993
            view
    994
            returns (bytes32 proposedRoot, uint256 effectiveTime)
    995
        {
    996
            if (!_registeredHooks.contains(hook)) revert HOOK_NOT_APPROVED();
    997
            ISuperGovernor.HookMerkleRootData storage data = superBankHooksMerkleRoots[hook];
    998
            return (data.proposedRoot, data.effectiveTime);
    999
        }
    1000
    
                                                    
                                                
    1001
        /// @inheritdoc ISuperGovernor
    1002
        function getProposedVaultBankHookMerkleRoot(address hook)
    1003
            external
    1004
            view
    1005
            returns (bytes32 proposedRoot, uint256 effectiveTime)
    1006
        {
    1007
            if (!_registeredHooks.contains(hook)) revert HOOK_NOT_APPROVED();
    1008
            ISuperGovernor.HookMerkleRootData storage data = vaultBankHooksMerkleRoots[hook];
    1009
            return (data.proposedRoot, data.effectiveTime);
    1010
        }
    1011
    
                                                    
                                                
    1012
        /// @inheritdoc ISuperGovernor
    1013
        function getProver() external view returns (address) {
    1014
            return _prover;
    1015
        }
    1016
    
                                                    
                                                
    1017
        /// @inheritdoc ISuperGovernor
    1018
        function isUpkeepPaymentsEnabled() external view returns (bool enabled) {
    1019
    ✓ 385.5K
            return _upkeepPaymentsEnabled;
    1020
        }
    1021
    
                                                    
                                                
    1022
        /// @inheritdoc ISuperGovernor
    1023
        function getProposedUpkeepPaymentsStatus() external view returns (bool enabled, uint256 effectiveTime) {
    1024
            return (_proposedUpkeepPaymentsEnabled, _upkeepPaymentsChangeEffectiveTime);
    1025
        }
    1026
    
                                                    
                                                
    1027
        /// @inheritdoc ISuperGovernor
    1028
    ✓ 7.6K
        function isSuperformManager(address manager) external view returns (bool isSuperform) {
    1029
    ✓ 7.6K
            return _superformManagers.contains(manager);
    1030
        }
    1031
    
                                                    
                                                
    1032
        /// @inheritdoc ISuperGovernor
    1033
        function getAllSuperformManagers() external view returns (address[] memory managers) {
    1034
            return _superformManagers.values();
    1035
        }
    1036
    
                                                    
                                                
    1037
        /// @inheritdoc ISuperGovernor
    1038
        function getManagersPaginated(
    1039
            uint256 cursor,
    1040
            uint256 limit
    1041
        )
    1042
            external
    1043
            view
    1044
            returns (address[] memory chunkOfManagers, uint256 next)
    1045
        {
    1046
            uint256 len = _superformManagers.length();
    1047
    
                                                    
                                                
    1048
            // clamp limit so we don’t read past end
    1049
            uint256 realLimit = limit;
    1050
            // If cursor is beyond the end, return empty array
    1051
            if (cursor >= len) {
    1052
                return (new address[](0), 0);
    1053
            }
    1054
    
                                                    
                                                
    1055
            uint256 remaining = len - cursor;
    1056
            if (realLimit > remaining) realLimit = remaining;
    1057
    
                                                    
                                                
    1058
            chunkOfManagers = new address[](realLimit);
    1059
            for (uint256 i; i < realLimit; ++i) {
    1060
                chunkOfManagers[i] = _superformManagers.at(cursor + i);
    1061
            }
    1062
    
                                                    
                                                
    1063
            next = (cursor + realLimit < len) ? cursor + realLimit : 0;
    1064
        }
    1065
    
                                                    
                                                
    1066
        /// @inheritdoc ISuperGovernor
    1067
        function getSuperformManagersCount() external view returns (uint256) {
    1068
            return _superformManagers.length();
    1069
        }
    1070
    
                                                    
                                                
    1071
        /// @inheritdoc ISuperGovernor
    1072
        function isWhitelistedIncentiveToken(address token) external view returns (bool) {
    1073
            return _isWhitelistedIncentiveToken[token];
    1074
        }
    1075
    
                                                    
                                                
    1076
        /// @inheritdoc ISuperGovernor
    1077
        function registerProtectedKeeper(address keeper) external onlyRole(_GOVERNOR_ROLE) {
    1078
            if (keeper == address(0)) revert INVALID_ADDRESS();
    1079
            if (_protectedKeepers.contains(keeper)) revert KEEPER_ALREADY_REGISTERED();
    1080
    
                                                    
                                                
    1081
            _protectedKeepers.add(keeper);
    1082
            emit ProtectedKeeperRegistered(keeper);
    1083
        }
    1084
    
                                                    
                                                
    1085
        /// @inheritdoc ISuperGovernor
    1086
        function unregisterProtectedKeeper(address keeper) external onlyRole(_GOVERNOR_ROLE) {
    1087
            if (!_protectedKeepers.contains(keeper)) revert KEEPER_NOT_REGISTERED();
    1088
    
                                                    
                                                
    1089
            _protectedKeepers.remove(keeper);
    1090
            emit ProtectedKeeperUnregistered(keeper);
    1091
        }
    1092
    
                                                    
                                                
    1093
        /// @inheritdoc ISuperGovernor
    1094
    ✓ 7.7K
        function isProtectedKeeper(address keeper) external view returns (bool) {
    1095
    ✓ 7.7K
            return _protectedKeepers.contains(keeper);
    1096
        }
    1097
    
                                                    
                                                
    1098
        /// @inheritdoc ISuperGovernor
    1099
        function getProtectedKeepers() external view returns (address[] memory) {
    1100
            return _protectedKeepers.values();
    1101
        }
    1102
    
                                                    
                                                
    1103
        /// @inheritdoc ISuperGovernor
    1104
        function getProtectedKeepersCount() external view returns (uint256) {
    1105
            return _protectedKeepers.length();
    1106
        }
    1107
    
                                                    
                                                
    1108
        /*//////////////////////////////////////////////////////////////
    1109
                               INTERNAL FUNCTIONS
    1110
        //////////////////////////////////////////////////////////////*/
    1111
    ✓ 7.6K
        function _convertGasToUp(uint256 gasAmount) internal view returns (uint256) {
    1112
    ✓ 7.6K
            address oracle = _addressRegistry[SUPER_ORACLE];
    1113
    ✓ 7.6K
    ⟲ 7.6K
            if (oracle == address(0)) revert SUPER_ORACLE_NOT_FOUND();
    1114
            address upToken = _addressRegistry[UP];
    1115
            if (upToken == address(0)) revert UP_NOT_FOUND();
    1116
    
                                                    
                                                
    1117
            // Step 1: convert gas to ETH
    1118
            (uint256 ethAmount,,,) = ISuperOracle(oracle).getQuoteFromProvider(
    1119
                gasAmount,
    1120
                GAS_QUOTE,
    1121
                GWEI_QUOTE,
    1122
                AVERAGE_PROVIDER
    1123
            );
    1124
    
                                                    
                                                
    1125
            // Step 2: convert ETH to USD
    1126
            (uint256 ethToUsd,,,) = ISuperOracle(oracle).getQuoteFromProvider(
    1127
                ethAmount,
    1128
                NATIVE_TOKEN,
    1129
                USD_TOKEN,
    1130
                AVERAGE_PROVIDER
    1131
            );
    1132
    
                                                    
                                                
    1133
            // Step 3: convert USD to UP (how much USD per UP token)
    1134
            (uint256 upPerUsd,,,) = ISuperOracle(oracle).getQuoteFromProvider(
    1135
                1e18, // 1 UP token (18 decimals)
    1136
                upToken,
    1137
                USD_TOKEN,
    1138
                AVERAGE_PROVIDER
    1139
            );
    1140
    
                                                    
                                                
    1141
            // Calculate required UP tokens
    1142
            // usdAmount / upPerUsd = required UP tokens
    1143
            uint256 requiredUpTokens = Math.mulDiv(ethToUsd, 1e18, upPerUsd, Math.Rounding.Ceil);
    1144
            return requiredUpTokens;
    1145
        }
    1146
    }
    1147
    
                                                    
                                                
    85.0% src/SuperVault/SuperVault.sol
    Lines covered: 147 / 173 (85.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // External
    5
    import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
    6
    import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    7
    import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
    8
    
                                                    
                                                
    9
    // OpenZeppelin Upgradeable
    10
    import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    11
    import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
    12
    import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
    13
    import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    14
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    15
    import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
    16
    import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
    17
    import { IERC20Metadata } from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
    18
    
                                                    
                                                
    19
    // Interfaces
    20
    import { ISuperVault } from "../interfaces/SuperVault/ISuperVault.sol";
    21
    import { ISuperVaultStrategy } from "../interfaces/SuperVault/ISuperVaultStrategy.sol";
    22
    import { ISuperGovernor } from "../interfaces/ISuperGovernor.sol";
    23
    import { ISuperVaultAggregator } from "../interfaces/SuperVault/ISuperVaultAggregator.sol";
    24
    import { IERC7540Operator, IERC7540Redeem, IERC7741 } from "../vendor/standards/ERC7540/IERC7540Vault.sol";
    25
    import { IERC7575 } from "../vendor/standards/ERC7575/IERC7575.sol";
    26
    import { ISuperVaultEscrow } from "../interfaces/SuperVault/ISuperVaultEscrow.sol";
    27
    
                                                    
                                                
    28
    // Libraries
    29
    import { AssetMetadataLib } from "../libraries/AssetMetadataLib.sol";
    30
    
                                                    
                                                
    31
    /// @title SuperVault
    32
    /// @author Superform Labs
    33
    /// @notice SuperVault vault contract implementing ERC4626 with synchronous deposits and asynchronous redeems via
    34
    /// ERC7540
    35
    contract SuperVault is
    36
        Initializable,
    37
        ERC20Upgradeable,
    38
        IERC7540Redeem,
    39
        IERC7741,
    40
        IERC4626,
    41
        ISuperVault,
    42
        ReentrancyGuardUpgradeable,
    43
        EIP712Upgradeable
    44
    {
    45
        using AssetMetadataLib for address;
    46
        using SafeERC20 for IERC20;
    47
        using Math for uint256;
    48
    
                                                    
                                                
    49
        /*//////////////////////////////////////////////////////////////
    50
                                    CONSTANTS
    51
        //////////////////////////////////////////////////////////////*/
    52
    ✓ 568.5K
        uint256 private constant REQUEST_ID = 0;
    53
    ✓ 1.1M
        uint256 private constant BPS_PRECISION = 10_000;
    54
    
                                                    
                                                
    55
        // EIP712 TypeHash
    56
        bytes32 public constant AUTHORIZE_OPERATOR_TYPEHASH =
    57
            keccak256("AuthorizeOperator(address controller,address operator,bool approved,bytes32 nonce,uint256 deadline)");
    58
    
                                                    
                                                
    59
        /*//////////////////////////////////////////////////////////////
    60
                                    STATE
    61
        //////////////////////////////////////////////////////////////*/
    62
    ✓ 10.4K
        address public share;
    63
        IERC20 private _asset;
    64
        uint8 private _underlyingDecimals;
    65
        ISuperVaultStrategy public strategy;
    66
        address public escrow;
    67
    ✓ 2.0M
        uint256 public PRECISION;
    68
    
                                                    
                                                
    69
        // Core contracts
    70
    ✓ 14.0M
        ISuperGovernor public immutable superGovernor;
    71
    
                                                    
                                                
    72
        /// @inheritdoc IERC7540Operator
    73
        mapping(address owner => mapping(address operator => bool)) public isOperator;
    74
    
                                                    
                                                
    75
        // Authorization tracking
    76
        mapping(address controller => mapping(bytes32 nonce => bool used)) private _authorizations;
    77
    
                                                    
                                                
    78
        /*//////////////////////////////////////////////////////////////
    79
                                CONSTRUCTOR
    80
        //////////////////////////////////////////////////////////////*/
    81
    
                                                    
                                                
    82
        constructor(address superGovernor_) {
    83
    ✓ 1
            if (superGovernor_ == address(0)) revert ZERO_ADDRESS();
    84
    ✓ 1
            superGovernor = ISuperGovernor(superGovernor_);
    85
    ✓ 1
            emit SuperGovernorSet(superGovernor_);
    86
    
                                                    
                                                
    87
    ✓ 1
            _disableInitializers();
    88
        }
    89
    
                                                    
                                                
    90
        /*//////////////////////////////////////////////////////////////
    91
                                INITIALIZATION
    92
        //////////////////////////////////////////////////////////////*/
    93
    
                                                    
                                                
    94
        /// @notice Initialize the vault with required parameters
    95
        /// @param asset_ The underlying asset token address
    96
        /// @param name_ The name of the vault token
    97
        /// @param symbol_ The symbol of the vault token
    98
        /// @param strategy_ The strategy contract address
    99
        /// @param escrow_ The escrow contract address
    100
        function initialize(
    101
            address asset_,
    102
            string memory name_,
    103
            string memory symbol_,
    104
            address strategy_,
    105
            address escrow_
    106
        )
    107
            external
    108
            initializer
    109
        {
    110
    ✓ 226.3K
            if (asset_ == address(0)) revert INVALID_ASSET();
    111
    ✓ 226.3K
            if (strategy_ == address(0)) revert INVALID_STRATEGY();
    112
    ✓ 226.3K
            if (escrow_ == address(0)) revert INVALID_ESCROW();
    113
    
                                                    
                                                
    114
            // Initialize parent contracts
    115
    ✓ 226.3K
            __ERC20_init(name_, symbol_);
    116
    ✓ 226.3K
            __ReentrancyGuard_init();
    117
    ✓ 226.3K
            __EIP712_init(name_, "1");
    118
    
                                                    
                                                
    119
            // Set asset and precision
    120
    ✓ 226.3K
            _asset = IERC20(asset_);
    121
    ✓ 226.3K
            (bool success, uint8 assetDecimals) = asset_.tryGetAssetDecimals();
    122
    ✓ 200.4K
    ⟲ 85.6K
            if (!success) revert INVALID_ASSET();
    123
    ✓ 114.7K
            _underlyingDecimals = assetDecimals;
    124
    ✓ 114.7K
            PRECISION = 10 ** _underlyingDecimals;
    125
    ✓ 91.9K
            share = address(this);
    126
    ✓ 91.9K
            strategy = ISuperVaultStrategy(strategy_);
    127
    ✓ 91.9K
            escrow = escrow_;
    128
        }
    129
    
                                                    
                                                
    130
        /*//////////////////////////////////////////////////////////////
    131
                                ERC20 OVERRIDES
    132
        //////////////////////////////////////////////////////////////*/
    133
    
                                                    
                                                
    134
        /*//////////////////////////////////////////////////////////////
    135
                            USER EXTERNAL FUNCTIONS
    136
        //////////////////////////////////////////////////////////////*/
    137
    
                                                    
                                                
    138
        /// @inheritdoc IERC4626
    139
    ✓ 776.6K
        function deposit(uint256 assets, address receiver) public override nonReentrant returns (uint256 shares) {
    140
    ✓ 776.6K
    ⟲ 2.2K
            if (receiver == address(0)) revert ZERO_ADDRESS();
    141
    ✓ 774.4K
    ⟲ 16.6K
            if (assets == 0) revert ZERO_AMOUNT();
    142
    
                                                    
                                                
    143
            // Forward assets from msg-sender to strategy
    144
    ✓ 757.8K
            _asset.safeTransferFrom(msg.sender, address(strategy), assets);
    145
    
                                                    
                                                
    146
            // Single executor call: strategy skims entry fee, accounts on NET, returns net shares
    147
    ✓ 369.3K
            shares = strategy.handleOperations4626Deposit(receiver, assets);
    148
    ✓ 334.8K
            if (shares == 0) revert ZERO_AMOUNT();
    149
    
                                                    
                                                
    150
            // Mint the net shares
    151
    ✓ 334.8K
            _mint(receiver, shares);
    152
    
                                                    
                                                
    153
    ✓ 334.8K
            emit Deposit(msg.sender, receiver, assets, shares);
    154
        }
    155
    
                                                    
                                                
    156
        /// @inheritdoc IERC4626
    157
    ✓ 620.6K
        function mint(uint256 shares, address receiver) public override nonReentrant returns (uint256 assets) {
    158
    ✓ 620.6K
    ⟲ 4.6K
            if (receiver == address(0)) revert ZERO_ADDRESS();
    159
    ✓ 616.0K
    ⟲ 17.0K
            if (shares == 0) revert ZERO_AMOUNT();
    160
    
                                                    
                                                
    161
    ✓ 599.0K
            uint256 assetsNet;
    162
    ✓ 599.0K
            (assets, assetsNet) = strategy.quoteMintAssetsGross(shares);
    163
    
                                                    
                                                
    164
            // Forward quoted gross assets from msg-sender to strategy
    165
    ✓ 493.3K
            _asset.safeTransferFrom(msg.sender, address(strategy), assets);
    166
    
                                                    
                                                
    167
            // Single executor call: strategy handles fees and accounts on NET
    168
    ✓ 199.4K
            strategy.handleOperations4626Mint(receiver, shares, assets, assetsNet);
    169
    
                                                    
                                                
    170
            // Mint the exact shares asked
    171
    ✓ 197.8K
            _mint(receiver, shares);
    172
    
                                                    
                                                
    173
    ✓ 197.8K
            emit Deposit(msg.sender, receiver, assets, shares);
    174
        }
    175
    
                                                    
                                                
    176
        /// @inheritdoc IERC7540Redeem
    177
        /// @notice Once owner has authorized an operator, the operator can request a redeem with any controller address
    178
    ✓ 1.0M
        function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256) {
    179
    ✓ 1.0M
    ⟲ 288.1K
            if (shares == 0) revert ZERO_AMOUNT();
    180
    ✓ 742.4K
            if (owner == address(0) || controller == address(0)) revert ZERO_ADDRESS();
    181
    ✓ 742.4K
            if (owner != msg.sender && !isOperator[owner][msg.sender]) revert INVALID_OWNER_OR_OPERATOR();
    182
    ✓ 742.4K
    ⟲ 173.3K
            if (balanceOf(owner) < shares) revert INVALID_AMOUNT();
    183
    
                                                    
                                                
    184
            // Enforce auditor's invariant for current accounting model
    185
    ✓ 569.1K
            if (controller != owner) revert CONTROLLER_MUST_EQUAL_OWNER();
    186
    
                                                    
                                                
    187
            // Transfer shares to escrow for temporary locking
    188
    ✓ 569.1K
            _approve(owner, escrow, shares);
    189
    ✓ 569.1K
            ISuperVaultEscrow(escrow).escrowShares(owner, shares);
    190
    
                                                    
                                                
    191
            // Forward to strategy (7540 path)
    192
    ✓ 569.1K
            strategy.handleOperations7540(ISuperVaultStrategy.Operation.RedeemRequest, controller, address(0), shares);
    193
    
                                                    
                                                
    194
    ✓ 568.5K
            emit RedeemRequest(controller, owner, REQUEST_ID, msg.sender, shares);
    195
            return REQUEST_ID;
    196
        }
    197
    
                                                    
                                                
    198
        /// @inheritdoc ISuperVault
    199
        function cancelRedeem(address controller) external {
    200
    ✓ 101.0K
            _validateController(controller);
    201
    
                                                    
                                                
    202
    ✓ 101.0K
            uint256 shares = strategy.pendingRedeemRequest(controller);
    203
    
                                                    
                                                
    204
            // Forward to strategy (7540 path)
    205
    ✓ 101.0K
            strategy.handleOperations7540(ISuperVaultStrategy.Operation.CancelRedeem, controller, address(0), 0);
    206
    
                                                    
                                                
    207
            // Return shares to controller
    208
    ✓ 1.3K
            ISuperVaultEscrow(escrow).returnShares(controller, shares);
    209
    
                                                    
                                                
    210
    ✓ 1.3K
            emit RedeemRequestCancelled(controller, msg.sender);
    211
        }
    212
    
                                                    
                                                
    213
        /// @inheritdoc IERC7540Operator
    214
    ✓ 123.7K
        function setOperator(address operator, bool approved) external returns (bool success) {
    215
    ✓ 123.7K
    ⟲ 51.8K
            if (msg.sender == operator) revert UNAUTHORIZED();
    216
    ✓ 72.0K
            isOperator[msg.sender][operator] = approved;
    217
    ✓ 72.0K
            emit OperatorSet(msg.sender, operator, approved);
    218
    ✓ 72.0K
            return true;
    219
        }
    220
    
                                                    
                                                
    221
        /// @inheritdoc IERC7741
    222
        function authorizeOperator(
    223
            address controller,
    224
            address operator,
    225
            bool approved,
    226
            bytes32 nonce,
    227
            uint256 deadline,
    228
            bytes memory signature
    229
        )
    230
            external
    231
            returns (bool)
    232
        {
    233
            if (controller == operator) revert UNAUTHORIZED();
    234
            if (block.timestamp > deadline) revert DEADLINE_PASSED();
    235
            if (_authorizations[controller][nonce]) revert UNAUTHORIZED();
    236
    
                                                    
                                                
    237
            _authorizations[controller][nonce] = true;
    238
    
                                                    
                                                
    239
            bytes32 structHash =
    240
                keccak256(abi.encode(AUTHORIZE_OPERATOR_TYPEHASH, controller, operator, approved, nonce, deadline));
    241
            bytes32 digest = _hashTypedDataV4(structHash);
    242
    
                                                    
                                                
    243
            if (!_isValidSignature(controller, digest, signature)) revert INVALID_SIGNATURE();
    244
    
                                                    
                                                
    245
            isOperator[controller][operator] = approved;
    246
            emit OperatorSet(controller, operator, approved);
    247
    
                                                    
                                                
    248
            return true;
    249
        }
    250
    
                                                    
                                                
    251
        /*//////////////////////////////////////////////////////////////
    252
                        USER EXTERNAL VIEW FUNCTIONS
    253
        //////////////////////////////////////////////////////////////*/
    254
    
                                                    
                                                
    255
        //--ERC7540--
    256
    
                                                    
                                                
    257
        /// @inheritdoc IERC7540Redeem
    258
        function pendingRedeemRequest(
    259
            uint256, /*requestId*/
    260
            address controller
    261
        )
    262
            external
    263
            view
    264
    ✓ 19.3M
            returns (uint256 pendingShares)
    265
        {
    266
    ✓ 19.3M
            return strategy.pendingRedeemRequest(controller);
    267
        }
    268
    
                                                    
                                                
    269
        /// @inheritdoc IERC7540Redeem
    270
        function claimableRedeemRequest(
    271
            uint256, /*requestId*/
    272
            address controller
    273
        )
    274
            external
    275
            view
    276
    ✓ 5.1M
            returns (uint256 claimableShares)
    277
        {
    278
    ✓ 5.1M
            return maxRedeem(controller);
    279
        }
    280
    
                                                    
                                                
    281
        //--Operator Management--
    282
    
                                                    
                                                
    283
        /// @inheritdoc IERC7741
    284
        function authorizations(address controller, bytes32 nonce) external view returns (bool used) {
    285
            return _authorizations[controller][nonce];
    286
        }
    287
    
                                                    
                                                
    288
        /// @inheritdoc IERC7741
    289
        function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
    290
            return _domainSeparatorV4();
    291
        }
    292
    
                                                    
                                                
    293
        /// @inheritdoc IERC7741
    294
        function invalidateNonce(bytes32 nonce) external {
    295
    ✓ 134.6K
    ⟲ 211
            if (_authorizations[msg.sender][nonce]) revert INVALID_NONCE();
    296
    ✓ 134.4K
            _authorizations[msg.sender][nonce] = true;
    297
    
                                                    
                                                
    298
    ✓ 134.4K
            emit NonceInvalidated(msg.sender, nonce);
    299
        }
    300
    
                                                    
                                                
    301
        /*//////////////////////////////////////////////////////////////
    302
                                ERC4626 IMPLEMENTATION
    303
        //////////////////////////////////////////////////////////////*/
    304
        function decimals() public view virtual override(ERC20Upgradeable, IERC20Metadata) returns (uint8) {
    305
    ✓ 90.0K
            return _underlyingDecimals;
    306
        }
    307
    
                                                    
                                                
    308
        function asset() public view virtual override returns (address) {
    309
    ✓ 14.0M
            return address(_asset);
    310
        }
    311
    
                                                    
                                                
    312
        /// @inheritdoc IERC4626
    313
    ✓ 3.5K
        function totalAssets() external view override returns (uint256) {
    314
    ✓ 3.5K
            uint256 supply = totalSupply();
    315
    ✓ 3.5K
            if (supply == 0) return 0;
    316
    ✓ 344
            uint256 currentPPS = _getStoredPPS();
    317
    ✓ 344
            return Math.mulDiv(supply, currentPPS, PRECISION, Math.Rounding.Floor);
    318
        }
    319
    
                                                    
                                                
    320
        /// @inheritdoc IERC4626
    321
    ✓ 362.8K
        function convertToShares(uint256 assets) public view override returns (uint256) {
    322
    ✓ 362.8K
            uint256 pps = _getStoredPPS();
    323
    ✓ 362.8K
            if (pps == 0) return 0;
    324
    ✓ 359.0K
            return Math.mulDiv(assets, PRECISION, pps, Math.Rounding.Floor);
    325
        }
    326
    
                                                    
                                                
    327
        /// @inheritdoc IERC4626
    328
    ✓ 10.1M
        function convertToAssets(uint256 shares) public view override returns (uint256) {
    329
    ✓ 10.1M
            uint256 currentPPS = _getStoredPPS();
    330
    ✓ 10.1M
            if (currentPPS == 0) return 0;
    331
    ✓ 10.0M
            return Math.mulDiv(shares, currentPPS, PRECISION, Math.Rounding.Floor);
    332
        }
    333
    
                                                    
                                                
    334
        /// @inheritdoc IERC4626
    335
    ✓ 284.6K
        function maxDeposit(address) public view override returns (uint256) {
    336
    ✓ 284.6K
            if (_isPaused()) return 0;
    337
    ✓ 264.5K
            return type(uint256).max;
    338
        }
    339
    
                                                    
                                                
    340
        /// @inheritdoc IERC4626
    341
        function maxMint(address) external view override returns (uint256) {
    342
            if (_isPaused()) return 0;
    343
            return type(uint256).max;
    344
        }
    345
    
                                                    
                                                
    346
        /// @inheritdoc IERC4626
    347
    ✓ 1.9M
        function maxWithdraw(address owner) public view override returns (uint256) {
    348
    ✓ 1.9M
            return strategy.claimableWithdraw(owner);
    349
        }
    350
    
                                                    
                                                
    351
        /// @inheritdoc IERC4626
    352
    ✓ 5.5M
        function maxRedeem(address owner) public view override returns (uint256) {
    353
    ✓ 5.5M
            uint256 withdrawPrice = strategy.getAverageWithdrawPrice(owner);
    354
    ✓ 5.5M
            if (withdrawPrice == 0) return 0;
    355
    ✓ 879.9K
            return maxWithdraw(owner).mulDiv(PRECISION, withdrawPrice, Math.Rounding.Floor);
    356
        }
    357
    
                                                    
                                                
    358
        /// @inheritdoc IERC4626
    359
    ✓ 1.1M
        function previewDeposit(uint256 assets) public view override returns (uint256) {
    360
    ✓ 1.1M
            uint256 pps = _getStoredPPS();
    361
    ✓ 1.1M
            if (pps == 0) return 0;
    362
    
                                                    
                                                
    363
    ✓ 1.1M
            (uint256 feeBps,) = _getManagementFeeConfig();
    364
    
                                                    
                                                
    365
    ✓ 1.1M
            if (feeBps == 0) return Math.mulDiv(assets, PRECISION, pps, Math.Rounding.Floor);
    366
            // fee-on-gross: fee = ceil(gross * feeBps / BPS)
    367
    ✓ 1.1M
            uint256 fee = Math.mulDiv(assets, feeBps, BPS_PRECISION, Math.Rounding.Ceil);
    368
    
                                                    
                                                
    369
    ✓ 1.1M
            uint256 assetsNet = assets - fee;
    370
    ✓ 1.1M
            return Math.mulDiv(assetsNet, PRECISION, pps, Math.Rounding.Floor);
    371
        }
    372
    
                                                    
                                                
    373
        /// @inheritdoc IERC4626
    374
    ✓ 966.8K
        function previewMint(uint256 shares) public view override returns (uint256) {
    375
    ✓ 966.8K
            uint256 pps = _getStoredPPS();
    376
    ✓ 965.9K
            if (pps == 0) return 0;
    377
    
                                                    
                                                
    378
    ✓ 954.7K
            uint256 assetsNet = Math.mulDiv(shares, pps, PRECISION, Math.Rounding.Ceil);
    379
    
                                                    
                                                
    380
    ✓ 875.7K
            (uint256 feeBps,) = _getManagementFeeConfig();
    381
    ✓ 875.7K
            if (feeBps == 0) return assetsNet;
    382
    ✓ 872.4K
            if (feeBps >= BPS_PRECISION) return 0; // impossible to mint (would require infinite gross)
    383
    
                                                    
                                                
    384
    ✓ 818.3K
            return Math.mulDiv(assetsNet, BPS_PRECISION, (BPS_PRECISION - feeBps), Math.Rounding.Ceil);
    385
        }
    386
    
                                                    
                                                
    387
        /// @inheritdoc IERC4626
    388
    ✓ 1.9K
        function previewWithdraw(uint256 /* assets*/ ) public pure override returns (uint256) {
    389
    ✓ 1.9K
    ⟲ 1.9K
            revert NOT_IMPLEMENTED();
    390
        }
    391
    
                                                    
                                                
    392
        /// @inheritdoc IERC4626
    393
        function previewRedeem(uint256 /* shares*/ ) public pure override returns (uint256) {
    394
            revert NOT_IMPLEMENTED();
    395
        }
    396
    
                                                    
                                                
    397
        /// @inheritdoc IERC4626
    398
        function withdraw(
    399
            uint256 assets,
    400
            address receiver,
    401
            address controller
    402
        )
    403
            public
    404
            override
    405
            nonReentrant
    406
    ✓ 520.6K
            returns (uint256 shares)
    407
        {
    408
    ✓ 520.6K
    ⟲ 897
            if (receiver == address(0)) revert ZERO_ADDRESS();
    409
    ✓ 519.7K
            _validateController(controller);
    410
    
                                                    
                                                
    411
    ✓ 517.4K
            uint256 averageWithdrawPrice = strategy.getAverageWithdrawPrice(controller);
    412
    ✓ 517.4K
    ⟲ 445.6K
            if (averageWithdrawPrice == 0) revert INVALID_WITHDRAW_PRICE();
    413
    
                                                    
                                                
    414
    ✓ 71.8K
            uint256 maxWithdrawAmount = maxWithdraw(controller);
    415
    ✓ 71.8K
    ⟲ 7.7K
            if (assets > maxWithdrawAmount) revert INVALID_AMOUNT();
    416
    
                                                    
                                                
    417
            // Calculate shares based on assets and average withdraw price
    418
    ✓ 64.1K
            shares = assets.mulDiv(PRECISION, averageWithdrawPrice, Math.Rounding.Floor);
    419
    
                                                    
                                                
    420
            // Take assets from strategy (7540 path)
    421
    ✓ 64.1K
            strategy.handleOperations7540(ISuperVaultStrategy.Operation.ClaimRedeem, controller, receiver, assets);
    422
    
                                                    
                                                
    423
    ✓ 60.4K
            emit Withdraw(msg.sender, receiver, controller, assets, shares);
    424
        }
    425
    
                                                    
                                                
    426
        /// @inheritdoc IERC4626
    427
        function redeem(
    428
            uint256 shares,
    429
            address receiver,
    430
            address controller
    431
        )
    432
            public
    433
            override
    434
            nonReentrant
    435
    ✓ 662.0K
            returns (uint256 assets)
    436
        {
    437
    ✓ 662.0K
    ⟲ 965
            if (receiver == address(0)) revert ZERO_ADDRESS();
    438
    ✓ 661.0K
            _validateController(controller);
    439
    
                                                    
                                                
    440
    ✓ 660.1K
            uint256 averageWithdrawPrice = strategy.getAverageWithdrawPrice(controller);
    441
    ✓ 660.1K
    ⟲ 496.1K
            if (averageWithdrawPrice == 0) revert INVALID_WITHDRAW_PRICE();
    442
    
                                                    
                                                
    443
            // Calculate assets based on shares and average withdraw price
    444
    ✓ 164.0K
            assets = shares.mulDiv(averageWithdrawPrice, PRECISION, Math.Rounding.Floor);
    445
    
                                                    
                                                
    446
    ✓ 161.0K
            uint256 maxWithdrawAmount = maxWithdraw(controller);
    447
    ✓ 161.0K
    ⟲ 9.3K
            if (assets > maxWithdrawAmount) revert INVALID_AMOUNT();
    448
    
                                                    
                                                
    449
            // Take assets from strategy (7540 path)
    450
    ✓ 151.7K
            strategy.handleOperations7540(ISuperVaultStrategy.Operation.ClaimRedeem, controller, receiver, assets);
    451
    
                                                    
                                                
    452
    ✓ 72.6K
            emit Withdraw(msg.sender, receiver, controller, assets, shares);
    453
        }
    454
    
                                                    
                                                
    455
        // @inheritdoc ISuperVault
    456
        function burnShares(uint256 amount) external {
    457
    ✓ 495.9K
    ⟲ 117.9K
            if (msg.sender != address(strategy)) revert UNAUTHORIZED();
    458
    ✓ 378.0K
            _burn(escrow, amount);
    459
        }
    460
    
                                                    
                                                
    461
        // @inheritdoc ISuperVault
    462
        function onRedeemClaimable(
    463
            address user,
    464
            uint256 assets,
    465
            uint256 shares,
    466
            uint256 averageWithdrawPrice,
    467
            uint256 accumulatorShares,
    468
            uint256 accumulatorCostBasis
    469
        )
    470
            external
    471
        {
    472
    ✓ 378.0K
            if (msg.sender != address(strategy)) revert UNAUTHORIZED();
    473
    ✓ 378.0K
            emit RedeemClaimable(
    474
                user, REQUEST_ID, assets, shares, averageWithdrawPrice, accumulatorShares, accumulatorCostBasis
    475
            );
    476
        }
    477
    
                                                    
                                                
    478
        /*//////////////////////////////////////////////////////////////
    479
                                ERC165 INTERFACE
    480
        //////////////////////////////////////////////////////////////*/
    481
        function supportsInterface(bytes4 interfaceId) public pure returns (bool) {
    482
            return interfaceId == type(IERC7540Redeem).interfaceId || interfaceId == type(IERC165).interfaceId
    483
                || interfaceId == type(IERC7741).interfaceId || interfaceId == type(IERC4626).interfaceId
    484
                || interfaceId == type(IERC7575).interfaceId || interfaceId == type(IERC7540Operator).interfaceId;
    485
        }
    486
    
                                                    
                                                
    487
        /*//////////////////////////////////////////////////////////////
    488
                            INTERNAL FUNCTIONS
    489
        //////////////////////////////////////////////////////////////*/
    490
        /// @notice Validates that the controller is the msg.sender or has been authorized by the controller
    491
        /// @param controller The controller to validate
    492
        function _validateController(address controller) internal view {
    493
    ✓ 1.3M
    ⟲ 3.2K
            if (controller != msg.sender && !isOperator[controller][msg.sender]) revert INVALID_CONTROLLER();
    494
        }
    495
    
                                                    
                                                
    496
        /// @notice Verify an EIP712 signature using OpenZeppelin's ECDSA library
    497
        /// @param signer The signer to verify
    498
        /// @param digest The digest to verify
    499
        /// @param signature The signature to verify
    500
        function _isValidSignature(address signer, bytes32 digest, bytes memory signature) internal pure returns (bool) {
    501
            address recoveredSigner = ECDSA.recover(digest, signature);
    502
            return recoveredSigner == signer;
    503
        }
    504
    
                                                    
                                                
    505
        /// @notice Overrides the ERC20 _update function to update the state of the vault when a transfer occurs
    506
        /// @param from The address of the sender
    507
        /// @param to The address of the recipient
    508
        /// @param value The amount of shares being transferred
    509
        function _update(address from, address to, uint256 value) internal override(ERC20Upgradeable) {
    510
            /// @dev Move only accumulators pro-rata between actual users, not to/from infrastructure contracts
    511
    ✓ 1.6M
            if (from != address(0) && to != address(0) && to != address(escrow) && from != address(escrow)) {
    512
    ✓ 119.1K
                uint256 shares = value;
    513
                // Zero-value transfers are legal: treat as accounting no-op.
    514
    ✓ 119.1K
                if (shares > 0) {
    515
    ✓ 111.9K
                    strategy.moveAccumulatorOnTransfer(from, to, shares);
    516
                }
    517
            }
    518
    ✓ 1.6M
            super._update(from, to, value);
    519
        }
    520
    
                                                    
                                                
    521
    ✓ 12.6M
        function _getStoredPPS() internal view returns (uint256) {
    522
    ✓ 12.6M
            return strategy.getStoredPPS();
    523
        }
    524
    
                                                    
                                                
    525
        /// @notice Checks if the vault is currently paused
    526
        /// @dev This accesses the SuperVaultAggregator directly via the governor to determine pause status
    527
        /// @return True if the vault is paused, false otherwise
    528
    ✓ 284.6K
        function _isPaused() internal view returns (bool) {
    529
    ✓ 284.6K
            address aggregatorAddress = superGovernor.getAddress(superGovernor.SUPER_VAULT_AGGREGATOR());
    530
    ✓ 284.6K
            return ISuperVaultAggregator(aggregatorAddress).isStrategyPaused(address(strategy));
    531
        }
    532
    
                                                    
                                                
    533
        /// @dev Read management fee config (view-only for previews)
    534
    ✓ 2.0M
        function _getManagementFeeConfig() internal view returns (uint256 feeBps, address recipient) {
    535
    ✓ 2.0M
            ISuperVaultStrategy.FeeConfig memory cfg = strategy.getConfigInfo();
    536
    ✓ 2.0M
            return (cfg.managementFeeBps, cfg.recipient);
    537
        }
    538
    }
    539
    
                                                    
                                                
    54.8% src/SuperVault/SuperVaultAggregator.sol
    Lines covered: 285 / 520 (54.8%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // External
    5
    import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
    6
    import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    7
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    8
    import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
    9
    import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    10
    import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
    11
    
                                                    
                                                
    12
    // Superform
    13
    import {SuperVault} from "./SuperVault.sol";
    14
    import {SuperVaultStrategy} from "./SuperVaultStrategy.sol";
    15
    import {SuperVaultEscrow} from "./SuperVaultEscrow.sol";
    16
    import {ISuperGovernor} from "../interfaces/ISuperGovernor.sol";
    17
    import {ISuperVaultAggregator} from "../interfaces/SuperVault/ISuperVaultAggregator.sol";
    18
    // Libraries
    19
    import {AssetMetadataLib} from "../libraries/AssetMetadataLib.sol";
    20
    
                                                    
                                                
    21
    /// @title SuperVaultAggregator
    22
    /// @author Superform Labs
    23
    /// @notice Registry and PPS oracle for all SuperVaults
    24
    /// @dev Creates new SuperVault trios and manages PPS updates
    25
    contract SuperVaultAggregator is ISuperVaultAggregator {
    26
        using AssetMetadataLib for address;
    27
        using Clones for address;
    28
        using SafeERC20 for IERC20;
    29
        using Math for uint256;
    30
        using EnumerableSet for EnumerableSet.AddressSet;
    31
    
                                                    
                                                
    32
        /*//////////////////////////////////////////////////////////////
    33
                                     STORAGE
    34
        //////////////////////////////////////////////////////////////*/
    35
        // Vault implementation contracts
    36
        address public immutable VAULT_IMPLEMENTATION;
    37
        address public immutable STRATEGY_IMPLEMENTATION;
    38
        address public immutable ESCROW_IMPLEMENTATION;
    39
    
                                                    
                                                
    40
        // Governance
    41
        ISuperGovernor public immutable SUPER_GOVERNOR;
    42
    
                                                    
                                                
    43
        // Claimable upkeep
    44
    ✓ 24.5M
        uint256 public claimableUpkeep;
    45
    
                                                    
                                                
    46
        // Strategy data storage
    47
        mapping(address strategy => StrategyData) private _strategyData;
    48
    
                                                    
                                                
    49
        // Upkeep balances
    50
        mapping(address manager => uint256 upkeep) private _managerUpkeepBalance;
    51
    
                                                    
                                                
    52
        // Stake balances
    53
        mapping(address manager => uint256 stake) private _managerStakeBalance;
    54
    
                                                    
                                                
    55
        // Registry of created vaults
    56
        EnumerableSet.AddressSet private _superVaults;
    57
        EnumerableSet.AddressSet private _superVaultStrategies;
    58
        EnumerableSet.AddressSet private _superVaultEscrows;
    59
    
                                                    
                                                
    60
        // Constant for PPS decimals
    61
        uint256 public constant PPS_DECIMALS = 18;
    62
    
                                                    
                                                
    63
        // Maximum number of secondary managers per strategy to prevent governance DoS on manager replacement
    64
    ✓ 78.4K
        uint256 public constant MAX_SECONDARY_MANAGERS = 5;
    65
    
                                                    
                                                
    66
        // Maximum number of strategies to process in `batchForwardPPS`
    67
    ✓ 529.1K
        uint256 public constant MAX_STRATEGIES = 300;
    68
    
                                                    
                                                
    69
        // Timelock for manager changes and Merkle root updates
    70
    ✓ 6.8K
        uint256 private constant _MANAGER_CHANGE_TIMELOCK = 7 days;
    71
    ✓ 1
        uint256 private _hooksRootUpdateTimelock = 15 minutes;
    72
    
                                                    
                                                
    73
        // Global hooks Merkle root data
    74
        bytes32 private _globalHooksRoot;
    75
        bytes32 private _proposedGlobalHooksRoot;
    76
        uint256 private _globalHooksRootEffectiveTime;
    77
        bool private _globalHooksRootVetoed;
    78
    
                                                    
                                                
    79
        // Nonce for vault creation tracking
    80
        uint256 private _vaultCreationNonce;
    81
    
                                                    
                                                
    82
        /*//////////////////////////////////////////////////////////////
    83
                                   MODIFIERS
    84
        //////////////////////////////////////////////////////////////*/
    85
        /// @notice Validates that msg.sender is the active PPS Oracle
    86
        modifier onlyPPSOracle() {
    87
    ✓ 529.1K
            if (!SUPER_GOVERNOR.isActivePPSOracle(msg.sender)) {
    88
                revert UNAUTHORIZED_PPS_ORACLE();
    89
            }
    90
            _;
    91
        }
    92
    
                                                    
                                                
    93
        /// @notice Validates that a strategy exists (has been created by this aggregator)
    94
        modifier validStrategy(address strategy) {
    95
    ✓ 19.7M
            if (!_superVaultStrategies.contains(strategy))
    96
    ✓ 171.3K
    ⟲ 1.1M
                revert UNKNOWN_STRATEGY();
    97
    ✓ 3.0K
            _;
    98
        }
    99
    
                                                    
                                                
    100
        /*//////////////////////////////////////////////////////////////
    101
                                  CONSTRUCTOR
    102
        //////////////////////////////////////////////////////////////*/
    103
        /// @notice Initializes the SuperVaultAggregator
    104
        /// @param superGovernor_ Address of the SuperGovernor contract
    105
        /// @param vaultImpl_ Address of the pre-deployed SuperVault implementation
    106
        /// @param strategyImpl_ Address of the pre-deployed SuperVaultStrategy implementation
    107
        /// @param escrowImpl_ Address of the pre-deployed SuperVaultEscrow implementation
    108
        constructor(
    109
            address superGovernor_,
    110
            address vaultImpl_,
    111
            address strategyImpl_,
    112
            address escrowImpl_
    113
        ) {
    114
    ✓ 1
            if (superGovernor_ == address(0)) revert ZERO_ADDRESS();
    115
    ✓ 1
            if (vaultImpl_ == address(0)) revert ZERO_ADDRESS();
    116
    ✓ 1
            if (strategyImpl_ == address(0)) revert ZERO_ADDRESS();
    117
    ✓ 1
            if (escrowImpl_ == address(0)) revert ZERO_ADDRESS();
    118
    
                                                    
                                                
    119
    ✓ 1
            SUPER_GOVERNOR = ISuperGovernor(superGovernor_);
    120
    ✓ 1
            VAULT_IMPLEMENTATION = vaultImpl_;
    121
    ✓ 1
            STRATEGY_IMPLEMENTATION = strategyImpl_;
    122
    ✓ 1
            ESCROW_IMPLEMENTATION = escrowImpl_;
    123
        }
    124
    
                                                    
                                                
    125
        /*//////////////////////////////////////////////////////////////
    126
                                VAULT CREATION
    127
        //////////////////////////////////////////////////////////////*/
    128
        /// @inheritdoc ISuperVaultAggregator
    129
        function createVault(
    130
            VaultCreationParams calldata params
    131
    ✓ 230.7K
        ) external returns (address superVault, address strategy, address escrow) {
    132
            // Input validation
    133
            if (
    134
    ✓ 230.7K
                params.asset == address(0) ||
    135
    ✓ 229.7K
                params.mainManager == address(0) ||
    136
    ✓ 228.8K
                params.feeConfig.recipient == address(0)
    137
            ) {
    138
    ✓ 4.4K
    ⟲ 4.4K
                revert ZERO_ADDRESS();
    139
            }
    140
    
                                                    
                                                
    141
            // Initialize local variables struct to avoid stack too deep
    142
            VaultCreationLocalVars memory vars;
    143
    
                                                    
                                                
    144
            // Increment nonce before creating proxies
    145
    ✓ 226.3K
            vars.currentNonce = _vaultCreationNonce++;
    146
    ✓ 226.3K
            vars.salt = keccak256(
    147
                abi.encode(
    148
    ✓ 226.3K
                    msg.sender,
    149
    ✓ 226.3K
                    params.asset,
    150
    ✓ 226.3K
                    params.name,
    151
    ✓ 226.3K
                    params.symbol,
    152
    ✓ 226.3K
                    vars.currentNonce
    153
                )
    154
            );
    155
    
                                                    
                                                
    156
            // Create minimal proxies
    157
    ✓ 226.3K
            superVault = VAULT_IMPLEMENTATION.cloneDeterministic(vars.salt);
    158
    ✓ 226.3K
            escrow = ESCROW_IMPLEMENTATION.cloneDeterministic(vars.salt);
    159
    ✓ 226.3K
            strategy = STRATEGY_IMPLEMENTATION.cloneDeterministic(vars.salt);
    160
    
                                                    
                                                
    161
            // Initialize superVault
    162
    ✓ 226.3K
            SuperVault(superVault).initialize(
    163
    ✓ 226.3K
                params.asset,
    164
    ✓ 226.3K
                params.name,
    165
    ✓ 226.3K
                params.symbol,
    166
    ✓ 226.3K
                strategy,
    167
    ✓ 226.3K
                escrow
    168
            );
    169
    
                                                    
                                                
    170
            // Initialize escrow
    171
    ✓ 91.9K
            SuperVaultEscrow(escrow).initialize(superVault, strategy);
    172
    
                                                    
                                                
    173
            // Initialize strategy
    174
    ✓ 91.9K
            SuperVaultStrategy(payable(strategy)).initialize(
    175
    ✓ 91.9K
                superVault,
    176
    ✓ 91.9K
                params.feeConfig
    177
            );
    178
    
                                                    
                                                
    179
            // Store vault trio in registry
    180
    ✓ 81.3K
            _superVaults.add(superVault);
    181
    ✓ 81.3K
            _superVaultStrategies.add(strategy);
    182
    ✓ 81.3K
            _superVaultEscrows.add(escrow);
    183
    
                                                    
                                                
    184
            // Get asset decimals
    185
    ✓ 81.3K
            (bool success, uint8 assetDecimals) = params
    186
                .asset
    187
                .tryGetAssetDecimals();
    188
    ✓ 81.3K
            if (!success) revert INVALID_ASSET();
    189
    ✓ 81.3K
            vars.initialPPS = 10 ** assetDecimals; // 1.0 as initial PPS
    190
    
                                                    
                                                
    191
            // Validate maxStaleness against minimum required staleness
    192
    ✓ 81.3K
            if (params.maxStaleness < SUPER_GOVERNOR.getMinStaleness()) {
    193
    ✓ 2.9K
    ⟲ 2.9K
                revert MAX_STALENESS_TOO_LOW();
    194
            }
    195
    
                                                    
                                                
    196
            // Initialize StrategyData individually to avoid mapping assignment issues
    197
    ✓ 78.4K
            _strategyData[strategy].pps = vars.initialPPS;
    198
            // Initialize standard deviation to 0
    199
    ✓ 78.4K
            _strategyData[strategy].lastUpdateTimestamp = block.timestamp;
    200
    ✓ 78.4K
            _strategyData[strategy].minUpdateInterval = params.minUpdateInterval;
    201
    ✓ 78.4K
            _strategyData[strategy].maxStaleness = params.maxStaleness;
    202
    ✓ 78.4K
            _strategyData[strategy].isPaused = false;
    203
    ✓ 78.4K
            _strategyData[strategy].mainManager = params.mainManager;
    204
    
                                                    
                                                
    205
    ✓ 78.4K
            uint256 secondaryLen = params.secondaryManagers.length;
    206
    ✓ 187.4K
            for (uint256 i; i < secondaryLen; ++i) {
    207
    ✓ 109.1K
                _strategyData[strategy].secondaryManagers.add(
    208
    ✓ 109.1K
                    params.secondaryManagers[i]
    209
                );
    210
            }
    211
            if (
    212
    ✓ 78.4K
                _strategyData[strategy].secondaryManagers.length() >=
    213
                MAX_SECONDARY_MANAGERS
    214
            ) {
    215
    ✓ 221
    ⟲ 221
                revert TOO_MANY_SECONDARY_MANAGERS();
    216
            }
    217
    
                                                    
                                                
    218
            // Set default threshold values
    219
    ✓ 78.1K
            _strategyData[strategy].dispersionThreshold = type(uint256).max; // Default: max (disabled)
    220
    ✓ 78.1K
            _strategyData[strategy].deviationThreshold = type(uint256).max; // Default: max (disabled)
    221
    
                                                    
                                                
    222
            emit VaultDeployed(
    223
                superVault,
    224
                strategy,
    225
    ✓ 78.1K
                escrow,
    226
    ✓ 78.1K
                params.asset,
    227
    ✓ 78.1K
                params.name,
    228
    ✓ 78.1K
                params.symbol,
    229
    ✓ 78.1K
                vars.currentNonce
    230
            );
    231
            emit PPSUpdated(
    232
                strategy,
    233
    ✓ 78.1K
                vars.initialPPS,
    234
    ✓ 78.1K
                0,
    235
                0,
    236
                0,
    237
    ✓ 78.1K
                _strategyData[strategy].lastUpdateTimestamp
    238
            );
    239
    
                                                    
                                                
    240
    ✓ 78.1K
            return (superVault, strategy, escrow);
    241
        }
    242
    
                                                    
                                                
    243
        /*//////////////////////////////////////////////////////////////
    244
                              PPS UPDATE FUNCTIONS
    245
        //////////////////////////////////////////////////////////////*/
    246
        /// @inheritdoc ISuperVaultAggregator
    247
        function forwardPPS(ForwardPPSArgs calldata args) external onlyPPSOracle {
    248
    ✓ 529.1K
            uint256 strategiesLength = args.strategies.length;
    249
    ✓ 529.1K
            if (strategiesLength > MAX_STRATEGIES) revert MAX_STRATEGIES_EXCEEDED();
    250
    
                                                    
                                                
    251
    ✓ 529.1K
    ⟲ 688
            if (strategiesLength == 0) revert ZERO_ARRAY_LENGTH();
    252
            // Validate input array lengths
    253
            if (
    254
    ✓ 528.4K
                strategiesLength != args.ppss.length ||
    255
    ✓ 386.2K
                strategiesLength != args.ppsStdevs.length ||
    256
    ✓ 385.5K
                strategiesLength != args.validatorSets.length ||
    257
    ✓ 385.5K
                strategiesLength != args.timestamps.length ||
    258
    ✓ 385.5K
                strategiesLength != args.totalValidators.length
    259
    ✓ 142.9K
    ⟲ 142.9K
            ) revert ARRAY_LENGTH_MISMATCH();
    260
    
                                                    
                                                
    261
    ✓ 385.5K
            bool paymentsEnabled = SUPER_GOVERNOR.isUpkeepPaymentsEnabled();
    262
    ✓ 385.5K
            uint256 chargeableCount;
    263
    ✓ 385.5K
            if (paymentsEnabled) {
    264
    ✓ 15.2K
                for (uint256 i; i < strategiesLength; ++i) {
    265
                    // Skip invalid strategies without reverting
    266
    ✓ 385.5K
                    if (!_superVaultStrategies.contains(args.strategies[i])) {
    267
                        emit UnknownStrategy(args.strategies[i]);
    268
                        continue;
    269
                    }
    270
    
                                                    
                                                
    271
                    // Skip when invalid timestamp is provided
    272
    ✓ 7.6K
                    if (args.timestamps[i] > block.timestamp) {
    273
                        emit ProvidedTimestampExceedsBlockTimestamp(
    274
                            args.strategies[i],
    275
                            args.timestamps[i],
    276
                            block.timestamp
    277
                        );
    278
                        continue;
    279
                    }
    280
    
                                                    
                                                
    281
                    // Skip Superform manager
    282
    ✓ 7.6K
                    address manager = _strategyData[args.strategies[i]].mainManager;
    283
    ✓ 7.6K
                    if (SUPER_GOVERNOR.isSuperformManager(manager)) {
    284
                        emit SuperformManager(args.strategies[i], manager);
    285
                        continue;
    286
                    }
    287
    
                                                    
                                                
    288
                    // Skip if updateAuthority is in the authorized callers list
    289
                    // These are manager-designated keepers that should be exempt from fees
    290
                    // NOTE: Protected keepers cannot be added to this list (blocked in addAuthorizedCaller)
    291
                    /// @dev: cannot underflow; it's checked above already and it skips the entry if that's the case
    292
                    if (
    293
    ✓ 7.6K
                        _strategyData[args.strategies[i]]
    294
                            .authorizedCallers
    295
    ✓ 7.6K
                            .contains(args.updateAuthority)
    296
                    ) {
    297
                        emit AuthorizedCaller(
    298
    ✓ 1.4K
                            args.strategies[i],
    299
    ✓ 1.4K
                            args.updateAuthority
    300
                        );
    301
    ✓ 1.4K
                        continue;
    302
                    }
    303
    
                                                    
                                                
    304
                    // Count only non-stale entries as chargeable
    305
                    if (
    306
    ✓ 6.2K
                        block.timestamp - args.timestamps[i] <=
    307
    ✓ 6.2K
                        _strategyData[args.strategies[i]].maxStaleness
    308
                    ) {
    309
    ✓ 6.2K
                        ++chargeableCount;
    310
                    }
    311
                }
    312
            }
    313
    
                                                    
                                                
    314
            ///@dev Total upkeep cost is determined by the oracle based on the number of chargeable entries
    315
    ✓ 385.5K
            uint256 totalCost = paymentsEnabled
    316
    ✓ 7.6K
                ? SUPER_GOVERNOR.getUpkeepCostPerBatchUpdate(
    317
    ✓ 7.6K
                    msg.sender,
    318
                    chargeableCount
    319
                )
    320
    ✓ 377.9K
                : 0;
    321
    
                                                    
                                                
    322
            // Compute per-entry charge
    323
    ✓ 377.9K
            uint256 perEntry = 0;
    324
    ✓ 377.9K
            if (paymentsEnabled && chargeableCount > 0) {
    325
                perEntry = totalCost / chargeableCount;
    326
            }
    327
    
                                                    
                                                
    328
            // Process all valid strategies
    329
    ✓ 755.8K
            for (uint256 i; i < strategiesLength; ++i) {
    330
                // Skip invalid strategies without reverting
    331
    ✓ 377.9K
                if (!_superVaultStrategies.contains(args.strategies[i])) continue;
    332
    
                                                    
                                                
    333
                // Skip when invalid timestamp is provided (future timestamp)
    334
    ✓ 377.9K
                if (args.timestamps[i] > block.timestamp) {
    335
                    emit ProvidedTimestampExceedsBlockTimestamp(
    336
                        args.strategies[i],
    337
                        args.timestamps[i],
    338
                        block.timestamp
    339
                    );
    340
                    continue;
    341
                }
    342
    
                                                    
                                                
    343
    ✓ 377.9K
                uint256 upkeepCost;
    344
    ✓ 377.9K
                if (paymentsEnabled) {
    345
                    // check exemption due to staleness of a given strategy
    346
                    /// @dev cannot underflow as it's already checked above, in the previous `for` loop
    347
                    if (
    348
                        block.timestamp - args.timestamps[i] >
    349
                        _strategyData[args.strategies[i]].maxStaleness
    350
                    ) {
    351
                        upkeepCost = 0;
    352
                        emit StaleUpdate(
    353
                            args.strategies[i],
    354
                            args.updateAuthority,
    355
                            args.timestamps[i]
    356
                        );
    357
                    } else {
    358
                        address manager = _strategyData[args.strategies[i]]
    359
                            .mainManager;
    360
                        if (
    361
                            SUPER_GOVERNOR.isSuperformManager(manager) ||
    362
                            _strategyData[args.strategies[i]]
    363
                                .authorizedCallers
    364
                                .contains(args.updateAuthority)
    365
                        ) {
    366
                            upkeepCost = 0;
    367
                        } else {
    368
                            // Split the total batch cost fairly across chargeable entries
    369
                            upkeepCost = perEntry;
    370
                        }
    371
                    }
    372
                }
    373
    
                                                    
                                                
    374
                // Forward update
    375
    ✓ 377.9K
                _forwardPPS(
    376
                    PPSUpdateData({
    377
    ✓ 377.9K
                        strategy: args.strategies[i],
    378
    ✓ 377.9K
                        isExempt: (!paymentsEnabled) || (upkeepCost == 0), // If payments are disabled or the update is exempt from UP payments
    379
    ✓ 377.9K
                        pps: args.ppss[i],
    380
    ✓ 377.9K
                        ppsStdev: args.ppsStdevs[i],
    381
    ✓ 377.9K
                        validatorSet: args.validatorSets[i],
    382
    ✓ 377.9K
                        totalValidators: args.totalValidators[i],
    383
    ✓ 377.9K
                        timestamp: args.timestamps[i],
    384
    ✓ 377.9K
                        upkeepCost: upkeepCost
    385
                    })
    386
                );
    387
            }
    388
        }
    389
    
                                                    
                                                
    390
        /*//////////////////////////////////////////////////////////////
    391
                            UPKEEP MANAGEMENT
    392
        //////////////////////////////////////////////////////////////*/
    393
        /// @inheritdoc ISuperVaultAggregator
    394
        function depositUpkeep(address manager, uint256 amount) external {
    395
    ✓ 199.4K
    ⟲ 3.2K
            if (amount == 0) revert ZERO_AMOUNT();
    396
    
                                                    
                                                
    397
            // Get the UP token address from SUPER_GOVERNOR
    398
    ✓ 196.1K
            address upToken = SUPER_GOVERNOR.getAddress(SUPER_GOVERNOR.UP());
    399
    
                                                    
                                                
    400
            // Transfer UP tokens from msg.sender to this contract
    401
    ✓ 196.1K
            IERC20(upToken).safeTransferFrom(msg.sender, address(this), amount);
    402
    
                                                    
                                                
    403
            // Update upkeep balance
    404
    ✓ 21.2K
            _managerUpkeepBalance[manager] += amount;
    405
    
                                                    
                                                
    406
    ✓ 21.2K
            emit UpkeepDeposited(manager, amount);
    407
        }
    408
    
                                                    
                                                
    409
        /// @inheritdoc ISuperVaultAggregator
    410
        function claimUpkeep(uint256 amount) external {
    411
            // Only SUPER_GOVERNOR can claim upkeep
    412
    ✓ 331.8K
            if (msg.sender != address(SUPER_GOVERNOR)) {
    413
    ✓ 183.9K
    ⟲ 183.9K
                revert CALLER_NOT_AUTHORIZED();
    414
            }
    415
    
                                                    
                                                
    416
    ✓ 148.0K
    ⟲ 145.6K
            if (claimableUpkeep < amount) revert INSUFFICIENT_UPKEEP();
    417
    ✓ 2.4K
            claimableUpkeep -= amount;
    418
    
                                                    
                                                
    419
            // Get the UP token address from SUPER_GOVERNOR
    420
    ✓ 2.4K
            address upToken = SUPER_GOVERNOR.getAddress(SUPER_GOVERNOR.UP());
    421
    
                                                    
                                                
    422
            // Transfer UP tokens to `SuperBank`
    423
    ✓ 2.4K
            address _superBank = _getSuperBank();
    424
    ✓ 2.4K
            IERC20(upToken).safeTransfer(_superBank, amount);
    425
    ✓ 2.4K
            emit UpkeepClaimed(_superBank, amount);
    426
        }
    427
    
                                                    
                                                
    428
        /// @inheritdoc ISuperVaultAggregator
    429
        function withdrawUpkeep(uint256 amount) external {
    430
    ✓ 100.4K
    ⟲ 1.9K
            if (amount == 0) revert ZERO_AMOUNT();
    431
    
                                                    
                                                
    432
            // Check sufficient balance
    433
    ✓ 98.5K
            if (_managerUpkeepBalance[msg.sender] < amount) {
    434
    ✓ 98.0K
    ⟲ 98.0K
                revert INSUFFICIENT_UPKEEP_BALANCE();
    435
            }
    436
    
                                                    
                                                
    437
            // Get the UP token address from SUPER_GOVERNOR
    438
    ✓ 516
            address upToken = SUPER_GOVERNOR.getAddress(SUPER_GOVERNOR.UP());
    439
    
                                                    
                                                
    440
            // Update upkeep balance
    441
            unchecked {
    442
    ✓ 516
                _managerUpkeepBalance[msg.sender] -= amount;
    443
            }
    444
    
                                                    
                                                
    445
            // Transfer UP tokens to manager
    446
    ✓ 516
            IERC20(upToken).safeTransfer(msg.sender, amount);
    447
    
                                                    
                                                
    448
    ✓ 516
            emit UpkeepWithdrawn(msg.sender, amount);
    449
        }
    450
    
                                                    
                                                
    451
        /*//////////////////////////////////////////////////////////////
    452
                            STAKE MANAGEMENT
    453
        //////////////////////////////////////////////////////////////*/
    454
        /// @notice Deposits UP tokens as stake for manager economic security
    455
        /// @param manager Address of the manager to deposit stake for
    456
        /// @param amount Amount of UP tokens to deposit as stake
    457
        function depositStake(address manager, uint256 amount) external {
    458
    ✓ 120.9K
    ⟲ 4.8K
            if (amount == 0) revert ZERO_ADDRESS(); // Reusing error code for consistency
    459
    ✓ 116.1K
    ⟲ 1.0K
            if (manager == address(0)) revert ZERO_ADDRESS();
    460
    
                                                    
                                                
    461
            // Get the UP token address from SUPER_GOVERNOR
    462
    ✓ 115.0K
            address upToken = SUPER_GOVERNOR.getAddress(SUPER_GOVERNOR.UP());
    463
    
                                                    
                                                
    464
            // Transfer UP tokens from msg.sender to this contract
    465
    ✓ 115.0K
            IERC20(upToken).safeTransferFrom(msg.sender, address(this), amount);
    466
    
                                                    
                                                
    467
            // Update stake balance
    468
    ✓ 34.6K
            _managerStakeBalance[manager] += amount;
    469
    
                                                    
                                                
    470
    ✓ 58.2K
            emit StakeDeposited(manager, amount);
    471
        }
    472
    
                                                    
                                                
    473
        /// @notice Withdraws UP tokens from stake balance
    474
        /// @param amount Amount of UP tokens to withdraw from stake
    475
        function withdrawStake(uint256 amount) external {
    476
    ✓ 150.7K
    ⟲ 6.2K
            if (amount == 0) revert ZERO_ADDRESS(); // Reusing error code for consistency
    477
    
                                                    
                                                
    478
            // Check sufficient balance
    479
    ✓ 144.5K
            if (_managerStakeBalance[msg.sender] < amount) {
    480
    ✓ 144.4K
    ⟲ 144.4K
                revert INSUFFICIENT_STAKE_BALANCE();
    481
            }
    482
    
                                                    
                                                
    483
            // Get the UP token address from SUPER_GOVERNOR
    484
    ✓ 133
            address upToken = SUPER_GOVERNOR.getAddress(SUPER_GOVERNOR.UP());
    485
    
                                                    
                                                
    486
            // Update stake balance
    487
            unchecked {
    488
    ✓ 133
                _managerStakeBalance[msg.sender] -= amount;
    489
            }
    490
    
                                                    
                                                
    491
            // Transfer UP tokens to manager
    492
    ✓ 133
            IERC20(upToken).safeTransfer(msg.sender, amount);
    493
    
                                                    
                                                
    494
    ✓ 133
            emit StakeWithdrawn(msg.sender, amount);
    495
        }
    496
    
                                                    
                                                
    497
        /// @notice Slashes a manager's stake balance by a specified amount
    498
        /// @param manager The manager whose stake will be slashed
    499
        /// @param amount The amount of UP tokens to slash from the manager's stake balance
    500
        function slashStake(address manager, uint256 amount) external {
    501
            // Only SUPER_GOVERNOR can slash stake
    502
    ✓ 240.1K
            if (msg.sender != address(SUPER_GOVERNOR)) {
    503
    ✓ 240.1K
    ⟲ 240.1K
                revert CALLER_NOT_AUTHORIZED();
    504
            }
    505
    
                                                    
                                                
    506
            // Validate inputs
    507
            if (manager == address(0)) revert ZERO_ADDRESS();
    508
            if (amount == 0) revert ZERO_ADDRESS(); // Reusing error code for consistency
    509
    
                                                    
                                                
    510
            // Check if manager has sufficient stake balance to slash
    511
            if (_managerStakeBalance[manager] < amount) {
    512
                revert INSUFFICIENT_STAKE_BALANCE();
    513
            }
    514
    
                                                    
                                                
    515
            // Reduce manager's stake balance
    516
            _managerStakeBalance[manager] -= amount;
    517
    
                                                    
                                                
    518
            // Get the UP token address and SuperBank address
    519
            address upToken = SUPER_GOVERNOR.getAddress(SUPER_GOVERNOR.UP());
    520
            address superBank = _getSuperBank();
    521
    
                                                    
                                                
    522
            // Transfer slashed amount directly to SuperBank
    523
            IERC20(upToken).safeTransfer(superBank, amount);
    524
    
                                                    
                                                
    525
            // Emit event for transparency
    526
            emit StakeSlashed(manager, amount);
    527
        }
    528
    
                                                    
                                                
    529
        /*//////////////////////////////////////////////////////////////
    530
                            AUTHORIZED CALLER MANAGEMENT
    531
        //////////////////////////////////////////////////////////////*/
    532
        /// @inheritdoc ISuperVaultAggregator
    533
        function addAuthorizedCaller(
    534
            address strategy,
    535
            address caller
    536
    ✓ 175.1K
        ) external validStrategy(strategy) {
    537
            // Either primary or secondary manager can add authorized callers
    538
    ✓ 9.7K
            if (!isAnyManager(msg.sender, strategy))
    539
    ✓ 1.4K
    ⟲ 1.4K
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    540
    
                                                    
                                                
    541
    ✓ 8.3K
    ⟲ 600
            if (caller == address(0)) revert ZERO_ADDRESS();
    542
    
                                                    
                                                
    543
            // Prevent managers from adding protected keepers to circumvent fees
    544
    ✓ 7.7K
            if (SUPER_GOVERNOR.isProtectedKeeper(caller)) {
    545
                revert CANNOT_ADD_PROTECTED_KEEPER();
    546
            }
    547
    
                                                    
                                                
    548
            // Check if caller is already authorized and add if not
    549
    ✓ 7.7K
            if (!_strategyData[strategy].authorizedCallers.add(caller)) {
    550
    ✓ 698
    ⟲ 698
                revert CALLER_ALREADY_AUTHORIZED();
    551
            }
    552
    ✓ 7.0K
            emit AuthorizedCallerAdded(strategy, caller);
    553
        }
    554
    
                                                    
                                                
    555
        /// @inheritdoc ISuperVaultAggregator
    556
        function removeAuthorizedCaller(
    557
            address strategy,
    558
            address caller
    559
    ✓ 131.9K
        ) external validStrategy(strategy) {
    560
            // Either primary or secondary manager can remove authorized callers
    561
    ✓ 3.1K
            if (!isAnyManager(msg.sender, strategy))
    562
    ✓ 697
    ⟲ 697
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    563
    
                                                    
                                                
    564
            // Remove the caller
    565
    ✓ 2.4K
            if (!_strategyData[strategy].authorizedCallers.remove(caller)) {
    566
    ✓ 2.4K
    ⟲ 2.4K
                revert CALLER_NOT_AUTHORIZED();
    567
            }
    568
            emit AuthorizedCallerRemoved(strategy, caller);
    569
        }
    570
    
                                                    
                                                
    571
        /*//////////////////////////////////////////////////////////////
    572
                           MANAGER MANAGEMENT FUNCTIONS
    573
        //////////////////////////////////////////////////////////////*/
    574
        /// @inheritdoc ISuperVaultAggregator
    575
        function addSecondaryManager(
    576
            address strategy,
    577
            address manager
    578
    ✓ 110.2K
        ) external validStrategy(strategy) {
    579
            // Only the primary manager can add secondary managers
    580
    ✓ 3.3K
            if (msg.sender != _strategyData[strategy].mainManager)
    581
    ✓ 1.5K
    ⟲ 1.5K
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    582
    
                                                    
                                                
    583
    ✓ 1.8K
    ⟲ 80
            if (manager == address(0)) revert ZERO_ADDRESS();
    584
    
                                                    
                                                
    585
            // Check if manager is already the primary manager
    586
    ✓ 1.7K
            if (_strategyData[strategy].mainManager == manager)
    587
    ✓ 219
    ⟲ 219
                revert MANAGER_ALREADY_EXISTS();
    588
    
                                                    
                                                
    589
            // Enforce a cap on secondary managers to prevent governance DoS on changePrimaryManager
    590
            if (
    591
    ✓ 1.5K
                _strategyData[strategy].secondaryManagers.length() >=
    592
                MAX_SECONDARY_MANAGERS
    593
            ) {
    594
                revert TOO_MANY_SECONDARY_MANAGERS();
    595
            }
    596
    
                                                    
                                                
    597
            // Add as secondary manager using EnumerableSet
    598
    ✓ 1.5K
            if (!_strategyData[strategy].secondaryManagers.add(manager))
    599
                revert MANAGER_ALREADY_EXISTS();
    600
    
                                                    
                                                
    601
    ✓ 1.5K
            emit SecondaryManagerAdded(strategy, manager);
    602
        }
    603
    
                                                    
                                                
    604
        /// @inheritdoc ISuperVaultAggregator
    605
        function removeSecondaryManager(
    606
            address strategy,
    607
            address manager
    608
    ✓ 160.4K
        ) external validStrategy(strategy) {
    609
            // Only the primary manager can remove secondary managers
    610
    ✓ 2.5K
            if (msg.sender != _strategyData[strategy].mainManager)
    611
    ✓ 45
    ⟲ 45
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    612
    
                                                    
                                                
    613
            // Remove the manager using EnumerableSet
    614
    ✓ 2.5K
            if (!_strategyData[strategy].secondaryManagers.remove(manager))
    615
    ✓ 1.1K
    ⟲ 1.1K
                revert MANAGER_NOT_FOUND();
    616
    
                                                    
                                                
    617
    ✓ 1.3K
            emit SecondaryManagerRemoved(strategy, manager);
    618
        }
    619
    
                                                    
                                                
    620
        /// @inheritdoc ISuperVaultAggregator
    621
        function updatePPSVerificationThresholds(
    622
            address strategy,
    623
            uint256 dispersionThreshold_,
    624
            uint256 deviationThreshold_,
    625
            uint256 mnThreshold_
    626
    ✓ 121.3K
        ) external validStrategy(strategy) {
    627
            // Since this is a risky call, we only allow main managers as callers
    628
    ✓ 13.5K
            if (msg.sender != _strategyData[strategy].mainManager) {
    629
    ✓ 1.4K
    ⟲ 1.4K
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    630
            }
    631
    
                                                    
                                                
    632
            // Update the thresholds
    633
    ✓ 12.1K
            _strategyData[strategy].dispersionThreshold = dispersionThreshold_;
    634
    ✓ 12.1K
            _strategyData[strategy].deviationThreshold = deviationThreshold_;
    635
    ✓ 12.1K
            _strategyData[strategy].mnThreshold = mnThreshold_;
    636
    
                                                    
                                                
    637
            // Emit the event
    638
    ✓ 12.1K
            emit PPSVerificationThresholdsUpdated(
    639
                strategy,
    640
                dispersionThreshold_,
    641
                deviationThreshold_,
    642
                mnThreshold_
    643
            );
    644
        }
    645
    
                                                    
                                                
    646
        /// @inheritdoc ISuperVaultAggregator
    647
        function changeGlobalLeavesStatus(
    648
            bytes32[] memory leaves,
    649
            bool[] memory statuses,
    650
            address strategy
    651
        ) external validStrategy(strategy) {
    652
            // Only the primary manager can change global leaves status
    653
            if (msg.sender != _strategyData[strategy].mainManager) {
    654
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    655
            }
    656
            uint256 leavesLen = leaves.length;
    657
            // Check array lengths match
    658
            if (leavesLen != statuses.length) {
    659
                revert MISMATCHED_ARRAY_LENGTHS();
    660
            }
    661
    
                                                    
                                                
    662
            // Update banned status for each leaf
    663
            for (uint256 i; i < leavesLen; i++) {
    664
                _strategyData[strategy].bannedLeaves[leaves[i]] = statuses[i];
    665
            }
    666
    
                                                    
                                                
    667
            // Emit event
    668
            emit GlobalLeavesStatusChanged(strategy, leaves, statuses);
    669
        }
    670
    
                                                    
                                                
    671
        /// @inheritdoc ISuperVaultAggregator
    672
        function changePrimaryManager(
    673
            address strategy,
    674
            address newManager
    675
    ✓ 321.8K
        ) external validStrategy(strategy) {
    676
            // Only SuperGovernor can call this
    677
    ✓ 150.5K
            if (msg.sender != address(SUPER_GOVERNOR)) {
    678
    ✓ 860
    ⟲ 860
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    679
            }
    680
    
                                                    
                                                
    681
    ✓ 149.6K
            if (strategy == address(0)) revert ZERO_ADDRESS();
    682
    
                                                    
                                                
    683
    ✓ 149.6K
            if (newManager == address(0)) revert ZERO_ADDRESS();
    684
    
                                                    
                                                
    685
    ✓ 149.6K
            address oldManager = _strategyData[strategy].mainManager;
    686
    
                                                    
                                                
    687
            // SECURITY: Clear any pending manager proposals to prevent malicious re-takeover
    688
    ✓ 149.6K
            _strategyData[strategy].proposedManager = address(0);
    689
    ✓ 149.6K
            _strategyData[strategy].managerChangeEffectiveTime = 0;
    690
    
                                                    
                                                
    691
            // SECURITY: Clear any pending hooks root proposals to prevent malicious hook updates
    692
    ✓ 149.6K
            _strategyData[strategy].proposedHooksRoot = bytes32(0);
    693
    ✓ 149.6K
            _strategyData[strategy].hooksRootEffectiveTime = 0;
    694
    
                                                    
                                                
    695
            // SECURITY: Clear all secondary managers as they may be controlled by malicious manager
    696
            // Get all secondary managers first to emit proper events
    697
    ✓ 149.6K
            address[] memory clearedSecondaryManagers = _strategyData[strategy]
    698
                .secondaryManagers
    699
                .values();
    700
    
                                                    
                                                
    701
            // Clear the entire secondary managers set
    702
    ✓ 175.6K
            for (uint256 i = 0; i < clearedSecondaryManagers.length; i++) {
    703
    ✓ 26.0K
                _strategyData[strategy].secondaryManagers.remove(
    704
    ✓ 26.0K
                    clearedSecondaryManagers[i]
    705
                );
    706
    ✓ 26.0K
                emit SecondaryManagerRemoved(strategy, clearedSecondaryManagers[i]);
    707
            }
    708
    
                                                    
                                                
    709
            // Set the new primary manager
    710
    ✓ 149.6K
            _strategyData[strategy].mainManager = newManager;
    711
    
                                                    
                                                
    712
    ✓ 149.6K
            emit PrimaryManagerChanged(strategy, oldManager, newManager);
    713
        }
    714
    
                                                    
                                                
    715
        /// @inheritdoc ISuperVaultAggregator
    716
        function proposeChangePrimaryManager(
    717
            address strategy,
    718
            address newManager
    719
    ✓ 296.7K
        ) external validStrategy(strategy) {
    720
            // Only secondary managers can propose changes to the primary manager
    721
    ✓ 196.5K
            if (!_strategyData[strategy].secondaryManagers.contains(msg.sender)) {
    722
    ✓ 189.7K
    ⟲ 189.7K
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    723
            }
    724
    
                                                    
                                                
    725
    ✓ 6.8K
            if (newManager == address(0)) revert ZERO_ADDRESS();
    726
    
                                                    
                                                
    727
            // Set up the proposal with 7-day timelock
    728
    ✓ 6.8K
            uint256 effectiveTime = block.timestamp + _MANAGER_CHANGE_TIMELOCK;
    729
    
                                                    
                                                
    730
            // Store proposal in the strategy data
    731
    ✓ 6.8K
            _strategyData[strategy].proposedManager = newManager;
    732
    ✓ 6.8K
            _strategyData[strategy].managerChangeEffectiveTime = effectiveTime;
    733
    
                                                    
                                                
    734
            emit PrimaryManagerChangeProposed(
    735
                strategy,
    736
    ✓ 6.8K
                msg.sender,
    737
                newManager,
    738
                effectiveTime
    739
            );
    740
        }
    741
    
                                                    
                                                
    742
        /// @inheritdoc ISuperVaultAggregator
    743
        function executeChangePrimaryManager(
    744
            address strategy
    745
    ✓ 296.7K
        ) external validStrategy(strategy) {
    746
            // Check if there is a pending proposal
    747
    ✓ 139.8K
            if (_strategyData[strategy].proposedManager == address(0))
    748
    ✓ 136.4K
    ⟲ 136.4K
                revert NO_PENDING_MANAGER_CHANGE();
    749
    
                                                    
                                                
    750
            // Check if the timelock period has passed
    751
            if (
    752
    ✓ 3.4K
                block.timestamp < _strategyData[strategy].managerChangeEffectiveTime
    753
    ✓ 1.5K
    ⟲ 1.5K
            ) revert TIMELOCK_NOT_EXPIRED();
    754
    
                                                    
                                                
    755
    ✓ 2.0K
            address newManager = _strategyData[strategy].proposedManager;
    756
    ✓ 2.0K
            address oldManager = _strategyData[strategy].mainManager;
    757
    
                                                    
                                                
    758
            // If new manager is already a secondary manager, remove them
    759
    ✓ 2.0K
            _strategyData[strategy].secondaryManagers.remove(newManager);
    760
    
                                                    
                                                
    761
            // Make the old primary manager a secondary manager
    762
            if (
    763
    ✓ 2.0K
                _strategyData[strategy].secondaryManagers.length() <
    764
                MAX_SECONDARY_MANAGERS
    765
            ) {
    766
    ✓ 2.0K
                _strategyData[strategy].secondaryManagers.add(oldManager);
    767
            }
    768
    
                                                    
                                                
    769
            // Set the new primary manager
    770
    ✓ 2.0K
            _strategyData[strategy].mainManager = newManager;
    771
    
                                                    
                                                
    772
            // Clear the proposal
    773
    ✓ 2.0K
            _strategyData[strategy].proposedManager = address(0);
    774
    
                                                    
                                                
    775
    ✓ 2.0K
            emit PrimaryManagerChanged(strategy, oldManager, newManager);
    776
        }
    777
    
                                                    
                                                
    778
        /*//////////////////////////////////////////////////////////////
    779
                            HOOK VALIDATION FUNCTIONS
    780
        //////////////////////////////////////////////////////////////*/
    781
        /// @inheritdoc ISuperVaultAggregator
    782
        function setHooksRootUpdateTimelock(uint256 newTimelock) external {
    783
            // Only SUPER_GOVERNOR can update the timelock
    784
            if (msg.sender != address(SUPER_GOVERNOR)) {
    785
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    786
            }
    787
    
                                                    
                                                
    788
            // Update the timelock
    789
            _hooksRootUpdateTimelock = newTimelock;
    790
    
                                                    
                                                
    791
            emit HooksRootUpdateTimelockChanged(newTimelock);
    792
        }
    793
    
                                                    
                                                
    794
        /// @inheritdoc ISuperVaultAggregator
    795
        function proposeGlobalHooksRoot(bytes32 newRoot) external {
    796
            // Only SUPER_GOVERNOR can update the global hooks root
    797
    ✓ 218.2K
            if (msg.sender != address(SUPER_GOVERNOR)) {
    798
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    799
            }
    800
    
                                                    
                                                
    801
            // Set new root with timelock
    802
    ✓ 218.2K
            _proposedGlobalHooksRoot = newRoot;
    803
    ✓ 218.2K
            uint256 effectiveTime = block.timestamp + _hooksRootUpdateTimelock;
    804
    ✓ 218.2K
            _globalHooksRootEffectiveTime = effectiveTime;
    805
    
                                                    
                                                
    806
    ✓ 218.2K
            emit GlobalHooksRootUpdateProposed(newRoot, effectiveTime);
    807
        }
    808
    
                                                    
                                                
    809
        /// @inheritdoc ISuperVaultAggregator
    810
        function executeGlobalHooksRootUpdate() external {
    811
            bytes32 proposedRoot = _proposedGlobalHooksRoot;
    812
            // Ensure there is a pending proposal
    813
            if (proposedRoot == bytes32(0)) {
    814
                revert NO_PENDING_GLOBAL_ROOT_CHANGE();
    815
            }
    816
    
                                                    
                                                
    817
            // Check if timelock period has elapsed
    818
            if (block.timestamp < _globalHooksRootEffectiveTime) {
    819
                revert ROOT_UPDATE_NOT_READY();
    820
            }
    821
    
                                                    
                                                
    822
            // Update the global hooks root
    823
            bytes32 oldRoot = _globalHooksRoot;
    824
            _globalHooksRoot = _proposedGlobalHooksRoot;
    825
            _globalHooksRootEffectiveTime = 0;
    826
            _proposedGlobalHooksRoot = bytes32(0);
    827
    
                                                    
                                                
    828
    ✓ 218.8K
            emit GlobalHooksRootUpdated(oldRoot, proposedRoot);
    829
        }
    830
    
                                                    
                                                
    831
        /// @inheritdoc ISuperVaultAggregator
    832
        function setGlobalHooksRootVetoStatus(bool vetoed) external {
    833
            // Only SuperGovernor can call this
    834
            if (msg.sender != address(SUPER_GOVERNOR)) {
    835
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    836
            }
    837
    
                                                    
                                                
    838
            // Don't emit event if status doesn't change
    839
            if (_globalHooksRootVetoed == vetoed) {
    840
                return;
    841
            }
    842
    
                                                    
                                                
    843
            // Update veto status
    844
            _globalHooksRootVetoed = vetoed;
    845
    
                                                    
                                                
    846
            emit GlobalHooksRootVetoStatusChanged(vetoed, _globalHooksRoot);
    847
        }
    848
    
                                                    
                                                
    849
        /// @inheritdoc ISuperVaultAggregator
    850
        function proposeStrategyHooksRoot(
    851
            address strategy,
    852
            bytes32 newRoot
    853
        ) external validStrategy(strategy) {
    854
            // Only the main manager can propose strategy-specific hooks root
    855
            if (_strategyData[strategy].mainManager != msg.sender) {
    856
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    857
            }
    858
    
                                                    
                                                
    859
            // Set proposed root with timelock
    860
            _strategyData[strategy].proposedHooksRoot = newRoot;
    861
            uint256 effectiveTime = block.timestamp + _hooksRootUpdateTimelock;
    862
            _strategyData[strategy].hooksRootEffectiveTime = effectiveTime;
    863
    
                                                    
                                                
    864
            emit StrategyHooksRootUpdateProposed(
    865
                strategy,
    866
                msg.sender,
    867
                newRoot,
    868
                effectiveTime
    869
            );
    870
        }
    871
    
                                                    
                                                
    872
        /// @inheritdoc ISuperVaultAggregator
    873
        function executeStrategyHooksRootUpdate(
    874
            address strategy
    875
        ) external validStrategy(strategy) {
    876
            bytes32 proposedRoot = _strategyData[strategy].proposedHooksRoot;
    877
            // Ensure there is a pending proposal
    878
            if (proposedRoot == bytes32(0)) {
    879
                revert NO_PENDING_MANAGER_CHANGE(); // Reusing error for simplicity
    880
            }
    881
    
                                                    
                                                
    882
            // Check if timelock period has elapsed
    883
            if (block.timestamp < _strategyData[strategy].hooksRootEffectiveTime) {
    884
                revert ROOT_UPDATE_NOT_READY();
    885
            }
    886
    
                                                    
                                                
    887
            // Update the strategy's hooks root
    888
            bytes32 oldRoot = _strategyData[strategy].managerHooksRoot;
    889
            _strategyData[strategy].managerHooksRoot = proposedRoot;
    890
    
                                                    
                                                
    891
            // Reset proposal state
    892
            _strategyData[strategy].proposedHooksRoot = bytes32(0);
    893
            _strategyData[strategy].hooksRootEffectiveTime = 0;
    894
    
                                                    
                                                
    895
            emit StrategyHooksRootUpdated(strategy, oldRoot, proposedRoot);
    896
        }
    897
    
                                                    
                                                
    898
        /// @inheritdoc ISuperVaultAggregator
    899
        function setStrategyHooksRootVetoStatus(
    900
            address strategy,
    901
            bool vetoed
    902
        ) external validStrategy(strategy) {
    903
            // Only SuperGovernor can call this
    904
            if (msg.sender != address(SUPER_GOVERNOR)) {
    905
                revert UNAUTHORIZED_UPDATE_AUTHORITY();
    906
            }
    907
    
                                                    
                                                
    908
            // Don't emit event if status doesn't change
    909
            if (_strategyData[strategy].hooksRootVetoed == vetoed) {
    910
                return;
    911
            }
    912
    
                                                    
                                                
    913
            // Update veto status
    914
            _strategyData[strategy].hooksRootVetoed = vetoed;
    915
    
                                                    
                                                
    916
            emit StrategyHooksRootVetoStatusChanged(
    917
                strategy,
    918
                vetoed,
    919
                _strategyData[strategy].managerHooksRoot
    920
            );
    921
        }
    922
        /// @inheritdoc ISuperVaultAggregator
    923
    
                                                    
                                                
    924
        function isGlobalHooksRootVetoed() external view returns (bool vetoed) {
    925
    ✓ 554.4K
            return _globalHooksRootVetoed;
    926
        }
    927
    
                                                    
                                                
    928
        /// @inheritdoc ISuperVaultAggregator
    929
        function isStrategyHooksRootVetoed(
    930
            address strategy
    931
        ) external view returns (bool vetoed) {
    932
            return _strategyData[strategy].hooksRootVetoed;
    933
        }
    934
    
                                                    
                                                
    935
        /*//////////////////////////////////////////////////////////////
    936
                                  VIEW FUNCTIONS
    937
        //////////////////////////////////////////////////////////////*/
    938
    
                                                    
                                                
    939
        /// @inheritdoc ISuperVaultAggregator
    940
        function getCurrentNonce() external view returns (uint256) {
    941
            return _vaultCreationNonce;
    942
        }
    943
    
                                                    
                                                
    944
        /// @inheritdoc ISuperVaultAggregator
    945
        function getHooksRootUpdateTimelock() external view returns (uint256) {
    946
            return _hooksRootUpdateTimelock;
    947
        }
    948
    
                                                    
                                                
    949
        /// @inheritdoc ISuperVaultAggregator
    950
        function getPPS(
    951
            address strategy
    952
    ✓ 19.7M
        ) external view validStrategy(strategy) returns (uint256 pps) {
    953
    ✓ 19.7M
            return _strategyData[strategy].pps;
    954
        }
    955
    
                                                    
                                                
    956
        /// @inheritdoc ISuperVaultAggregator
    957
        function getPPSWithStdDev(
    958
            address strategy
    959
        )
    960
            external
    961
            view
    962
            validStrategy(strategy)
    963
            returns (uint256 pps, uint256 ppsStdev)
    964
        {
    965
            return (_strategyData[strategy].pps, _strategyData[strategy].ppsStdev);
    966
        }
    967
    
                                                    
                                                
    968
        /// @inheritdoc ISuperVaultAggregator
    969
        function getLastUpdateTimestamp(
    970
            address strategy
    971
        ) external view returns (uint256 timestamp) {
    972
            return _strategyData[strategy].lastUpdateTimestamp;
    973
        }
    974
    
                                                    
                                                
    975
        /// @inheritdoc ISuperVaultAggregator
    976
        function getMinUpdateInterval(
    977
            address strategy
    978
        ) external view returns (uint256 interval) {
    979
            return _strategyData[strategy].minUpdateInterval;
    980
        }
    981
    
                                                    
                                                
    982
        /// @inheritdoc ISuperVaultAggregator
    983
        function getMaxStaleness(
    984
            address strategy
    985
        ) external view returns (uint256 staleness) {
    986
            return _strategyData[strategy].maxStaleness;
    987
        }
    988
    
                                                    
                                                
    989
        /// @inheritdoc ISuperVaultAggregator
    990
        function getPPSVerificationThresholds(
    991
            address strategy
    992
        )
    993
            external
    994
            view
    995
            validStrategy(strategy)
    996
            returns (
    997
                uint256 dispersionThreshold,
    998
                uint256 deviationThreshold,
    999
                uint256 mnThreshold
    1000
            )
    1001
        {
    1002
            return (
    1003
                _strategyData[strategy].dispersionThreshold,
    1004
                _strategyData[strategy].deviationThreshold,
    1005
                _strategyData[strategy].mnThreshold
    1006
            );
    1007
        }
    1008
    
                                                    
                                                
    1009
        /// @inheritdoc ISuperVaultAggregator
    1010
        function isStrategyPaused(
    1011
            address strategy
    1012
    ✓ 1.6M
        ) external view returns (bool isPaused) {
    1013
    ✓ 1.6M
            return _strategyData[strategy].isPaused;
    1014
        }
    1015
    
                                                    
                                                
    1016
        /// @inheritdoc ISuperVaultAggregator
    1017
        function getUpkeepBalance(
    1018
            address manager
    1019
        ) external view returns (uint256 balance) {
    1020
            return _managerUpkeepBalance[manager];
    1021
        }
    1022
    
                                                    
                                                
    1023
        /// @notice Gets the current stake balance for a manager
    1024
        /// @param manager Address of the manager
    1025
        /// @return balance Current stake balance in UP tokens
    1026
        function getStakeBalance(
    1027
            address manager
    1028
        ) external view returns (uint256 balance) {
    1029
            return _managerStakeBalance[manager];
    1030
        }
    1031
    
                                                    
                                                
    1032
        /// @inheritdoc ISuperVaultAggregator
    1033
        function getAuthorizedCallers(
    1034
            address strategy
    1035
        ) external view returns (address[] memory callers) {
    1036
            return _strategyData[strategy].authorizedCallers.values();
    1037
        }
    1038
    
                                                    
                                                
    1039
        /// @inheritdoc ISuperVaultAggregator
    1040
        function getMainManager(
    1041
            address strategy
    1042
        ) external view returns (address manager) {
    1043
            manager = _strategyData[strategy].mainManager;
    1044
            if (manager == address(0)) revert ZERO_ADDRESS();
    1045
    
                                                    
                                                
    1046
            return manager;
    1047
        }
    1048
    
                                                    
                                                
    1049
        /// @inheritdoc ISuperVaultAggregator
    1050
        function isMainManager(
    1051
            address manager,
    1052
            address strategy
    1053
    ✓ 1.0M
        ) external view returns (bool) {
    1054
    ✓ 1.0M
            return _strategyData[strategy].mainManager == manager;
    1055
        }
    1056
    
                                                    
                                                
    1057
        /// @inheritdoc ISuperVaultAggregator
    1058
        function getSecondaryManagers(
    1059
            address strategy
    1060
        ) external view returns (address[] memory) {
    1061
            return _strategyData[strategy].secondaryManagers.values();
    1062
        }
    1063
    
                                                    
                                                
    1064
        /// @inheritdoc ISuperVaultAggregator
    1065
        function isSecondaryManager(
    1066
            address manager,
    1067
            address strategy
    1068
        ) external view returns (bool) {
    1069
            return _strategyData[strategy].secondaryManagers.contains(manager);
    1070
        }
    1071
    
                                                    
                                                
    1072
        /// @inheritdoc ISuperVaultAggregator
    1073
        function isAnyManager(
    1074
            address manager,
    1075
            address strategy
    1076
    ✓ 883.6K
        ) public view returns (bool) {
    1077
            // Check if primary manager
    1078
    ✓ 883.6K
            if (_strategyData[strategy].mainManager == manager) {
    1079
    ✓ 874.3K
                return true;
    1080
            }
    1081
    
                                                    
                                                
    1082
            // Check if secondary manager using EnumerableSet
    1083
    ✓ 9.3K
            return _strategyData[strategy].secondaryManagers.contains(manager);
    1084
        }
    1085
    
                                                    
                                                
    1086
        /// @inheritdoc ISuperVaultAggregator
    1087
        function getAllSuperVaults() external view returns (address[] memory) {
    1088
            return _superVaults.values();
    1089
        }
    1090
    
                                                    
                                                
    1091
        /// @inheritdoc ISuperVaultAggregator
    1092
        function superVaults(uint256 index) external view returns (address) {
    1093
            if (index >= _superVaults.length()) revert INDEX_OUT_OF_BOUNDS();
    1094
            return _superVaults.at(index);
    1095
        }
    1096
    
                                                    
                                                
    1097
        /// @inheritdoc ISuperVaultAggregator
    1098
        function getAllSuperVaultStrategies()
    1099
            external
    1100
            view
    1101
            returns (address[] memory)
    1102
        {
    1103
            return _superVaultStrategies.values();
    1104
        }
    1105
    
                                                    
                                                
    1106
        /// @inheritdoc ISuperVaultAggregator
    1107
        function superVaultStrategies(
    1108
            uint256 index
    1109
        ) external view returns (address) {
    1110
            if (index >= _superVaultStrategies.length())
    1111
                revert INDEX_OUT_OF_BOUNDS();
    1112
            return _superVaultStrategies.at(index);
    1113
        }
    1114
    
                                                    
                                                
    1115
        /// @inheritdoc ISuperVaultAggregator
    1116
        function getAllSuperVaultEscrows()
    1117
            external
    1118
            view
    1119
            returns (address[] memory)
    1120
        {
    1121
            return _superVaultEscrows.values();
    1122
        }
    1123
    
                                                    
                                                
    1124
        /// @inheritdoc ISuperVaultAggregator
    1125
        function superVaultEscrows(uint256 index) external view returns (address) {
    1126
            if (index >= _superVaultEscrows.length()) revert INDEX_OUT_OF_BOUNDS();
    1127
            return _superVaultEscrows.at(index);
    1128
        }
    1129
    
                                                    
                                                
    1130
        /// @inheritdoc ISuperVaultAggregator
    1131
        function validateHook(
    1132
            address strategy,
    1133
            ValidateHookArgs calldata args
    1134
        ) external view virtual returns (bool isValid) {
    1135
            // Cache all state variables in struct
    1136
            HookValidationCache memory cache = HookValidationCache({
    1137
                globalHooksRootVetoed: _globalHooksRootVetoed,
    1138
                globalHooksRoot: _globalHooksRoot,
    1139
                strategyHooksRootVetoed: _strategyData[strategy].hooksRootVetoed,
    1140
                strategyRoot: _strategyData[strategy].managerHooksRoot
    1141
            });
    1142
    
                                                    
                                                
    1143
            // Early return false if either global or strategy hooks root is vetoed
    1144
            if (cache.globalHooksRootVetoed || cache.strategyHooksRootVetoed) {
    1145
                return false;
    1146
            }
    1147
    
                                                    
                                                
    1148
            // Try to validate against global root first
    1149
            if (
    1150
                _validateSingleHook(
    1151
                    args.hookAddress,
    1152
                    args.hookArgs,
    1153
                    args.globalProof,
    1154
                    true,
    1155
                    cache,
    1156
                    strategy
    1157
                )
    1158
            ) {
    1159
                return true;
    1160
            }
    1161
    
                                                    
                                                
    1162
            // If global validation fails, try strategy root
    1163
            return
    1164
                _validateSingleHook(
    1165
                    args.hookAddress,
    1166
                    args.hookArgs,
    1167
                    args.strategyProof,
    1168
                    false,
    1169
                    cache,
    1170
                    strategy
    1171
                );
    1172
        }
    1173
    
                                                    
                                                
    1174
        /// @inheritdoc ISuperVaultAggregator
    1175
        function validateHooks(
    1176
            address strategy,
    1177
            ValidateHookArgs[] calldata argsArray
    1178
        ) external view returns (bool[] memory validHooks) {
    1179
            uint256 length = argsArray.length;
    1180
    
                                                    
                                                
    1181
            // Cache all state variables in struct
    1182
            HookValidationCache memory cache = HookValidationCache({
    1183
                globalHooksRootVetoed: _globalHooksRootVetoed,
    1184
                globalHooksRoot: _globalHooksRoot,
    1185
                strategyHooksRootVetoed: _strategyData[strategy].hooksRootVetoed,
    1186
                strategyRoot: _strategyData[strategy].managerHooksRoot
    1187
            });
    1188
    
                                                    
                                                
    1189
            // Early return all false if either global or strategy hooks root is vetoed
    1190
            if (cache.globalHooksRootVetoed || cache.strategyHooksRootVetoed) {
    1191
                return new bool[](length); // Array initialized with all false values
    1192
            }
    1193
    
                                                    
                                                
    1194
            // Validate each hook
    1195
            validHooks = new bool[](length);
    1196
            for (uint256 i; i < length; i++) {
    1197
                // Try global root first
    1198
                if (
    1199
                    _validateSingleHook(
    1200
                        argsArray[i].hookAddress,
    1201
                        argsArray[i].hookArgs,
    1202
                        argsArray[i].globalProof,
    1203
                        true,
    1204
                        cache,
    1205
                        strategy
    1206
                    )
    1207
                ) {
    1208
                    validHooks[i] = true;
    1209
                } else {
    1210
                    // Try strategy root
    1211
                    validHooks[i] = _validateSingleHook(
    1212
                        argsArray[i].hookAddress,
    1213
                        argsArray[i].hookArgs,
    1214
                        argsArray[i].strategyProof,
    1215
                        false,
    1216
                        cache,
    1217
                        strategy
    1218
                    );
    1219
                }
    1220
                // If both conditions fail, validHooks[i] remains false (default value)
    1221
            }
    1222
    
                                                    
                                                
    1223
            return validHooks;
    1224
        }
    1225
    
                                                    
                                                
    1226
        /// @inheritdoc ISuperVaultAggregator
    1227
        function getGlobalHooksRoot() external view returns (bytes32 root) {
    1228
            return _globalHooksRoot;
    1229
        }
    1230
    
                                                    
                                                
    1231
        /// @inheritdoc ISuperVaultAggregator
    1232
        function getProposedGlobalHooksRoot()
    1233
            external
    1234
            view
    1235
            returns (bytes32 root, uint256 effectiveTime)
    1236
        {
    1237
            return (_proposedGlobalHooksRoot, _globalHooksRootEffectiveTime);
    1238
        }
    1239
    
                                                    
                                                
    1240
        /// @notice Checks if the global hooks root is active (timelock period has passed)
    1241
        /// @return isActive True if the global hooks root is active
    1242
        function isGlobalHooksRootActive() external view returns (bool) {
    1243
            return
    1244
                block.timestamp >= _globalHooksRootEffectiveTime &&
    1245
                _globalHooksRoot != bytes32(0);
    1246
        }
    1247
    
                                                    
                                                
    1248
        /// @inheritdoc ISuperVaultAggregator
    1249
        function getStrategyHooksRoot(
    1250
            address strategy
    1251
        ) external view returns (bytes32 root) {
    1252
            return _strategyData[strategy].managerHooksRoot;
    1253
        }
    1254
    
                                                    
                                                
    1255
        /// @inheritdoc ISuperVaultAggregator
    1256
        function getProposedStrategyHooksRoot(
    1257
            address strategy
    1258
        ) external view returns (bytes32 root, uint256 effectiveTime) {
    1259
            return (
    1260
                _strategyData[strategy].proposedHooksRoot,
    1261
                _strategyData[strategy].hooksRootEffectiveTime
    1262
            );
    1263
        }
    1264
    
                                                    
                                                
    1265
        /*//////////////////////////////////////////////////////////////
    1266
                             INTERNAL HELPER FUNCTIONS
    1267
        //////////////////////////////////////////////////////////////*/
    1268
        /// @notice Internal implementation of forwarding PPS updates
    1269
        /// @param args Struct containing all parameters for PPS update
    1270
        function _forwardPPS(PPSUpdateData memory args) internal {
    1271
            // Check rate limiting
    1272
    ✓ 377.9K
            uint256 minInterval = _strategyData[args.strategy].minUpdateInterval;
    1273
    ✓ 377.9K
            uint256 lastUpdate = _strategyData[args.strategy].lastUpdateTimestamp;
    1274
    ✓ 377.9K
            if (block.timestamp - lastUpdate < minInterval) {
    1275
    ✓ 1.3K
                emit UpdateTooFrequent();
    1276
    ✓ 1.3K
                return;
    1277
            }
    1278
    
                                                    
                                                
    1279
            // Ensure timestamp is monotonically increasing to prevent out-of-order updates
    1280
    ✓ 376.6K
            if (args.timestamp <= lastUpdate) {
    1281
    ✓ 1.6K
                emit TimestampNotMonotonic();
    1282
    ✓ 1.6K
                return;
    1283
            }
    1284
    
                                                    
                                                
    1285
            // Get the strategy's manager to deduct upkeep cost from
    1286
    ✓ 375.0K
            address manager = _strategyData[args.strategy].mainManager;
    1287
    
                                                    
                                                
    1288
            // Flag to track if any check failed
    1289
            bool checksFailed = false;
    1290
    
                                                    
                                                
    1291
            // C2.1) Dispersion Check: Check if the standard deviation is too high relative to mean
    1292
            if (
    1293
    ✓ 375.0K
                _strategyData[args.strategy].dispersionThreshold !=
    1294
                type(uint256).max &&
    1295
    ✓ 17.3K
                args.pps > 0
    1296
            ) {
    1297
                // Calculate dispersion as stddev/mean
    1298
    ✓ 16.0K
                uint256 dispersion = (args.ppsStdev * 1e18) / args.pps; // Scaled by 1e18 for precision
    1299
    ✓ 16.0K
                if (dispersion > _strategyData[args.strategy].dispersionThreshold) {
    1300
                    checksFailed = true;
    1301
                    emit StrategyCheckFailed(args.strategy, "HIGH_PPS_DISPERSION");
    1302
                }
    1303
            }
    1304
    
                                                    
                                                
    1305
            // C2.2) Deviation Check: Check if new PPS deviates too much from current PPS
    1306
    ✓ 375.0K
            uint256 currentPPS = _strategyData[args.strategy].pps;
    1307
            if (
    1308
    ✓ 375.0K
                _strategyData[args.strategy].deviationThreshold !=
    1309
                type(uint256).max &&
    1310
    ✓ 17.3K
                currentPPS > 0
    1311
            ) {
    1312
                // Calculate absolute deviation, scaled by 1e18
    1313
    ✓ 16.1K
                uint256 absDiff = args.pps > currentPPS
    1314
    ✓ 1.3K
                    ? (args.pps - currentPPS)
    1315
    ✓ 14.8K
                    : (currentPPS - args.pps);
    1316
    ✓ 16.1K
                uint256 relativeDeviation = (absDiff * 1e18) / currentPPS;
    1317
                if (
    1318
                    relativeDeviation >
    1319
    ✓ 16.1K
                    _strategyData[args.strategy].deviationThreshold
    1320
                ) {
    1321
    ✓ 15.7K
                    checksFailed = true;
    1322
    ✓ 15.7K
                    emit StrategyCheckFailed(args.strategy, "HIGH_PPS_DEVIATION");
    1323
                }
    1324
            }
    1325
    
                                                    
                                                
    1326
            // C2.3) M/N Check: Check if enough validators participated
    1327
            if (
    1328
    ✓ 375.0K
                args.totalValidators > 0 &&
    1329
                _strategyData[args.strategy].mnThreshold > 0
    1330
            ) {
    1331
                // Calculate participation rate, scaled by 1e18
    1332
                uint256 participationRate = (args.validatorSet * 1e18) /
    1333
                    args.totalValidators;
    1334
                if (participationRate < _strategyData[args.strategy].mnThreshold) {
    1335
                    checksFailed = true;
    1336
                    emit StrategyCheckFailed(
    1337
                        args.strategy,
    1338
                        "INSUFFICIENT_VALIDATOR_PARTICIPATION"
    1339
                    );
    1340
                }
    1341
            }
    1342
    
                                                    
                                                
    1343
            // Pause strategy if any check failed
    1344
    ✓ 375.0K
            if (checksFailed && !_strategyData[args.strategy].isPaused) {
    1345
    ✓ 10.2K
                _strategyData[args.strategy].isPaused = true;
    1346
    ✓ 10.2K
                emit StrategyPaused(args.strategy);
    1347
            }
    1348
            // Unpause strategy if all checks passed and strategy was previously paused
    1349
    ✓ 364.8K
            else if (!checksFailed && _strategyData[args.strategy].isPaused) {
    1350
    ✓ 1.3K
                _strategyData[args.strategy].isPaused = false;
    1351
    ✓ 1.3K
                emit StrategyUnpaused(args.strategy);
    1352
            }
    1353
    
                                                    
                                                
    1354
            // Handle upkeep costs unless exempt
    1355
    ✓ 375.0K
            if (!args.isExempt) {
    1356
                // Check if manager has sufficient upkeep balance
    1357
                if (_managerUpkeepBalance[manager] < args.upkeepCost) {
    1358
                    emit InsufficientUpkeep(
    1359
                        args.strategy,
    1360
                        manager,
    1361
                        _managerUpkeepBalance[manager],
    1362
                        args.upkeepCost
    1363
                    );
    1364
                    return;
    1365
                }
    1366
    
                                                    
                                                
    1367
                // Deduct the upkeep cost and emit event
    1368
                _managerUpkeepBalance[manager] -= args.upkeepCost;
    1369
    
                                                    
                                                
    1370
                // Add claimable upkeep for the `feeRecipient`
    1371
                claimableUpkeep += args.upkeepCost;
    1372
    
                                                    
                                                
    1373
                emit UpkeepSpent(manager, args.upkeepCost);
    1374
            }
    1375
    
                                                    
                                                
    1376
            // Update PPS, ppsStdev and timestamp in StrategyData
    1377
    ✓ 375.0K
            _strategyData[args.strategy].pps = args.pps;
    1378
    ✓ 375.0K
            _strategyData[args.strategy].ppsStdev = args.ppsStdev;
    1379
    ✓ 375.0K
            _strategyData[args.strategy].lastUpdateTimestamp = args.timestamp;
    1380
    
                                                    
                                                
    1381
            emit PPSUpdated(
    1382
    ✓ 375.0K
                args.strategy,
    1383
    ✓ 375.0K
                args.pps,
    1384
    ✓ 375.0K
                args.ppsStdev,
    1385
    ✓ 375.0K
                args.validatorSet,
    1386
    ✓ 375.0K
                args.totalValidators,
    1387
    ✓ 375.0K
                args.timestamp
    1388
            );
    1389
        }
    1390
    
                                                    
                                                
    1391
        /// @notice Check if an update authority is exempt from paying upkeep costs
    1392
        /// @param strategy Address of the strategy being updated
    1393
        /// @param updateAuthority Address initiating the update
    1394
        /// @param timestamp Timestamp of the PPS measurement
    1395
        /// @return isExempt True if the authority is exempt from paying upkeep
    1396
        function _isExemptFromUpkeep(
    1397
            address strategy,
    1398
            address updateAuthority,
    1399
            uint256 timestamp
    1400
        ) internal returns (bool) {
    1401
            // Check if upkeep payments are globally disabled in SuperGovernor
    1402
            if (!SUPER_GOVERNOR.isUpkeepPaymentsEnabled()) {
    1403
                return true;
    1404
            }
    1405
    
                                                    
                                                
    1406
            // Update is exempt if it is stale
    1407
            if (
    1408
                block.timestamp - timestamp > _strategyData[strategy].maxStaleness
    1409
            ) {
    1410
                emit StaleUpdate(strategy, updateAuthority, timestamp);
    1411
                return true;
    1412
            }
    1413
    
                                                    
                                                
    1414
            // If manager is a superform manager, they're exempt from upkeep fees
    1415
            address manager = _strategyData[strategy].mainManager;
    1416
            if (SUPER_GOVERNOR.isSuperformManager(manager)) {
    1417
                return true;
    1418
            }
    1419
    
                                                    
                                                
    1420
            // Check if the updateAuthority is in the authorized callers list
    1421
            // These are manager-designated keepers that should be exempt from fees
    1422
            // NOTE: Protected keepers cannot be added to this list (blocked in addAuthorizedCaller)
    1423
            if (
    1424
                _strategyData[strategy].authorizedCallers.contains(updateAuthority)
    1425
            ) {
    1426
                return true;
    1427
            }
    1428
    
                                                    
                                                
    1429
            return false;
    1430
        }
    1431
    
                                                    
                                                
    1432
        /// @notice Creates a leaf node for Merkle verification from hook address and arguments
    1433
        /// @param hookAddress The address of the hook contract
    1434
        /// @param hookArgs The packed-encoded hook arguments (from solidityPack in JS)
    1435
        /// @return leaf The leaf node hash
    1436
        function _createLeaf(
    1437
            address hookAddress,
    1438
            bytes calldata hookArgs
    1439
        ) internal pure returns (bytes32) {
    1440
            /// @dev The leaf now includes both hook address and args to prevent cross-hook replay attacks
    1441
            /// @dev Different hooks with identical encoded args will have different authorization leaves
    1442
            /// @dev This matches StandardMerkleTree's standardLeafHash: keccak256(keccak256(abi.encode(hookAddress,
    1443
            /// hookArgs)))
    1444
            /// @dev but uses bytes.concat for explicit concatenation
    1445
            return
    1446
                keccak256(
    1447
                    bytes.concat(keccak256(abi.encode(hookAddress, hookArgs)))
    1448
                );
    1449
        }
    1450
    
                                                    
                                                
    1451
        /**
    1452
         * @dev Internal function to validate a single hook against either global or strategy root
    1453
         * @param hookAddress The address of the hook contract
    1454
         * @param hookArgs Hook arguments
    1455
         * @param proof Merkle proof for the specified root
    1456
         * @param isGlobalProof Whether to validate against global root (true) or strategy root (false)
    1457
         * @param cache Cached hook validation state variables
    1458
         * @param strategy Address of the strategy (needed to check banned leaves for global proofs)
    1459
         * @return True if hook is valid, false otherwise
    1460
         */
    1461
        function _validateSingleHook(
    1462
            address hookAddress,
    1463
            bytes calldata hookArgs,
    1464
            bytes32[] calldata proof,
    1465
            bool isGlobalProof,
    1466
            HookValidationCache memory cache,
    1467
            address strategy
    1468
        ) internal view returns (bool) {
    1469
            // Create leaf node from the hook address and arguments
    1470
            bytes32 leaf = _createLeaf(hookAddress, hookArgs);
    1471
    
                                                    
                                                
    1472
            if (isGlobalProof) {
    1473
                // Validate against global root
    1474
                if (
    1475
                    cache.globalHooksRootVetoed ||
    1476
                    cache.globalHooksRoot == bytes32(0)
    1477
                ) {
    1478
                    return false;
    1479
                }
    1480
    
                                                    
                                                
    1481
                // Check if this leaf is banned by the manager
    1482
                if (_strategyData[strategy].bannedLeaves[leaf]) {
    1483
                    return false;
    1484
                }
    1485
    
                                                    
                                                
    1486
                // For single-leaf trees, empty proof is valid when root equals leaf
    1487
                if (proof.length == 0) {
    1488
                    return cache.globalHooksRoot == leaf;
    1489
                }
    1490
                return MerkleProof.verify(proof, cache.globalHooksRoot, leaf);
    1491
            } else {
    1492
                // Validate against strategy root
    1493
                if (
    1494
                    cache.strategyHooksRootVetoed ||
    1495
                    cache.strategyRoot == bytes32(0)
    1496
                ) {
    1497
                    return false;
    1498
                }
    1499
                // For single-leaf trees, empty proof is valid when root equals leaf
    1500
                if (proof.length == 0) {
    1501
                    return cache.strategyRoot == leaf;
    1502
                }
    1503
                return MerkleProof.verify(proof, cache.strategyRoot, leaf);
    1504
            }
    1505
        }
    1506
    
                                                    
                                                
    1507
        /**
    1508
         * @dev Internal function to return the `SuperBank` address
    1509
         * @return superBank The superBank address
    1510
         */
    1511
    ✓ 2.4K
        function _getSuperBank() internal view returns (address) {
    1512
    ✓ 2.4K
            return SUPER_GOVERNOR.getAddress(SUPER_GOVERNOR.SUPER_BANK());
    1513
        }
    1514
    }
    1515
    
                                                    
                                                
    75.0% src/SuperVault/SuperVaultEscrow.sol
    Lines covered: 9 / 12 (75.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    5
    import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    6
    
                                                    
                                                
    7
    /// @title SuperVaultEscrow
    8
    /// @author Superform Labs
    9
    /// @notice Escrow contract for SuperVault shares during request/claim process
    10
    ✓ 1
    contract SuperVaultEscrow {
    11
        using SafeERC20 for IERC20;
    12
    
                                                    
                                                
    13
        /*//////////////////////////////////////////////////////////////
    14
                                    ERRORS
    15
        //////////////////////////////////////////////////////////////*/
    16
        error ALREADY_INITIALIZED();
    17
        error UNAUTHORIZED();
    18
        error ZERO_ADDRESS();
    19
    
                                                    
                                                
    20
        /*//////////////////////////////////////////////////////////////
    21
                                    STATE
    22
        //////////////////////////////////////////////////////////////*/
    23
        bool public initialized;
    24
        address public vault;
    25
        address public strategy;
    26
    
                                                    
                                                
    27
        /*//////////////////////////////////////////////////////////////
    28
                                    MODIFIERS
    29
        //////////////////////////////////////////////////////////////*/
    30
        modifier onlyVault() {
    31
    ✓ 684.3K
    ⟲ 232.1K
            if (msg.sender != vault) revert UNAUTHORIZED();
    32
            _;
    33
        }
    34
    
                                                    
                                                
    35
        /*//////////////////////////////////////////////////////////////
    36
                                INITIALIZATION
    37
        //////////////////////////////////////////////////////////////*/
    38
    
                                                    
                                                
    39
        /// @notice Initialize the escrow with required parameters
    40
        /// @param vaultAddress The vault contract address
    41
        /// @param strategyAddress The strategy contract address
    42
        function initialize(address vaultAddress, address strategyAddress) external {
    43
    ✓ 199.7K
    ⟲ 107.8K
            if (initialized) revert ALREADY_INITIALIZED();
    44
    ✓ 91.9K
            if (vaultAddress == address(0) || strategyAddress == address(0)) revert ZERO_ADDRESS();
    45
    
                                                    
                                                
    46
    ✓ 91.9K
            initialized = true;
    47
    ✓ 91.9K
            vault = vaultAddress;
    48
    ✓ 91.9K
            strategy = strategyAddress;
    49
        }
    50
    
                                                    
                                                
    51
        /*//////////////////////////////////////////////////////////////
    52
                                VAULT FUNCTIONS
    53
        //////////////////////////////////////////////////////////////*/
    54
    
                                                    
                                                
    55
        /// @notice Transfer shares from user to escrow during redeem request
    56
        /// @param from The address to transfer shares from
    57
        /// @param amount The amount of shares to transfer
    58
        function escrowShares(address from, uint256 amount) external onlyVault {
    59
    ✓ 569.1K
            IERC20(vault).safeTransferFrom(from, address(this), amount);
    60
        }
    61
    
                                                    
                                                
    62
        /// @notice Return shares from escrow to user during redeem cancellation
    63
        /// @param to The address to return shares to
    64
        /// @param amount The amount of shares to return
    65
        function returnShares(address to, uint256 amount) external onlyVault {
    66
    ✓ 1.3K
            IERC20(vault).safeTransfer(to, amount);
    67
        }
    68
    }
    69
    
                                                    
                                                
    86.5% src/SuperVault/SuperVaultStrategy.sol
    Lines covered: 508 / 587 (86.5%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    // External
    5
    import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    6
    import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
    7
    import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
    8
    import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    9
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    10
    import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
    11
    import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
    12
    import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    13
    
                                                    
                                                
    14
    // Core Interfaces
    15
    import {ISuperHook, ISuperHookResult, ISuperHookOutflow, ISuperHookInflowOutflow, ISuperHookResultOutflow, ISuperHookContextAware, ISuperHookInspector} from "@superform-v2-core/src/interfaces/ISuperHook.sol";
    16
    import {IYieldSourceOracle} from "@superform-v2-core/src/interfaces/accounting/IYieldSourceOracle.sol";
    17
    
                                                    
                                                
    18
    // Periphery Interfaces
    19
    import {ISuperVault} from "../interfaces/SuperVault/ISuperVault.sol";
    20
    import {HookDataDecoder} from "@superform-v2-core/src/libraries/HookDataDecoder.sol";
    21
    import {ISuperVaultStrategy} from "../interfaces/SuperVault/ISuperVaultStrategy.sol";
    22
    import {ISuperGovernor, FeeType} from "../interfaces/ISuperGovernor.sol";
    23
    import {ISuperVaultAggregator} from "../interfaces/SuperVault/ISuperVaultAggregator.sol";
    24
    
                                                    
                                                
    25
    /// @title SuperVaultStrategy
    26
    /// @author Superform Labs
    27
    /// @notice Strategy implementation for SuperVault that executes strategies
    28
    contract SuperVaultStrategy is
    29
        ISuperVaultStrategy,
    30
        Initializable,
    31
        ReentrancyGuardUpgradeable
    32
    {
    33
        using EnumerableSet for EnumerableSet.AddressSet;
    34
        using SafeERC20 for IERC20;
    35
        using Math for uint256;
    36
    
                                                    
                                                
    37
        /*//////////////////////////////////////////////////////////////
    38
                                    CONSTANTS
    39
        //////////////////////////////////////////////////////////////*/
    40
    ✓ 45.9K
        uint256 private constant ONE_WEEK = 7 days;
    41
    ✓ 536.3K
        uint256 private constant BPS_PRECISION = 10_000;
    42
        /// @dev The following is needed because the `processedShares` for some vaults (for example Centrifuge)
    43
        ///      can be lower by 1 or 2 wei than the `totalRequestedAmount`in SuperVault shares
    44
    ✓ 594.0K
        uint256 private constant TOLERANCE_CONSTANT = 10 wei;
    45
    
                                                    
                                                
    46
        // Slippage tolerance in BPS (1%)
    47
    ✓ 382.8K
        uint256 private constant SV_SLIPPAGE_TOLERANCE_BPS = 100;
    48
    
                                                    
                                                
    49
        uint256 public PRECISION;
    50
    
                                                    
                                                
    51
        /*//////////////////////////////////////////////////////////////
    52
                                    STATE
    53
        //////////////////////////////////////////////////////////////*/
    54
        address private _vault;
    55
        IERC20 private _asset;
    56
        uint8 private _vaultDecimals;
    57
    
                                                    
                                                
    58
        // Global configuration
    59
        uint256 private _maxPPSSlippage;
    60
    
                                                    
                                                
    61
        // Fee configuration
    62
        FeeConfig private feeConfig;
    63
        FeeConfig private proposedFeeConfig;
    64
        uint256 private feeConfigEffectiveTime;
    65
    
                                                    
                                                
    66
        // Core contracts
    67
        ISuperGovernor public immutable superGovernor;
    68
    
                                                    
                                                
    69
        // Emergency withdrawable configuration
    70
        bool public emergencyWithdrawable;
    71
        bool public proposedEmergencyWithdrawable;
    72
        uint256 public emergencyWithdrawableEffectiveTime;
    73
    
                                                    
                                                
    74
        // Yield source configuration - simplified mapping from source to oracle
    75
        mapping(address source => address oracle) private yieldSources;
    76
        EnumerableSet.AddressSet private yieldSourcesList;
    77
    
                                                    
                                                
    78
        // --- Redeem Request State ---
    79
        mapping(address controller => SuperVaultState state)
    80
            private superVaultState;
    81
    
                                                    
                                                
    82
        constructor(address superGovernor_) {
    83
    ✓ 1
            if (superGovernor_ == address(0)) revert ZERO_ADDRESS();
    84
    
                                                    
                                                
    85
    ✓ 1
            superGovernor = ISuperGovernor(superGovernor_);
    86
    ✓ 1
            emit SuperGovernorSet(superGovernor_);
    87
    ✓ 1
            _disableInitializers();
    88
        }
    89
    
                                                    
                                                
    90
        /// @notice Allows the contract to receive native ETH
    91
        /// @dev Required for hooks that may send ETH back to the strategy
    92
        receive() external payable {}
    93
    
                                                    
                                                
    94
        /*//////////////////////////////////////////////////////////////
    95
                                INITIALIZATION
    96
        //////////////////////////////////////////////////////////////*/
    97
        function initialize(
    98
            address vaultAddress,
    99
            FeeConfig memory feeConfigData
    100
        ) external initializer {
    101
    ✓ 91.9K
            if (vaultAddress == address(0)) revert INVALID_VAULT();
    102
            // if either fee is configured, check if recipient is address (0), if it is revert with ZERO ADDRESS
    103
            // if both fees are 0, no need check address (it just passes the if). Recipient can be configured later
    104
            if (
    105
    ✓ 91.9K
                (feeConfigData.performanceFeeBps > 0 ||
    106
    ✓ 1.7K
                    feeConfigData.managementFeeBps > 0) &&
    107
    ✓ 91.9K
                feeConfigData.recipient == address(0)
    108
            ) revert ZERO_ADDRESS();
    109
    ✓ 91.9K
            if (feeConfigData.performanceFeeBps > BPS_PRECISION)
    110
    ✓ 7.3K
    ⟲ 7.3K
                revert INVALID_PERFORMANCE_FEE_BPS();
    111
    ✓ 84.6K
            if (feeConfigData.managementFeeBps > BPS_PRECISION)
    112
    ✓ 3.3K
    ⟲ 3.3K
                revert INVALID_PERFORMANCE_FEE_BPS();
    113
    
                                                    
                                                
    114
    ✓ 81.3K
            __ReentrancyGuard_init();
    115
    
                                                    
                                                
    116
    ✓ 81.3K
            _vault = vaultAddress;
    117
    ✓ 81.3K
            _asset = IERC20(IERC4626(vaultAddress).asset());
    118
    ✓ 81.3K
            _vaultDecimals = IERC20Metadata(vaultAddress).decimals();
    119
    ✓ 81.3K
            PRECISION = 10 ** _vaultDecimals;
    120
    ✓ 81.3K
            feeConfig = feeConfigData;
    121
    ✓ 81.3K
            _maxPPSSlippage = 500; // 5% as a start, configurable later
    122
    
                                                    
                                                
    123
    ✓ 81.3K
            emit Initialized(_vault);
    124
        }
    125
    
                                                    
                                                
    126
        /*//////////////////////////////////////////////////////////////
    127
                            CORE STRATEGY OPERATIONS
    128
        //////////////////////////////////////////////////////////////*/
    129
    
                                                    
                                                
    130
        /// @inheritdoc ISuperVaultStrategy
    131
        function handleOperations4626Deposit(
    132
            address controller,
    133
            uint256 assetsGross
    134
    ✓ 510.0K
        ) external returns (uint256 sharesNet) {
    135
    ✓ 510.0K
            _requireVault();
    136
    
                                                    
                                                
    137
    ✓ 369.3K
            if (assetsGross == 0) revert INVALID_AMOUNT();
    138
    ✓ 369.3K
            if (controller == address(0)) revert ZERO_ADDRESS();
    139
    
                                                    
                                                
    140
            // Check if strategy is paused or if global hooks root is vetoed
    141
    ✓ 369.3K
    ⟲ 12.6K
            if (_isPaused()) revert STRATEGY_PAUSED();
    142
    ✓ 356.6K
            if (_getSuperVaultAggregator().isGlobalHooksRootVetoed()) {
    143
                revert OPERATIONS_BLOCKED_BY_VETO();
    144
            }
    145
    
                                                    
                                                
    146
            // Fee skim in ASSETS (asset-side entry fee)
    147
    ✓ 356.6K
            uint256 feeBps = feeConfig.managementFeeBps;
    148
    ✓ 356.6K
            uint256 feeAssets = feeBps == 0
    149
    ✓ 692
                ? 0
    150
    ✓ 355.9K
                : Math.mulDiv(
    151
    ✓ 355.9K
                    assetsGross,
    152
    ✓ 355.9K
                    feeBps,
    153
                    BPS_PRECISION,
    154
    ✓ 355.9K
                    Math.Rounding.Ceil
    155
                );
    156
    
                                                    
                                                
    157
    ✓ 356.6K
            uint256 assetsNet = assetsGross - feeAssets;
    158
    ✓ 356.6K
    ⟲ 3.0K
            if (assetsNet == 0) revert INVALID_AMOUNT();
    159
    
                                                    
                                                
    160
    ✓ 353.7K
            if (feeAssets != 0) {
    161
    ✓ 353.0K
                address recipient = feeConfig.recipient;
    162
                if (recipient == address(0)) revert ZERO_ADDRESS();
    163
    ✓ 353.0K
                _safeTokenTransfer(address(_asset), recipient, feeAssets);
    164
    ✓ 353.0K
                emit ManagementFeePaid(controller, recipient, feeAssets, feeBps);
    165
            }
    166
    
                                                    
                                                
    167
            // Compute shares on NET using current PPS
    168
    ✓ 353.7K
            uint256 pps = getStoredPPS();
    169
    ✓ 353.7K
    ⟲ 2.0K
            if (pps == 0) revert INVALID_PPS();
    170
    ✓ 351.6K
            sharesNet = Math.mulDiv(assetsNet, PRECISION, pps, Math.Rounding.Floor);
    171
    ✓ 351.6K
    ⟲ 16.8K
            if (sharesNet == 0) revert INVALID_AMOUNT();
    172
    
                                                    
                                                
    173
            // Account on NET
    174
    ✓ 334.8K
            SuperVaultState storage state = superVaultState[controller];
    175
    ✓ 334.8K
            state.accumulatorShares += sharesNet;
    176
    ✓ 334.8K
            state.accumulatorCostBasis += assetsNet;
    177
    ✓ 334.8K
            emit DepositHandled(controller, assetsNet, sharesNet);
    178
    ✓ 334.8K
            return sharesNet;
    179
        }
    180
    
                                                    
                                                
    181
        /// @inheritdoc ISuperVaultStrategy
    182
        function handleOperations4626Mint(
    183
            address controller,
    184
            uint256 sharesNet,
    185
            uint256 assetsGross,
    186
            uint256 assetsNet
    187
        ) external {
    188
    ✓ 316.8K
            _requireVault();
    189
    
                                                    
                                                
    190
    ✓ 199.4K
            if (sharesNet == 0) revert INVALID_AMOUNT();
    191
    ✓ 199.4K
            if (controller == address(0)) revert ZERO_ADDRESS();
    192
    
                                                    
                                                
    193
            // Check if strategy is paused or if global hooks root is vetoed
    194
    ✓ 199.4K
    ⟲ 1.6K
            if (_isPaused()) revert STRATEGY_PAUSED();
    195
    ✓ 197.8K
            if (_getSuperVaultAggregator().isGlobalHooksRootVetoed()) {
    196
                revert OPERATIONS_BLOCKED_BY_VETO();
    197
            }
    198
    
                                                    
                                                
    199
    ✓ 197.8K
            uint256 feeBps = feeConfig.managementFeeBps;
    200
            // Transfer fee if needed
    201
    ✓ 197.8K
            if (feeBps != 0) {
    202
    ✓ 197.0K
                uint256 feeAssets = assetsGross - assetsNet;
    203
    ✓ 197.0K
                if (feeAssets != 0) {
    204
    ✓ 197.0K
                    address recipient = feeConfig.recipient;
    205
                    if (recipient == address(0)) revert ZERO_ADDRESS();
    206
    ✓ 197.0K
                    _safeTokenTransfer(address(_asset), recipient, feeAssets);
    207
                    emit ManagementFeePaid(
    208
    ✓ 197.0K
                        controller,
    209
    ✓ 197.0K
                        recipient,
    210
    ✓ 197.0K
                        feeAssets,
    211
    ✓ 197.0K
                        feeBps
    212
                    );
    213
                }
    214
            }
    215
    
                                                    
                                                
    216
            // Account on NET
    217
    ✓ 197.8K
            SuperVaultState storage state = superVaultState[controller];
    218
    ✓ 197.8K
            state.accumulatorShares += sharesNet;
    219
    ✓ 197.8K
            state.accumulatorCostBasis += assetsNet;
    220
    ✓ 197.8K
            emit DepositHandled(controller, assetsNet, sharesNet);
    221
        }
    222
    
                                                    
                                                
    223
        /// @inheritdoc ISuperVaultStrategy
    224
        function quoteMintAssetsGross(
    225
            uint256 shares
    226
    ✓ 599.0K
        ) external view returns (uint256 assetsGross, uint256 assetsNet) {
    227
    ✓ 599.0K
            uint256 pps = getStoredPPS();
    228
    ✓ 599.0K
    ⟲ 8.0K
            if (pps == 0) revert INVALID_PPS();
    229
    ✓ 591.0K
            assetsNet = Math.mulDiv(shares, pps, PRECISION, Math.Rounding.Ceil);
    230
    ✓ 538.9K
            if (assetsNet == 0) revert INVALID_AMOUNT();
    231
    
                                                    
                                                
    232
    ✓ 538.9K
            uint256 feeBps = feeConfig.managementFeeBps;
    233
    ✓ 538.9K
            if (feeBps == 0) return (assetsNet, assetsNet);
    234
    ✓ 536.3K
    ⟲ 3.4K
            if (feeBps >= BPS_PRECISION) revert INVALID_AMOUNT(); // prevents div-by-zero (100% fee)
    235
    ✓ 532.9K
            assetsGross = Math.mulDiv(
    236
    ✓ 532.9K
                assetsNet,
    237
                BPS_PRECISION,
    238
    ✓ 532.9K
                (BPS_PRECISION - feeBps),
    239
    ✓ 532.9K
                Math.Rounding.Ceil
    240
            );
    241
    ✓ 490.7K
            return (assetsGross, assetsNet);
    242
        }
    243
    
                                                    
                                                
    244
        /// @inheritdoc ISuperVaultStrategy
    245
        function handleOperations7540(
    246
            Operation operation,
    247
            address controller,
    248
            address receiver,
    249
            uint256 amount
    250
        ) external {
    251
    ✓ 1.0M
            _requireVault();
    252
    ✓ 885.9K
            if (operation == Operation.RedeemRequest) {
    253
    ✓ 569.1K
                _handleRequestRedeem(controller, amount); // amount = shares
    254
    ✓ 316.8K
            } else if (operation == Operation.CancelRedeem) {
    255
    ✓ 101.0K
                _handleCancelRedeem(controller);
    256
    ✓ 215.8K
            } else if (operation == Operation.ClaimRedeem) {
    257
    ✓ 215.8K
                _handleClaimRedeem(controller, receiver, amount); // amount = assets
    258
            } else {
    259
    ✓ 271.2K
    ⟲ 271.2K
                revert ACTION_TYPE_DISALLOWED();
    260
            }
    261
        }
    262
    
                                                    
                                                
    263
        /*//////////////////////////////////////////////////////////////
    264
                    MANAGER EXTERNAL ACCESS FUNCTIONS
    265
        //////////////////////////////////////////////////////////////*/
    266
    
                                                    
                                                
    267
        /// @inheritdoc ISuperVaultStrategy
    268
        function executeHooks(
    269
            ExecuteArgs calldata args
    270
        ) external payable nonReentrant {
    271
    ✓ 259.8K
            _isManager(msg.sender);
    272
    
                                                    
                                                
    273
    ✓ 258.2K
            uint256 hooksLength = args.hooks.length;
    274
    ✓ 258.2K
    ⟲ 843
            if (hooksLength == 0) revert ZERO_LENGTH();
    275
    ✓ 257.4K
            if (args.hookCalldata.length != hooksLength)
    276
    ✓ 129.1K
    ⟲ 129.1K
                revert INVALID_ARRAY_LENGTH();
    277
    ✓ 128.3K
            if (args.expectedAssetsOrSharesOut.length != hooksLength)
    278
    ✓ 810
    ⟲ 810
                revert INVALID_ARRAY_LENGTH();
    279
    ✓ 127.5K
            if (args.globalProofs.length != hooksLength)
    280
    ✓ 205
    ⟲ 205
                revert INVALID_ARRAY_LENGTH();
    281
    ✓ 127.3K
            if (args.strategyProofs.length != hooksLength)
    282
                revert INVALID_ARRAY_LENGTH();
    283
    
                                                    
                                                
    284
    ✓ 127.3K
            address prevHook;
    285
    ✓ 141.0K
            for (uint256 i; i < hooksLength; ++i) {
    286
    ✓ 127.3K
                address hook = args.hooks[i];
    287
    ✓ 127.3K
                if (!_isRegisteredHook(hook)) revert INVALID_HOOK();
    288
    
                                                    
                                                
    289
                // Check if the hook was validated
    290
                if (
    291
                    !_validateHook(
    292
    ✓ 127.3K
                        hook,
    293
    ✓ 127.3K
                        args.hookCalldata[i],
    294
    ✓ 127.3K
                        args.globalProofs[i],
    295
    ✓ 127.3K
                        args.strategyProofs[i]
    296
                    )
    297
                ) {
    298
                    revert HOOK_VALIDATION_FAILED();
    299
                }
    300
    
                                                    
                                                
    301
    ✓ 127.3K
                prevHook = _processSingleHookExecution(
    302
    ✓ 127.3K
                    hook,
    303
    ✓ 127.3K
                    prevHook,
    304
    ✓ 127.3K
                    args.hookCalldata[i],
    305
    ✓ 127.3K
                    args.expectedAssetsOrSharesOut[i]
    306
                );
    307
            }
    308
    ✓ 13.7K
            emit HooksExecuted(args.hooks);
    309
        }
    310
    
                                                    
                                                
    311
        /// @inheritdoc ISuperVaultStrategy
    312
        function fulfillRedeemRequests(
    313
            FulfillArgs calldata args
    314
        ) external nonReentrant {
    315
    ✓ 611.1K
            _isManager(msg.sender);
    316
    
                                                    
                                                
    317
            // Check if strategy is paused
    318
    ✓ 611.0K
    ⟲ 4.9K
            if (_isPaused()) revert STRATEGY_PAUSED();
    319
    
                                                    
                                                
    320
    ✓ 606.1K
            uint256 hooksLength = args.hooks.length;
    321
    ✓ 606.1K
    ⟲ 52
            if (hooksLength == 0) revert ZERO_LENGTH();
    322
    ✓ 606.0K
            uint256 controllersLength = args.controllers.length;
    323
    ✓ 606.0K
    ⟲ 96
            if (controllersLength == 0) revert ZERO_LENGTH();
    324
    ✓ 605.9K
            if (args.hookCalldata.length != hooksLength)
    325
    ✓ 2.1K
    ⟲ 2.1K
                revert INVALID_ARRAY_LENGTH();
    326
    ✓ 603.8K
            if (args.expectedAssetsOrSharesOut.length != hooksLength)
    327
    ✓ 104
    ⟲ 104
                revert INVALID_ARRAY_LENGTH();
    328
    ✓ 603.7K
            if (args.globalProofs.length != hooksLength)
    329
                revert INVALID_ARRAY_LENGTH();
    330
    ✓ 603.7K
            if (args.strategyProofs.length != hooksLength)
    331
                revert INVALID_ARRAY_LENGTH();
    332
    
                                                    
                                                
    333
    ✓ 603.7K
            uint256 currentPPS = getStoredPPS();
    334
    ✓ 603.7K
    ⟲ 1.1K
            if (currentPPS == 0) revert INVALID_PPS();
    335
    
                                                    
                                                
    336
            // Pre-calculate totals to ensure no overburn of escrowed shares
    337
    ✓ 602.6K
            uint256 totalRequestedShares;
    338
    ✓ 1.2M
            for (uint256 i; i < controllersLength; ++i) {
    339
    ✓ 604.9K
                totalRequestedShares += superVaultState[args.controllers[i]]
    340
                    .pendingRedeemRequest;
    341
            }
    342
    ✓ 602.6K
            uint256 intendedShares;
    343
    ✓ 1.2M
            for (uint256 i; i < hooksLength; ++i) {
    344
    ✓ 602.7K
                address hook = args.hooks[i];
    345
    ✓ 602.7K
                if (!_isFulfillRequestsHook(hook)) revert INVALID_HOOK();
    346
                // Check if the hook was validated
    347
                if (
    348
    ✓ 729.9K
                    !_validateHook(
    349
    ✓ 602.7K
                        hook,
    350
    ✓ 602.7K
                        args.hookCalldata[i],
    351
    ✓ 602.7K
                        args.globalProofs[i],
    352
    ✓ 729.9K
                        args.strategyProofs[i]
    353
                    )
    354
                ) {
    355
                    revert HOOK_VALIDATION_FAILED();
    356
                }
    357
    ✓ 602.7K
                if (args.expectedAssetsOrSharesOut[i] == 0)
    358
    ✓ 1.1K
    ⟲ 1.1K
                    revert ZERO_EXPECTED_VALUE();
    359
    
                                                    
                                                
    360
    ✓ 601.5K
                intendedShares += ISuperHookInflowOutflow(hook).decodeAmount(
    361
    ✓ 601.5K
                    args.hookCalldata[i]
    362
                );
    363
            }
    364
    
                                                    
                                                
    365
            // Enforce both lower- and upper-bounds on intended shares to prevent escrow overburn
    366
    ✓ 594.0K
            if (intendedShares + TOLERANCE_CONSTANT < totalRequestedShares) {
    367
    ✓ 19.7K
    ⟲ 19.7K
                revert INVALID_REDEEM_FILL();
    368
            }
    369
    
                                                    
                                                
    370
    ✓ 574.3K
            if (intendedShares > totalRequestedShares + TOLERANCE_CONSTANT) {
    371
                revert INVALID_REDEEM_FILL();
    372
            }
    373
    
                                                    
                                                
    374
    ✓ 574.3K
            uint256 processedShares;
    375
    ✓ 953.2K
            for (uint256 i; i < hooksLength; ++i) {
    376
    ✓ 574.3K
                address hook = args.hooks[i];
    377
    ✓ 574.3K
                uint256 amountSharesSpent = _processSingleFulfillHookExecution(
    378
                    hook,
    379
    ✓ 574.3K
                    args.hookCalldata[i],
    380
    ✓ 574.3K
                    args.expectedAssetsOrSharesOut[i],
    381
    ✓ 574.3K
                    currentPPS
    382
                );
    383
    ✓ 378.9K
                processedShares += amountSharesSpent;
    384
            }
    385
    
                                                    
                                                
    386
            // Post-condition: processed shares must match intended shares
    387
    ✓ 378.9K
            if (processedShares != intendedShares) revert INVALID_REDEEM_FILL();
    388
    
                                                    
                                                
    389
    ✓ 378.9K
            _processRedeemFulfillments(
    390
    ✓ 378.9K
                args.controllers,
    391
    ✓ 378.9K
                controllersLength,
    392
    ✓ 378.9K
                processedShares,
    393
    ✓ 378.9K
                currentPPS
    394
            );
    395
    
                                                    
                                                
    396
    ✓ 378.0K
            ISuperVault(_vault).burnShares(processedShares);
    397
    
                                                    
                                                
    398
            emit RedeemRequestsFulfilled(
    399
    ✓ 378.0K
                args.hooks,
    400
    ✓ 378.0K
                args.controllers,
    401
    ✓ 378.0K
                processedShares,
    402
    ✓ 378.0K
                currentPPS
    403
            );
    404
        }
    405
    
                                                    
                                                
    406
        /*//////////////////////////////////////////////////////////////
    407
                            YIELD SOURCE MANAGEMENT
    408
        //////////////////////////////////////////////////////////////*/
    409
    
                                                    
                                                
    410
        // @inheritdoc ISuperVaultStrategy
    411
        function manageYieldSource(
    412
            address source,
    413
            address oracle,
    414
            uint8 actionType
    415
        ) external {
    416
    ✓ 593.8K
            _isPrimaryManager(msg.sender);
    417
    ✓ 585.7K
            _manageYieldSource(source, oracle, actionType);
    418
        }
    419
    
                                                    
                                                
    420
        // @inheritdoc ISuperVaultStrategy
    421
        function manageYieldSources(
    422
            address[] calldata sources,
    423
            address[] calldata oracles,
    424
            uint8[] calldata actionTypes
    425
        ) external {
    426
    ✓ 106.3K
            _isPrimaryManager(msg.sender);
    427
    
                                                    
                                                
    428
    ✓ 101.6K
            uint256 length = sources.length;
    429
    ✓ 101.6K
    ⟲ 750
            if (length == 0) revert ZERO_LENGTH();
    430
    ✓ 100.8K
    ⟲ 99.8K
            if (oracles.length != length) revert INVALID_ARRAY_LENGTH();
    431
    ✓ 1.0K
    ⟲ 715
            if (actionTypes.length != length) revert INVALID_ARRAY_LENGTH();
    432
    
                                                    
                                                
    433
    ✓ 300
            for (uint256 i; i < length; ++i) {
    434
    ✓ 300
                _manageYieldSource(sources[i], oracles[i], actionTypes[i]);
    435
            }
    436
        }
    437
    
                                                    
                                                
    438
        // @inheritdoc ISuperVaultStrategy
    439
        function proposeVaultFeeConfigUpdate(
    440
            uint256 performanceFeeBps,
    441
            uint256 managementFeeBps,
    442
            address recipient
    443
        ) external {
    444
    ✓ 148.3K
            _isPrimaryManager(msg.sender);
    445
    
                                                    
                                                
    446
    ✓ 143.6K
            if (performanceFeeBps > BPS_PRECISION)
    447
    ✓ 79.2K
    ⟲ 79.2K
                revert INVALID_PERFORMANCE_FEE_BPS();
    448
    ✓ 64.4K
            if (managementFeeBps > BPS_PRECISION)
    449
    ✓ 18.0K
    ⟲ 18.0K
                revert INVALID_PERFORMANCE_FEE_BPS();
    450
    ✓ 46.4K
    ⟲ 522
            if (recipient == address(0)) revert ZERO_ADDRESS();
    451
    ✓ 45.9K
            proposedFeeConfig = FeeConfig({
    452
                performanceFeeBps: performanceFeeBps,
    453
                managementFeeBps: managementFeeBps,
    454
                recipient: recipient
    455
            });
    456
    ✓ 45.9K
            feeConfigEffectiveTime = block.timestamp + ONE_WEEK;
    457
    ✓ 47.2K
            emit VaultFeeConfigProposed(
    458
                performanceFeeBps,
    459
                managementFeeBps,
    460
                recipient,
    461
                feeConfigEffectiveTime
    462
            );
    463
        }
    464
    
                                                    
                                                
    465
        // @inheritdoc ISuperVaultStrategy
    466
        function executeVaultFeeConfigUpdate() external {
    467
    ✓ 184.6K
            if (block.timestamp < feeConfigEffectiveTime)
    468
    ✓ 151
    ⟲ 151
                revert INVALID_TIMESTAMP();
    469
    ✓ 184.4K
    ⟲ 141.7K
            if (proposedFeeConfig.recipient == address(0)) revert ZERO_ADDRESS();
    470
    ✓ 42.7K
            feeConfig = proposedFeeConfig;
    471
    ✓ 42.7K
            delete proposedFeeConfig;
    472
    ✓ 42.7K
            feeConfigEffectiveTime = 0;
    473
    ✓ 42.7K
            emit VaultFeeConfigUpdated(
    474
                feeConfig.performanceFeeBps,
    475
                feeConfig.managementFeeBps,
    476
                feeConfig.recipient
    477
            );
    478
        }
    479
    
                                                    
                                                
    480
        // @inheritdoc ISuperVaultStrategy
    481
        function updateMaxPPSSlippage(uint256 maxSlippageBps) external {
    482
    ✓ 166.8K
            _isPrimaryManager(msg.sender);
    483
    ✓ 164.8K
    ⟲ 145.6K
            if (maxSlippageBps > BPS_PRECISION) revert INVALID_MAX_SLIPPAGE_BPS();
    484
    ✓ 19.3K
            _maxPPSSlippage = maxSlippageBps;
    485
    ✓ 19.3K
            emit MaxPPSSlippageUpdated(maxSlippageBps);
    486
        }
    487
    
                                                    
                                                
    488
        // @inheritdoc ISuperVaultStrategy
    489
        function manageEmergencyWithdraw(
    490
            uint8 action,
    491
            address recipient,
    492
            uint256 amount
    493
        ) external {
    494
    ✓ 203.9K
            if (action == 1) {
    495
    ✓ 6.7K
                _proposeEmergencyWithdraw();
    496
    ✓ 197.2K
            } else if (action == 2) {
    497
    ✓ 3.8K
                _executeEmergencyWithdrawActivation();
    498
    ✓ 193.4K
            } else if (action == 3) {
    499
    ✓ 2.1K
                _performEmergencyWithdraw(recipient, amount);
    500
    ✓ 191.3K
            } else if (action == 4) {
    501
    ✓ 2.1K
                _cancelEmergencyWithdrawProposal();
    502
            } else {
    503
                revert ACTION_TYPE_DISALLOWED();
    504
            }
    505
        }
    506
    
                                                    
                                                
    507
        /*//////////////////////////////////////////////////////////////
    508
                            ACCOUNTING MANAGEMENT
    509
        //////////////////////////////////////////////////////////////*/
    510
        /// @inheritdoc ISuperVaultStrategy
    511
        function moveAccumulatorOnTransfer(
    512
            address from,
    513
            address to,
    514
            uint256 shares
    515
        ) external {
    516
    ✓ 207.2K
            _requireVault();
    517
    ✓ 111.9K
            if (shares == 0) return;
    518
    
                                                    
                                                
    519
    ✓ 111.9K
            SuperVaultState storage fromState = superVaultState[from];
    520
    ✓ 111.9K
            SuperVaultState storage toState = superVaultState[to];
    521
    
                                                    
                                                
    522
            // Only move accumulator proportional to what's available
    523
            // (accumulator shares may be less than ERC20 shares due to pending redeems)
    524
            // see test_Fix1_AuditAttackScenarioFails
    525
    ✓ 111.9K
            uint256 availableAccumulatorShares = fromState.accumulatorShares;
    526
    ✓ 111.9K
            if (availableAccumulatorShares == 0) return; // No accumulator to move
    527
    
                                                    
                                                
    528
    ✓ 11.6K
            uint256 sharesToMove = shares > availableAccumulatorShares
    529
    ✓ 10.9K
                ? availableAccumulatorShares
    530
    ✓ 723
                : shares;
    531
    
                                                    
                                                
    532
            // Pro-rata move of cost basis (NO PPS here; preserves fee correctness)
    533
    ✓ 11.6K
            uint256 movedCostBasis = sharesToMove == availableAccumulatorShares
    534
    ✓ 10.9K
                ? fromState.accumulatorCostBasis
    535
    ✓ 704
                : Math.mulDiv(
    536
    ✓ 704
                    sharesToMove,
    537
    ✓ 704
                    fromState.accumulatorCostBasis,
    538
    ✓ 704
                    availableAccumulatorShares,
    539
    ✓ 704
                    Math.Rounding.Floor
    540
                ); /// @audit is there a value st when transfered transfers minimal shares and maximum cost basis
    541
    
                                                    
                                                
    542
    ✓ 11.6K
            fromState.accumulatorShares -= sharesToMove;
    543
    ✓ 11.6K
            fromState.accumulatorCostBasis -= movedCostBasis;
    544
    
                                                    
                                                
    545
    ✓ 11.6K
            toState.accumulatorShares += sharesToMove;
    546
    ✓ 11.6K
            toState.accumulatorCostBasis += movedCostBasis;
    547
    
                                                    
                                                
    548
            // Never touch: pendingRedeemRequest, averageRequestPPS, maxWithdraw, averageWithdrawPrice
    549
        }
    550
    
                                                    
                                                
    551
        /*//////////////////////////////////////////////////////////////
    552
                                VIEW FUNCTIONS
    553
        //////////////////////////////////////////////////////////////*/
    554
    
                                                    
                                                
    555
        // @inheritdoc ISuperVaultStrategy
    556
        function getVaultInfo()
    557
            external
    558
            view
    559
            returns (address vault, address asset, uint8 vaultDecimals)
    560
        {
    561
            vault = _vault;
    562
            asset = address(_asset);
    563
            vaultDecimals = _vaultDecimals;
    564
        }
    565
    
                                                    
                                                
    566
        // @inheritdoc ISuperVaultStrategy
    567
        function getConfigInfo()
    568
            external
    569
            view
    570
    ✓ 2.0M
            returns (FeeConfig memory feeConfig_)
    571
        {
    572
    ✓ 2.0M
            feeConfig_ = feeConfig;
    573
        }
    574
    
                                                    
                                                
    575
        // @inheritdoc ISuperVaultStrategy
    576
    ✓ 14.9M
        function getStoredPPS() public view returns (uint256) {
    577
    ✓ 14.9M
            return _getSuperVaultAggregator().getPPS(address(this));
    578
        }
    579
    
                                                    
                                                
    580
        // @inheritdoc ISuperVaultStrategy
    581
        function getSuperVaultState(
    582
            address controller
    583
    ✓ 33.8M
        ) external view returns (SuperVaultState memory state) {
    584
    ✓ 33.8M
            return superVaultState[controller];
    585
        }
    586
    
                                                    
                                                
    587
        // @inheritdoc ISuperVaultStrategy
    588
        function getYieldSource(
    589
            address source
    590
        ) external view returns (YieldSource memory) {
    591
            return YieldSource({oracle: yieldSources[source]});
    592
        }
    593
    
                                                    
                                                
    594
        // @inheritdoc ISuperVaultStrategy
    595
        function getYieldSourcesList()
    596
            external
    597
            view
    598
            returns (YieldSourceInfo[] memory)
    599
        {
    600
            uint256 length = yieldSourcesList.length();
    601
            YieldSourceInfo[] memory sourcesInfo = new YieldSourceInfo[](length);
    602
    
                                                    
                                                
    603
            for (uint256 i; i < length; ++i) {
    604
                address sourceAddress = yieldSourcesList.at(i);
    605
                address oracle = yieldSources[sourceAddress];
    606
    
                                                    
                                                
    607
                sourcesInfo[i] = YieldSourceInfo({
    608
                    sourceAddress: sourceAddress,
    609
                    oracle: oracle
    610
                });
    611
            }
    612
    
                                                    
                                                
    613
            return sourcesInfo;
    614
        }
    615
    
                                                    
                                                
    616
        // @inheritdoc ISuperVaultStrategy
    617
        function getYieldSources() external view returns (address[] memory) {
    618
            return yieldSourcesList.values();
    619
        }
    620
    
                                                    
                                                
    621
        // @inheritdoc ISuperVaultStrategy
    622
        function getYieldSourcesCount() external view returns (uint256) {
    623
            return yieldSourcesList.length();
    624
        }
    625
    
                                                    
                                                
    626
        // @inheritdoc ISuperVaultStrategy
    627
        function containsYieldSource(address source) external view returns (bool) {
    628
            return yieldSourcesList.contains(source);
    629
        }
    630
    
                                                    
                                                
    631
        // @inheritdoc ISuperVaultStrategy
    632
        function pendingRedeemRequest(
    633
            address controller
    634
    ✓ 20.0M
        ) external view returns (uint256 pendingShares) {
    635
    ✓ 20.0M
            return superVaultState[controller].pendingRedeemRequest;
    636
        }
    637
    
                                                    
                                                
    638
        // @inheritdoc ISuperVaultStrategy
    639
        function claimableWithdraw(
    640
            address controller
    641
    ✓ 1.8M
        ) external view returns (uint256 claimableAssets) {
    642
    ✓ 1.8M
            return superVaultState[controller].maxWithdraw;
    643
        }
    644
    
                                                    
                                                
    645
        // @inheritdoc ISuperVaultStrategy
    646
        function getAverageWithdrawPrice(
    647
            address controller
    648
    ✓ 6.8M
        ) external view returns (uint256 averageWithdrawPrice) {
    649
    ✓ 6.8M
            return superVaultState[controller].averageWithdrawPrice;
    650
        }
    651
    
                                                    
                                                
    652
        // @inheritdoc ISuperVaultStrategy
    653
        function previewPerformanceFee(
    654
            address controller,
    655
            uint256 sharesToRedeem
    656
        )
    657
            external
    658
            view
    659
            returns (uint256 totalFee, uint256 superformFee, uint256 recipientFee)
    660
        {
    661
            if (sharesToRedeem == 0) return (0, 0, 0);
    662
    
                                                    
                                                
    663
            // Get controller's state
    664
            SuperVaultState storage state = superVaultState[controller];
    665
    
                                                    
                                                
    666
            // Check if controller has enough shares
    667
            if (sharesToRedeem > state.accumulatorShares) return (0, 0, 0);
    668
    
                                                    
                                                
    669
            // Get the current price per share
    670
            uint256 currentPPS = getStoredPPS();
    671
    
                                                    
                                                
    672
            // Calculate historical assets (cost basis)
    673
            uint256 historicalAssets = 0;
    674
            if (state.accumulatorShares > 0) {
    675
                historicalAssets = sharesToRedeem.mulDiv(
    676
                    state.accumulatorCostBasis,
    677
                    state.accumulatorShares,
    678
                    Math.Rounding.Floor
    679
                );
    680
            }
    681
    
                                                    
                                                
    682
            // Calculate current value of shares in asset terms
    683
            uint256 currentAssetsWithFees = sharesToRedeem.mulDiv(
    684
                currentPPS,
    685
                PRECISION,
    686
                Math.Rounding.Floor
    687
            );
    688
    
                                                    
                                                
    689
            // Calculate fee (if any) using same logic as _calculateAndTransferFee
    690
            if (currentAssetsWithFees > historicalAssets) {
    691
                uint256 profit = currentAssetsWithFees - historicalAssets;
    692
                uint256 performanceFeeBps = feeConfig.performanceFeeBps;
    693
                totalFee = profit.mulDiv(
    694
                    performanceFeeBps,
    695
                    BPS_PRECISION,
    696
                    Math.Rounding.Floor
    697
                );
    698
    
                                                    
                                                
    699
                if (totalFee > 0) {
    700
                    // Calculate Superform's portion of the fee using revenueShare from SuperGovernor
    701
                    superformFee = totalFee.mulDiv(
    702
                        superGovernor.getFee(FeeType.SUPER_VAULT_PERFORMANCE_FEE),
    703
                        BPS_PRECISION,
    704
                        Math.Rounding.Floor
    705
                    );
    706
                    recipientFee = totalFee - superformFee;
    707
                }
    708
            }
    709
    
                                                    
                                                
    710
            return (totalFee, superformFee, recipientFee);
    711
        }
    712
    
                                                    
                                                
    713
        /*//////////////////////////////////////////////////////////////
    714
                            INTERNAL FUNCTIONS
    715
        //////////////////////////////////////////////////////////////*/
    716
    
                                                    
                                                
    717
        /// @notice Process a single hook execution
    718
        /// @param hook Hook address
    719
        /// @param prevHook Previous hook address
    720
        /// @param hookCalldata Hook calldata
    721
        /// @param expectedAssetsOrSharesOut Expected assets or shares output
    722
        /// @return processedHook Processed hook address
    723
        function _processSingleHookExecution(
    724
            address hook,
    725
            address prevHook,
    726
            bytes memory hookCalldata,
    727
            uint256 expectedAssetsOrSharesOut
    728
        ) internal returns (address) {
    729
            ExecutionVars memory vars;
    730
    ✓ 127.3K
            vars.hookContract = ISuperHook(hook);
    731
    
                                                    
                                                
    732
    ✓ 127.3K
            vars.targetedYieldSource = HookDataDecoder.extractYieldSource(
    733
    ✓ 127.3K
                hookCalldata
    734
            );
    735
    ✓ 127.3K
            if (yieldSources[vars.targetedYieldSource] == address(0))
    736
    ✓ 96.3K
    ⟲ 96.3K
                revert YIELD_SOURCE_NOT_FOUND();
    737
    
                                                    
                                                
    738
    ✓ 31.0K
            bool usePrevHookAmount = _decodeHookUsePrevHookAmount(
    739
    ✓ 31.0K
                hook,
    740
    ✓ 31.0K
                hookCalldata
    741
            );
    742
    ✓ 31.0K
            if (usePrevHookAmount && prevHook != address(0)) {
    743
                vars.outAmount = _getPreviousHookOutAmount(prevHook);
    744
                if (expectedAssetsOrSharesOut == 0) revert ZERO_EXPECTED_VALUE();
    745
                uint256 minExpectedPrevOut = expectedAssetsOrSharesOut *
    746
                    (BPS_PRECISION - _getSlippageTolerance());
    747
                if (vars.outAmount * BPS_PRECISION < minExpectedPrevOut) {
    748
                    revert MINIMUM_PREVIOUS_HOOK_OUT_AMOUNT_NOT_MET();
    749
                }
    750
            }
    751
    
                                                    
                                                
    752
    ✓ 31.0K
            ISuperHook(address(vars.hookContract)).setExecutionContext(
    753
    ✓ 31.0K
                address(this)
    754
            );
    755
    ✓ 31.0K
            vars.executions = vars.hookContract.build(
    756
    ✓ 31.0K
                prevHook,
    757
    ✓ 31.0K
                address(this),
    758
    ✓ 31.0K
                hookCalldata
    759
            );
    760
    ✓ 107.2K
            for (uint256 j; j < vars.executions.length; ++j) {
    761
    ✓ 93.0K
                (vars.success, ) = vars.executions[j].target.call{
    762
    ✓ 93.0K
                    value: vars.executions[j].value
    763
    ✓ 93.0K
                }(vars.executions[j].callData);
    764
    ✓ 5.5K
    ⟲ 5.5K
                if (!vars.success) revert OPERATION_FAILED();
    765
            }
    766
    ✓ 14.2K
            ISuperHook(address(vars.hookContract)).resetExecutionState(
    767
    ✓ 14.2K
                address(this)
    768
            );
    769
    
                                                    
                                                
    770
    ✓ 14.2K
            uint256 actualOutput = ISuperHookResult(hook).getOutAmount(
    771
    ✓ 14.2K
                address(this)
    772
            );
    773
    ✓ 14.2K
    ⟲ 493
            if (actualOutput == 0) revert ZERO_OUTPUT_AMOUNT();
    774
    
                                                    
                                                
    775
    ✓ 13.7K
            uint256 minExpectedOut = (expectedAssetsOrSharesOut *
    776
    ✓ 13.7K
                (BPS_PRECISION - _getSlippageTolerance())) / BPS_PRECISION;
    777
    ✓ 13.7K
            if (actualOutput < minExpectedOut) {
    778
                revert MINIMUM_OUTPUT_AMOUNT_ASSETS_NOT_MET();
    779
            }
    780
    
                                                    
                                                
    781
            emit HookExecuted(
    782
    ✓ 13.7K
                hook,
    783
    ✓ 13.7K
                prevHook,
    784
    ✓ 13.7K
                vars.targetedYieldSource,
    785
    ✓ 13.7K
                usePrevHookAmount,
    786
    ✓ 13.7K
                hookCalldata
    787
            );
    788
    
                                                    
                                                
    789
    ✓ 13.7K
            return hook;
    790
        }
    791
    
                                                    
                                                
    792
        /// @notice Process a single hook fulfillment execution
    793
        /// @param hook Hook address
    794
        /// @param hookCalldata Hook calldata
    795
        /// @param expectedAssetOutput Expected asset output
    796
        /// @param currentPPS Current price per share
    797
        /// @return processedShares Processed shares
    798
        function _processSingleFulfillHookExecution(
    799
            address hook,
    800
            bytes memory hookCalldata,
    801
            uint256 expectedAssetOutput,
    802
            uint256 currentPPS
    803
        ) internal returns (uint256) {
    804
            OutflowExecutionVars memory vars;
    805
    ✓ 574.3K
            vars.hookContract = ISuperHook(hook);
    806
    ✓ 574.3K
            vars.hookType = ISuperHookResult(hook).hookType();
    807
    ✓ 574.3K
            if (vars.hookType != ISuperHook.HookType.OUTFLOW)
    808
                revert INVALID_HOOK_TYPE();
    809
    
                                                    
                                                
    810
    ✓ 574.3K
            vars.targetedYieldSource = HookDataDecoder.extractYieldSource(
    811
    ✓ 574.3K
                hookCalldata
    812
            );
    813
    ✓ 574.3K
            if (yieldSources[vars.targetedYieldSource] == address(0))
    814
    ✓ 99.6K
    ⟲ 99.6K
                revert YIELD_SOURCE_NOT_FOUND();
    815
    
                                                    
                                                
    816
            // we must always encode supervault shares when fulfilling redemptions
    817
    ✓ 474.6K
            vars.superVaultShares = ISuperHookInflowOutflow(hook).decodeAmount(
    818
    ✓ 474.6K
                hookCalldata
    819
            );
    820
    
                                                    
                                                
    821
            // Calculate underlying shares and update hook calldata
    822
    ✓ 474.6K
            vars.amountOfAssets = vars.superVaultShares.mulDiv(
    823
    ✓ 474.6K
                currentPPS,
    824
    ✓ 474.6K
                PRECISION,
    825
                Math.Rounding.Floor
    826
            );
    827
    ✓ 474.6K
            vars.svAsset = address(_asset);
    828
    ✓ 465.2K
            vars.amountConvertedToUnderlyingShares = IYieldSourceOracle(
    829
    ✓ 474.6K
                yieldSources[vars.targetedYieldSource]
    830
            ).getShareOutput(
    831
    ✓ 474.6K
                    vars.targetedYieldSource,
    832
    ✓ 474.6K
                    vars.svAsset,
    833
    ✓ 474.6K
                    vars.amountOfAssets
    834
                );
    835
    ✓ 465.2K
            hookCalldata = ISuperHookOutflow(hook).replaceCalldataAmount(
    836
    ✓ 465.2K
                hookCalldata,
    837
                vars.amountConvertedToUnderlyingShares
    838
            );
    839
    
                                                    
                                                
    840
    ✓ 465.2K
            vars.balanceAssetBefore = _getTokenBalance(vars.svAsset, address(this));
    841
    
                                                    
                                                
    842
    ✓ 465.2K
            ISuperHook(address(vars.hookContract)).setExecutionContext(
    843
    ✓ 465.2K
                address(this)
    844
            );
    845
    
                                                    
                                                
    846
    ✓ 465.2K
            vars.executions = vars.hookContract.build(
    847
    ✓ 465.2K
                address(0),
    848
    ✓ 465.2K
                address(this),
    849
    ✓ 465.2K
                hookCalldata
    850
            );
    851
    ✓ 1.6M
            for (uint256 j; j < vars.executions.length; ++j) {
    852
    ✓ 1.2M
                (vars.success, ) = vars.executions[j].target.call(
    853
    ✓ 1.2M
                    vars.executions[j].callData
    854
                );
    855
    ✓ 18.8K
    ⟲ 18.8K
                if (!vars.success) revert OPERATION_FAILED();
    856
            }
    857
    ✓ 384.0K
            ISuperHook(address(vars.hookContract)).resetExecutionState(
    858
    ✓ 384.0K
                address(this)
    859
            );
    860
    
                                                    
                                                
    861
    ✓ 384.0K
            vars.outAmount =
    862
    ✓ 384.0K
                _getTokenBalance(vars.svAsset, address(this)) -
    863
    ✓ 384.0K
                vars.balanceAssetBefore;
    864
    
                                                    
                                                
    865
    ✓ 384.0K
    ⟲ 1.2K
            if (vars.outAmount == 0) revert ZERO_OUTPUT_AMOUNT();
    866
            if (
    867
    ✓ 382.8K
                vars.outAmount * BPS_PRECISION <
    868
    ✓ 382.8K
                expectedAssetOutput * (BPS_PRECISION - _getSlippageTolerance())
    869
            ) {
    870
    ✓ 3.8K
    ⟲ 3.8K
                revert MINIMUM_OUTPUT_AMOUNT_ASSETS_NOT_MET();
    871
            }
    872
    ✓ 378.9K
            emit FulfillHookExecuted(hook, vars.targetedYieldSource, hookCalldata);
    873
    
                                                    
                                                
    874
    ✓ 378.9K
            return vars.superVaultShares;
    875
        }
    876
    
                                                    
                                                
    877
        /// @notice Process redeem fulfillments for multiple controllers
    878
        /// @param controllers Array of controller addresses
    879
        /// @param controllersLength Length of controllers array
    880
        /// @param processedShares Total shares processed
    881
        /// @param currentPPS Current price per share
    882
        function _processRedeemFulfillments(
    883
            address[] calldata controllers,
    884
            uint256 controllersLength,
    885
            uint256 processedShares,
    886
            uint256 currentPPS
    887
        ) internal {
    888
    ✓ 756.9K
            for (uint256 i; i < controllersLength; ++i) {
    889
    ✓ 378.9K
                SuperVaultState storage state = superVaultState[controllers[i]];
    890
    
                                                    
                                                
    891
    ✓ 378.9K
                uint256 crtControllerRequestedAmount = state.pendingRedeemRequest;
    892
                // Check for PPS slippage if there's a recorded request PPS and max slippage is set
    893
    ✓ 378.9K
                if (state.averageRequestPPS > 0 && _maxPPSSlippage > 0) {
    894
    ✓ 378.9K
                    uint256 averageRequestPPS = state.averageRequestPPS;
    895
                    // Calculate the percentage decrease from request PPS to current PPS
    896
    ✓ 378.9K
                    if (currentPPS < averageRequestPPS) {
    897
                        uint256 decrease = (
    898
                            (averageRequestPPS - currentPPS).mulDiv(
    899
                                BPS_PRECISION,
    900
                                averageRequestPPS,
    901
                                Math.Rounding.Floor
    902
                            )
    903
                        );
    904
                        // If decrease exceeds maximum allowed slippage, revert
    905
                        if (decrease > _maxPPSSlippage) revert SLIPPAGE_EXCEEDED();
    906
                    }
    907
                }
    908
    
                                                    
                                                
    909
    ✓ 378.9K
                uint256 currentAssets = _calculateHistoricalAssetsAndProcessFees(
    910
    ✓ 378.9K
                    state,
    911
    ✓ 378.9K
                    crtControllerRequestedAmount,
    912
    ✓ 378.9K
                    currentPPS
    913
                );
    914
    
                                                    
                                                
    915
                // Update user state, no partial redeems allowed
    916
    ✓ 378.0K
                state.pendingRedeemRequest = 0;
    917
    ✓ 378.0K
                state.maxWithdraw += currentAssets;
    918
    ✓ 378.0K
                state.averageRequestPPS = 0; // Reset PPS value after fulfillment
    919
    
                                                    
                                                
    920
                // Call vault callback
    921
    ✓ 378.0K
                _onRedeemClaimable(
    922
    ✓ 378.0K
                    controllers[i],
    923
    ✓ 378.0K
                    currentAssets,
    924
    ✓ 378.0K
                    crtControllerRequestedAmount,
    925
    ✓ 378.0K
                    state.averageWithdrawPrice,
    926
    ✓ 378.0K
                    state.accumulatorShares,
    927
    ✓ 378.0K
                    state.accumulatorCostBasis
    928
                );
    929
            }
    930
        }
    931
    
                                                    
                                                
    932
        /// @notice Calculate historical assets and process fees
    933
        /// @param state User's vault state
    934
        /// @param requestedShares Shares being redeemed
    935
        /// @param currentPricePerShare Current price per share
    936
        function _calculateHistoricalAssetsAndProcessFees(
    937
            SuperVaultState storage state,
    938
            uint256 requestedShares,
    939
            uint256 currentPricePerShare
    940
    ✓ 378.9K
        ) private returns (uint256 currentAssets) {
    941
            // Calculate cost basis based on requested shares
    942
    ✓ 378.9K
            uint256 historicalAssets = _calculateCostBasis(state, requestedShares);
    943
    ✓ 378.9K
            uint256 currentAssetsWithFees;
    944
            // Process fees and get final assets
    945
    ✓ 378.9K
            (currentAssetsWithFees, currentAssets) = _processFees(
    946
    ✓ 378.9K
                requestedShares,
    947
    ✓ 378.9K
                currentPricePerShare,
    948
    ✓ 378.9K
                historicalAssets
    949
            );
    950
    
                                                    
                                                
    951
            // Update average withdraw price if needed
    952
    ✓ 378.0K
            if (requestedShares > 0) {
    953
    ✓ 378.0K
                _updateAverageWithdrawPrice(
    954
    ✓ 378.0K
                    state,
    955
    ✓ 378.0K
                    requestedShares,
    956
    ✓ 378.0K
                    currentAssetsWithFees
    957
                );
    958
            }
    959
    
                                                    
                                                
    960
    ✓ 378.0K
            return currentAssets;
    961
        }
    962
    
                                                    
                                                
    963
        /// @notice Calculate cost basis for requested shares using weighted average approach
    964
        /// @param state User's vault state
    965
        /// @param requestedShares Shares being redeemed
    966
        function _calculateCostBasis(
    967
            SuperVaultState storage state,
    968
            uint256 requestedShares
    969
    ✓ 378.9K
        ) private returns (uint256 costBasis) {
    970
    ✓ 378.9K
            if (requestedShares > state.accumulatorShares)
    971
                revert INSUFFICIENT_SHARES();
    972
    
                                                    
                                                
    973
            // Calculate cost basis proportionally
    974
    ✓ 378.9K
            costBasis = requestedShares.mulDiv(
    975
    ✓ 378.9K
                state.accumulatorCostBasis,
    976
    ✓ 378.9K
                state.accumulatorShares,
    977
    ✓ 378.9K
                Math.Rounding.Floor
    978
            );
    979
    
                                                    
                                                
    980
            // Update user's accumulator state
    981
    ✓ 378.9K
            state.accumulatorShares -= requestedShares;
    982
    ✓ 378.9K
            state.accumulatorCostBasis -= costBasis;
    983
    
                                                    
                                                
    984
            return costBasis;
    985
        }
    986
    
                                                    
                                                
    987
        // --- Fee Processing ---
    988
        /// @notice Calculate and transfer fees based on profit
    989
        /// @param requestedShares Shares being redeemed
    990
        /// @param currentPricePerShare Current price per share
    991
        /// @param historicalAssets Historical value of shares in assets
    992
        /// @return currentAssetsWithFees Current value of shares in assets (not net of fees)
    993
        /// @return currentAssets Current assets after fee deduction
    994
        function _processFees(
    995
            uint256 requestedShares,
    996
            uint256 currentPricePerShare,
    997
            uint256 historicalAssets
    998
    ✓ 378.9K
        ) private returns (uint256 currentAssetsWithFees, uint256 currentAssets) {
    999
            // Calculate current value of the shares at current price
    1000
    ✓ 378.9K
            currentAssetsWithFees = requestedShares.mulDiv(
    1001
    ✓ 378.9K
                currentPricePerShare,
    1002
    ✓ 378.9K
                PRECISION,
    1003
                Math.Rounding.Floor
    1004
            );
    1005
    
                                                    
                                                
    1006
            // Apply fees only on profit
    1007
    ✓ 378.9K
            currentAssets = _calculateAndTransferFee(
    1008
    ✓ 378.9K
                currentAssetsWithFees,
    1009
    ✓ 378.9K
                historicalAssets
    1010
            );
    1011
    
                                                    
                                                
    1012
            // Ensure we don't exceed available balance
    1013
    ✓ 378.0K
            uint256 balanceOfStrategy = _getTokenBalance(
    1014
    ✓ 378.0K
                address(_asset),
    1015
    ✓ 378.0K
                address(this)
    1016
            );
    1017
    ✓ 378.0K
            currentAssets = currentAssets > balanceOfStrategy
    1018
    ✓ 144.6K
                ? balanceOfStrategy
    1019
    ✓ 233.4K
                : currentAssets;
    1020
    
                                                    
                                                
    1021
    ✓ 378.0K
            return (currentAssetsWithFees, currentAssets);
    1022
        }
    1023
    
                                                    
                                                
    1024
        /// @notice Calculate fee on profit and transfer to recipient
    1025
        /// @param currentAssetsWithFees Current value of shares in assets (not net of fees)
    1026
        /// @param historicalAssets Historical value of shares in assets
    1027
        /// @return currentAssets Current assets after fee deduction
    1028
        function _calculateAndTransferFee(
    1029
            uint256 currentAssetsWithFees,
    1030
            uint256 historicalAssets
    1031
        ) private returns (uint256 currentAssets) {
    1032
    ✓ 378.9K
            currentAssets = currentAssetsWithFees;
    1033
    ✓ 378.9K
            if (currentAssetsWithFees > historicalAssets) {
    1034
    ✓ 253.0K
                uint256 profit = currentAssetsWithFees - historicalAssets;
    1035
    ✓ 253.0K
                uint256 performanceFeeBps = feeConfig.performanceFeeBps;
    1036
    ✓ 253.0K
                uint256 totalFee = profit.mulDiv(
    1037
                    performanceFeeBps,
    1038
                    BPS_PRECISION,
    1039
                    Math.Rounding.Floor
    1040
                );
    1041
    ✓ 253.0K
                if (totalFee > 0) {
    1042
                    // Calculate Superform's portion of the fee using revenueShare from SuperGovernor
    1043
    ✓ 253.0K
                    uint256 superformFee = totalFee.mulDiv(
    1044
    ✓ 253.0K
                        superGovernor.getFee(FeeType.SUPER_VAULT_PERFORMANCE_FEE),
    1045
                        BPS_PRECISION,
    1046
    ✓ 253.0K
                        Math.Rounding.Floor
    1047
                    );
    1048
    ✓ 253.0K
                    uint256 recipientFee = totalFee - superformFee;
    1049
    
                                                    
                                                
    1050
                    // Transfer fees
    1051
    ✓ 253.0K
                    if (superformFee > 0) {
    1052
                        // Get treasury address from SuperGovernor
    1053
    ✓ 253.0K
                        address treasury = superGovernor.getAddress(
    1054
    ✓ 253.0K
                            superGovernor.TREASURY()
    1055
                        );
    1056
    ✓ 253.0K
                        _safeTokenTransfer(address(_asset), treasury, superformFee);
    1057
    ✓ 253.0K
                        emit FeePaid(treasury, superformFee, performanceFeeBps);
    1058
                    }
    1059
    
                                                    
                                                
    1060
    ✓ 253.0K
                    if (recipientFee > 0) {
    1061
    ✓ 253.0K
                        address recipient = feeConfig.recipient;
    1062
                        if (recipient == address(0)) revert ZERO_ADDRESS();
    1063
    ✓ 253.0K
                        _safeTokenTransfer(
    1064
    ✓ 253.0K
                            address(_asset),
    1065
    ✓ 253.0K
                            recipient,
    1066
    ✓ 253.0K
                            recipientFee
    1067
                        );
    1068
    ✓ 252.1K
                        emit FeePaid(recipient, recipientFee, performanceFeeBps);
    1069
                    }
    1070
    
                                                    
                                                
    1071
    ✓ 252.1K
                    currentAssets -= totalFee;
    1072
                }
    1073
            }
    1074
            return currentAssets;
    1075
        }
    1076
    
                                                    
                                                
    1077
        /// @notice Internal function to update the average withdraw price
    1078
        /// @param state Storage reference to the vault state
    1079
        /// @param requestedShares Number of shares requested
    1080
        /// @param currentAssetsWithFees Current assets with fees
    1081
        function _updateAverageWithdrawPrice(
    1082
            SuperVaultState storage state,
    1083
            uint256 requestedShares,
    1084
            uint256 currentAssetsWithFees
    1085
        ) private {
    1086
    ✓ 378.0K
            uint256 existingShares;
    1087
    ✓ 378.0K
            uint256 existingAssets;
    1088
    
                                                    
                                                
    1089
    ✓ 378.0K
            if (state.maxWithdraw > 0 && state.averageWithdrawPrice > 0) {
    1090
    ✓ 112.6K
                existingShares = state.maxWithdraw.mulDiv(
    1091
    ✓ 112.6K
                    PRECISION,
    1092
    ✓ 112.6K
                    state.averageWithdrawPrice,
    1093
    ✓ 112.6K
                    Math.Rounding.Floor
    1094
                );
    1095
    ✓ 112.6K
                existingAssets = state.maxWithdraw;
    1096
            }
    1097
    
                                                    
                                                
    1098
    ✓ 378.0K
            uint256 newTotalShares = existingShares + requestedShares;
    1099
    ✓ 378.0K
            uint256 newTotalAssets = existingAssets + currentAssetsWithFees;
    1100
    
                                                    
                                                
    1101
    ✓ 378.0K
            if (newTotalShares > 0) {
    1102
    ✓ 378.0K
                state.averageWithdrawPrice = newTotalAssets.mulDiv(
    1103
    ✓ 378.0K
                    PRECISION,
    1104
    ✓ 378.0K
                    newTotalShares,
    1105
                    Math.Rounding.Floor
    1106
                );
    1107
            }
    1108
        }
    1109
    
                                                    
                                                
    1110
        /// @notice Internal function to get the SuperVaultAggregator
    1111
        /// @return The SuperVaultAggregator
    1112
        function _getSuperVaultAggregator()
    1113
            internal
    1114
            view
    1115
    ✓ 19.2M
            returns (ISuperVaultAggregator)
    1116
        {
    1117
    ✓ 19.2M
            address aggregatorAddress = superGovernor.getAddress(
    1118
    ✓ 19.2M
                superGovernor.SUPER_VAULT_AGGREGATOR()
    1119
            );
    1120
    
                                                    
                                                
    1121
            return ISuperVaultAggregator(aggregatorAddress);
    1122
        }
    1123
    
                                                    
                                                
    1124
        /// @notice Internal function to check if a manager is authorized
    1125
        /// @param manager_ The manager to check
    1126
        function _isManager(address manager_) internal view {
    1127
    ✓ 870.9K
            if (!_getSuperVaultAggregator().isAnyManager(manager_, address(this))) {
    1128
    ✓ 21.2K
    ⟲ 21.2K
                revert MANAGER_NOT_AUTHORIZED();
    1129
            }
    1130
        }
    1131
    
                                                    
                                                
    1132
        /// @notice Internal function to check if a manager is the primary manager
    1133
        /// @param manager_ The manager to check
    1134
        function _isPrimaryManager(address manager_) internal view {
    1135
            if (
    1136
    ✓ 1.0M
                !_getSuperVaultAggregator().isMainManager(manager_, address(this))
    1137
            ) {
    1138
                revert MANAGER_NOT_AUTHORIZED();
    1139
            }
    1140
        }
    1141
    
                                                    
                                                
    1142
        /// @notice Internal function to manage a yield source
    1143
        /// @param source Address of the yield source
    1144
        /// @param oracle Address of the oracle
    1145
        /// @param actionType Type of action: 0=Add, 1=UpdateOracle, 2=Remove
    1146
        function _manageYieldSource(
    1147
            address source,
    1148
            address oracle,
    1149
            uint8 actionType
    1150
        ) internal {
    1151
    ✓ 586.0K
            if (actionType == 0) {
    1152
    ✓ 497.6K
                _addYieldSource(source, oracle);
    1153
    ✓ 88.4K
            } else if (actionType == 1) {
    1154
    ✓ 4.5K
                _updateYieldSourceOracle(source, oracle);
    1155
    ✓ 83.9K
            } else if (actionType == 2) {
    1156
    ✓ 1.9K
                _removeYieldSource(source);
    1157
            } else {
    1158
                revert ACTION_TYPE_DISALLOWED();
    1159
            }
    1160
        }
    1161
    
                                                    
                                                
    1162
        /// @notice Internal function to add a yield source
    1163
        /// @param source Address of the yield source
    1164
        /// @param oracle Address of the oracle
    1165
        function _addYieldSource(address source, address oracle) internal {
    1166
    ✓ 497.6K
    ⟲ 259
            if (source == address(0) || oracle == address(0)) revert ZERO_ADDRESS();
    1167
    ✓ 497.3K
            if (yieldSources[source] != address(0))
    1168
    ✓ 58.9K
    ⟲ 58.9K
                revert YIELD_SOURCE_ALREADY_EXISTS();
    1169
    ✓ 438.4K
            yieldSources[source] = oracle;
    1170
    ✓ 438.4K
            if (!yieldSourcesList.add(source)) revert YIELD_SOURCE_ALREADY_EXISTS();
    1171
    
                                                    
                                                
    1172
    ✓ 438.4K
            emit YieldSourceAdded(source, oracle);
    1173
        }
    1174
    
                                                    
                                                
    1175
        /// @notice Internal function to update a yield source's oracle
    1176
        /// @param source Address of the yield source
    1177
        /// @param oracle Address of the oracle
    1178
        function _updateYieldSourceOracle(address source, address oracle) internal {
    1179
    ✓ 4.5K
    ⟲ 234
            if (oracle == address(0)) revert ZERO_ADDRESS();
    1180
    ✓ 4.3K
            address oldOracle = yieldSources[source];
    1181
    ✓ 4.2K
    ⟲ 4.2K
            if (oldOracle == address(0)) revert YIELD_SOURCE_NOT_FOUND();
    1182
    ✓ 117
            yieldSources[source] = oracle;
    1183
    
                                                    
                                                
    1184
    ✓ 117
            emit YieldSourceOracleUpdated(source, oldOracle, oracle);
    1185
        }
    1186
    
                                                    
                                                
    1187
        /// @notice Internal function to remove a yield source
    1188
        /// @param source Address of the yield source
    1189
        function _removeYieldSource(address source) internal {
    1190
    ✓ 1.9K
    ⟲ 857
            if (yieldSources[source] == address(0)) revert YIELD_SOURCE_NOT_FOUND();
    1191
    
                                                    
                                                
    1192
            // Remove from mapping
    1193
    ✓ 1.0K
            delete yieldSources[source];
    1194
    
                                                    
                                                
    1195
            // Remove from EnumerableSet
    1196
    ✓ 1.0K
            if (!yieldSourcesList.remove(source)) revert YIELD_SOURCE_NOT_FOUND();
    1197
    
                                                    
                                                
    1198
    ✓ 1.0K
            emit YieldSourceRemoved(source);
    1199
        }
    1200
    
                                                    
                                                
    1201
        /// @notice Internal function to propose an emergency withdraw
    1202
        function _proposeEmergencyWithdraw() internal {
    1203
    ✓ 6.7K
            _isPrimaryManager(msg.sender);
    1204
    
                                                    
                                                
    1205
    ✓ 6.7K
            proposedEmergencyWithdrawable = true;
    1206
    ✓ 6.7K
            emergencyWithdrawableEffectiveTime = block.timestamp + ONE_WEEK;
    1207
            emit EmergencyWithdrawableProposed(
    1208
    ✓ 6.7K
                true,
    1209
                emergencyWithdrawableEffectiveTime
    1210
            );
    1211
        }
    1212
    
                                                    
                                                
    1213
        /// @notice Internal function to execute an emergency withdraw
    1214
        function _executeEmergencyWithdrawActivation() internal {
    1215
    ✓ 3.8K
    ⟲ 1.3K
            if (emergencyWithdrawableEffectiveTime == 0) revert NO_PROPOSAL();
    1216
    ✓ 2.6K
            if (block.timestamp < emergencyWithdrawableEffectiveTime)
    1217
                revert INVALID_TIMESTAMP();
    1218
    ✓ 2.6K
            emergencyWithdrawable = proposedEmergencyWithdrawable;
    1219
    ✓ 2.6K
            proposedEmergencyWithdrawable = false;
    1220
    ✓ 2.6K
            emergencyWithdrawableEffectiveTime = 0;
    1221
    ✓ 2.6K
            emit EmergencyWithdrawableUpdated(emergencyWithdrawable);
    1222
        }
    1223
    
                                                    
                                                
    1224
        /// @notice Internal function to cancel an emergency withdraw proposal
    1225
        function _cancelEmergencyWithdrawProposal() internal {
    1226
    ✓ 2.1K
            _isPrimaryManager(msg.sender);
    1227
    
                                                    
                                                
    1228
    ✓ 2.1K
    ⟲ 544
            if (emergencyWithdrawableEffectiveTime == 0) revert NO_PROPOSAL();
    1229
    ✓ 1.6K
            proposedEmergencyWithdrawable = false;
    1230
    ✓ 1.6K
            emergencyWithdrawableEffectiveTime = 0;
    1231
    ✓ 1.6K
            emit EmergencyWithdrawableProposalCanceled();
    1232
        }
    1233
    
                                                    
                                                
    1234
        /// @notice Internal function to perform an emergency withdraw
    1235
        /// @param recipient Address to receive the assets
    1236
        /// @param amount Amount of assets to withdraw
    1237
        function _performEmergencyWithdraw(
    1238
            address recipient,
    1239
            uint256 amount
    1240
        ) internal {
    1241
    ✓ 2.1K
            _isPrimaryManager(msg.sender);
    1242
    
                                                    
                                                
    1243
    ✓ 2.0K
    ⟲ 916
            if (!emergencyWithdrawable) revert INVALID_EMERGENCY_WITHDRAWAL();
    1244
    ✓ 1.1K
            if (recipient == address(0)) revert ZERO_ADDRESS();
    1245
    ✓ 1.1K
            uint256 freeAssets = _getTokenBalance(address(_asset), address(this));
    1246
    ✓ 1.1K
    ⟲ 1.1K
            if (amount == 0 || amount > freeAssets) revert INSUFFICIENT_FUNDS();
    1247
            _safeTokenTransfer(address(_asset), recipient, amount);
    1248
            emit EmergencyWithdrawal(recipient, amount);
    1249
        }
    1250
    
                                                    
                                                
    1251
        /// @notice Internal function to check if a hook is a fulfill requests hook
    1252
        /// @param hook Address of the hook
    1253
        /// @return True if the hook is a fulfill requests hook, false otherwise
    1254
    ✓ 602.7K
        function _isFulfillRequestsHook(address hook) private view returns (bool) {
    1255
    ✓ 602.7K
            return superGovernor.isFulfillRequestsHookRegistered(hook);
    1256
        }
    1257
    
                                                    
                                                
    1258
        /// @notice Internal function to check if a hook is registered
    1259
        /// @param hook Address of the hook
    1260
        /// @return True if the hook is registered, false otherwise
    1261
    ✓ 127.3K
        function _isRegisteredHook(address hook) private view returns (bool) {
    1262
    ✓ 127.3K
            return superGovernor.isHookRegistered(hook);
    1263
        }
    1264
    
                                                    
                                                
    1265
        /// @notice Internal function to decode a hook's use previous hook amount
    1266
        /// @param hook Address of the hook
    1267
        /// @param hookCalldata Call data for the hook
    1268
        /// @return True if the hook should use the previous hook amount, false otherwise
    1269
        function _decodeHookUsePrevHookAmount(
    1270
            address hook,
    1271
            bytes memory hookCalldata
    1272
    ✓ 31.0K
        ) private pure returns (bool) {
    1273
            try
    1274
    ✓ 31.0K
                ISuperHookContextAware(hook).decodeUsePrevHookAmount(hookCalldata)
    1275
            returns (bool usePrevHookAmount) {
    1276
    ✓ 24.8K
                return usePrevHookAmount;
    1277
            } catch {
    1278
    ✓ 6.2K
                return false;
    1279
            }
    1280
        }
    1281
    
                                                    
                                                
    1282
        /// @notice Internal function to get the previous hook's output amount
    1283
        /// @param prevHook Address of the previous hook
    1284
        /// @return Output amount of the previous hook
    1285
        function _getPreviousHookOutAmount(
    1286
            address prevHook
    1287
        ) private view returns (uint256) {
    1288
            return ISuperHookResultOutflow(prevHook).getOutAmount(address(this));
    1289
        }
    1290
    
                                                    
                                                
    1291
        /// @notice Internal function to handle a redeem claimable
    1292
        /// @param controller Address of the controller
    1293
        /// @param assetsFulfilled Amount of assets fulfilled
    1294
        /// @param sharesFulfilled Amount of shares fulfilled
    1295
        /// @param averageWithdrawPrice Average withdraw price
    1296
        /// @param accumulatorShares Accumulator shares
    1297
        /// @param accumulatorCostBasis Accumulator cost basis
    1298
        function _onRedeemClaimable(
    1299
            address controller,
    1300
            uint256 assetsFulfilled,
    1301
            uint256 sharesFulfilled,
    1302
            uint256 averageWithdrawPrice,
    1303
            uint256 accumulatorShares,
    1304
            uint256 accumulatorCostBasis
    1305
        ) private {
    1306
    ✓ 378.0K
            ISuperVault(_vault).onRedeemClaimable(
    1307
                controller,
    1308
                assetsFulfilled,
    1309
                sharesFulfilled,
    1310
                averageWithdrawPrice,
    1311
                accumulatorShares,
    1312
                accumulatorCostBasis
    1313
            );
    1314
        }
    1315
    
                                                    
                                                
    1316
        /// @notice Internal function to handle a redeem
    1317
        /// @param controller Address of the controller
    1318
        /// @param shares Amount of shares
    1319
        function _handleRequestRedeem(address controller, uint256 shares) private {
    1320
    ✓ 569.1K
            if (shares == 0) revert INVALID_AMOUNT();
    1321
    ✓ 569.1K
            if (controller == address(0)) revert ZERO_ADDRESS();
    1322
    ✓ 569.1K
            SuperVaultState storage state = superVaultState[controller];
    1323
    
                                                    
                                                
    1324
            // Defense-in-depth: assert controller has accumulator shares
    1325
    ✓ 569.1K
            if (state.accumulatorShares == 0) revert INSUFFICIENT_SHARES();
    1326
    
                                                    
                                                
    1327
            // Get current PPS from aggregator to use as baseline for slippage protection
    1328
    ✓ 569.1K
            uint256 currentPPS = getStoredPPS();
    1329
    ✓ 569.1K
    ⟲ 652
            if (currentPPS == 0) revert INVALID_PPS();
    1330
    
                                                    
                                                
    1331
            // Calculate weighted average of PPS if there's an existing request
    1332
    ✓ 568.5K
            if (state.pendingRedeemRequest > 0) {
    1333
                // Calculate weighted average of PPS based on share amounts
    1334
    ✓ 85.4K
                uint256 existingSharesInRequest = state.pendingRedeemRequest;
    1335
    ✓ 85.4K
                uint256 newTotalSharesInRequest = existingSharesInRequest + shares;
    1336
    
                                                    
                                                
    1337
                // Use weighted average formula: (existingShares * existingPPS + newShares * currentPPS) / totalShares
    1338
    ✓ 85.4K
                state.averageRequestPPS =
    1339
    ✓ 85.4K
                    ((existingSharesInRequest * state.averageRequestPPS) +
    1340
    ✓ 85.4K
                        (shares * currentPPS)) /
    1341
                    newTotalSharesInRequest;
    1342
    
                                                    
                                                
    1343
                // Update total shares
    1344
    ✓ 85.4K
                state.pendingRedeemRequest = newTotalSharesInRequest;
    1345
            } else {
    1346
                // First request for this controller
    1347
    ✓ 483.0K
                state.pendingRedeemRequest = shares;
    1348
    ✓ 483.0K
                state.averageRequestPPS = currentPPS;
    1349
            }
    1350
    
                                                    
                                                
    1351
    ✓ 568.5K
            emit RedeemRequestPlaced(controller, controller, shares);
    1352
        }
    1353
    
                                                    
                                                
    1354
        /// @notice Internal function to handle a redeem cancellation
    1355
        /// @param controller Address of the controller
    1356
        function _handleCancelRedeem(address controller) private {
    1357
    ✓ 101.0K
            if (controller == address(0)) revert ZERO_ADDRESS();
    1358
    ✓ 101.0K
            SuperVaultState storage state = superVaultState[controller];
    1359
    ✓ 101.0K
            uint256 pendingShares = state.pendingRedeemRequest;
    1360
    ✓ 101.0K
    ⟲ 99.7K
            if (pendingShares == 0) revert REQUEST_NOT_FOUND();
    1361
            // Only clear pending request metadata
    1362
    ✓ 1.3K
            state.pendingRedeemRequest = 0;
    1363
    ✓ 1.3K
            state.averageRequestPPS = 0;
    1364
    ✓ 1.3K
            emit RedeemRequestCanceled(controller, pendingShares);
    1365
        }
    1366
    
                                                    
                                                
    1367
        /// @notice Internal function to handle a redeem claim
    1368
        /// @param controller Address of the controller
    1369
        /// @param receiver Address of the receiver
    1370
        /// @param assetsToClaim Amount of assets to claim
    1371
        function _handleClaimRedeem(
    1372
            address controller,
    1373
            address receiver,
    1374
            uint256 assetsToClaim
    1375
        ) private {
    1376
    ✓ 215.8K
    ⟲ 9.6K
            if (assetsToClaim == 0) revert INVALID_AMOUNT();
    1377
    ✓ 206.2K
            if (controller == address(0)) revert ZERO_ADDRESS();
    1378
    ✓ 206.2K
            SuperVaultState storage state = superVaultState[controller];
    1379
    
                                                    
                                                
    1380
            // Handle dust collection for rounding errors
    1381
    ✓ 206.2K
            uint256 actualAmountToClaim = assetsToClaim;
    1382
    ✓ 206.2K
            uint256 remainingAssets = _asset.balanceOf(address(this));
    1383
    
                                                    
                                                
    1384
            // If user is requesting slightly more than available due to rounding errors,
    1385
            // and the difference is small (dust), give them the remaining balance
    1386
            if (
    1387
    ✓ 206.2K
                assetsToClaim > remainingAssets &&
    1388
    ✓ 73.2K
                assetsToClaim - remainingAssets <= TOLERANCE_CONSTANT
    1389
            ) {
    1390
                actualAmountToClaim = remainingAssets;
    1391
            }
    1392
    
                                                    
                                                
    1393
    ✓ 206.2K
            if (state.maxWithdraw < actualAmountToClaim)
    1394
                revert INVALID_REDEEM_CLAIM();
    1395
    ✓ 206.2K
            state.maxWithdraw -= actualAmountToClaim;
    1396
    ✓ 206.2K
            _asset.safeTransfer(receiver, actualAmountToClaim);
    1397
            emit RedeemRequestFulfilled(
    1398
                receiver,
    1399
                controller,
    1400
                actualAmountToClaim,
    1401
    ✓ 133.1K
                0
    1402
            );
    1403
        }
    1404
    
                                                    
                                                
    1405
        /// @notice Internal function to safely transfer tokens
    1406
        /// @param token Address of the token
    1407
        /// @param recipient Address to receive the tokens
    1408
        /// @param amount Amount of tokens to transfer
    1409
        function _safeTokenTransfer(
    1410
            address token,
    1411
            address recipient,
    1412
            uint256 amount
    1413
        ) private {
    1414
    ✓ 1.1M
            if (amount > 0) IERC20(token).safeTransfer(recipient, amount);
    1415
        }
    1416
    
                                                    
                                                
    1417
        /// @notice Internal function to get the token balance of an account
    1418
        /// @param token Address of the token
    1419
        /// @param account Address of the account
    1420
        /// @return Token balance of the account
    1421
        function _getTokenBalance(
    1422
            address token,
    1423
            address account
    1424
    ✓ 1.2M
        ) private view returns (uint256) {
    1425
    ✓ 1.2M
            return IERC20(token).balanceOf(account);
    1426
        }
    1427
    
                                                    
                                                
    1428
        /// @notice Internal function to get the slippage tolerance
    1429
        /// @return Slippage tolerance
    1430
        function _getSlippageTolerance() private pure returns (uint256) {
    1431
            return SV_SLIPPAGE_TOLERANCE_BPS;
    1432
        }
    1433
    
                                                    
                                                
    1434
        /// @notice Internal function to check if the caller is the vault
    1435
        /// @dev This is used to prevent unauthorized access to certain functions
    1436
        function _requireVault() internal view {
    1437
    ✓ 2.1M
    ⟲ 491.4K
            if (msg.sender != _vault) revert ACCESS_DENIED();
    1438
        }
    1439
    
                                                    
                                                
    1440
        /// @notice Checks if the strategy is currently paused
    1441
        /// @dev This calls SuperVaultAggregator.isStrategyPaused to determine pause status
    1442
        /// @return True if the strategy is paused, false otherwise
    1443
    ✓ 1.2M
        function _isPaused() internal view returns (bool) {
    1444
    ✓ 1.2M
            return _getSuperVaultAggregator().isStrategyPaused(address(this));
    1445
        }
    1446
    
                                                    
                                                
    1447
        /// @notice Validates a hook using the Merkle root system
    1448
        /// @param hook Address of the hook to validate
    1449
        /// @param hookCalldata Calldata to be passed to the hook
    1450
        /// @param globalProof Merkle proof for the global root
    1451
        /// @param strategyProof Merkle proof for the strategy-specific root
    1452
        /// @return isValid True if the hook is valid, false otherwise
    1453
        function _validateHook(
    1454
            address hook,
    1455
            bytes memory hookCalldata,
    1456
            bytes32[] memory globalProof,
    1457
            bytes32[] memory strategyProof
    1458
    ✓ 729.9K
        ) internal view returns (bool) {
    1459
            return
    1460
    ✓ 729.9K
                _getSuperVaultAggregator().validateHook(
    1461
    ✓ 729.9K
                    address(this),
    1462
                    ISuperVaultAggregator.ValidateHookArgs({
    1463
    ✓ 729.9K
                        hookAddress: hook,
    1464
    ✓ 729.9K
                        hookArgs: ISuperHookInspector(hook).inspect(hookCalldata),
    1465
    ✓ 729.9K
                        globalProof: globalProof,
    1466
    ✓ 729.9K
                        strategyProof: strategyProof
    1467
                    })
    1468
                );
    1469
        }
    1470
    }
    1471
    
                                                    
                                                
    0.0% src/interfaces/ISuperGovernor.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    import "@openzeppelin/contracts/access/IAccessControl.sol";
    5
    
                                                    
                                                
    6
    /*//////////////////////////////////////////////////////////////
    7
                                      ENUMS
    8
        //////////////////////////////////////////////////////////////*/
    9
    /// @notice Enum representing different types of fees that can be managed
    10
    enum FeeType {
    11
        REVENUE_SHARE,
    12
        SUPER_VAULT_PERFORMANCE_FEE,
    13
        SUPER_ASSET_SWAP_FEE
    14
    }
    15
    /// @title ISuperGovernor
    16
    /// @author Superform Labs
    17
    /// @notice Interface for the SuperGovernor contract
    18
    /// @dev Central registry for all deployed contracts in the Superform periphery
    19
    
                                                    
                                                
    20
    interface ISuperGovernor is IAccessControl {
    21
        /*//////////////////////////////////////////////////////////////
    22
                                      STRUCTS
    23
        //////////////////////////////////////////////////////////////*/
    24
    
                                                    
                                                
    25
        /// @notice Structure containing Merkle root data for a hook
    26
        struct HookMerkleRootData {
    27
            bytes32 currentRoot; // Current active Merkle root for the hook
    28
            bytes32 proposedRoot; // Proposed new Merkle root (zero if no proposal exists)
    29
            uint256 effectiveTime; // Timestamp when the proposed root becomes effective
    30
        }
    31
    
                                                    
                                                
    32
        struct GasInfo {
    33
            // `batchForwardPPS` base gas
    34
            uint256 baseGasBatch;
    35
            // `batchForwardPPS` gas increase per entry
    36
            uint256 gasIncreasePerEntryBatch;
    37
        }
    38
    
                                                    
                                                
    39
        /*//////////////////////////////////////////////////////////////
    40
                                      ERRORS
    41
        //////////////////////////////////////////////////////////////*/
    42
        /// @notice Thrown when a function that should only be called by governor is called by someone else
    43
        error ONLY_GOVERNOR();
    44
        /// @notice Thrown when trying to register a contract that is already registered
    45
        error CONTRACT_ALREADY_REGISTERED();
    46
        /// @notice Thrown when trying to access a contract that is not registered
    47
        error CONTRACT_NOT_FOUND();
    48
        /// @notice Thrown when providing an invalid address (typically zero address)
    49
        error INVALID_ADDRESS();
    50
        /// @notice Thrown when providing an invalid chain ID
    51
        error INVALID_CHAIN_ID();
    52
        /// @notice Thrown when a hook is already approved
    53
        error HOOK_ALREADY_APPROVED();
    54
        /// @notice Thrown when a hook is not approved but expected to be
    55
        error HOOK_NOT_APPROVED();
    56
        /// @notice Thrown when a fulfill requests hook is already registered
    57
        error FULFILL_REQUESTS_HOOK_ALREADY_REGISTERED();
    58
        /// @notice Thrown when a fulfill requests hook is not registered but expected to be
    59
        error FULFILL_REQUESTS_HOOK_NOT_REGISTERED();
    60
        /// @notice Thrown when provided revenue share is invalid (exceeds 100%)
    61
        error INVALID_REVENUE_SHARE();
    62
        /// @notice Thrown when an invalid fee value is proposed (must be <= BPS_MAX)
    63
        error INVALID_FEE_VALUE();
    64
        /// @notice Thrown when no proposed fee exists but one is expected
    65
        error NO_PROPOSED_FEE(FeeType feeType);
    66
        /// @notice Thrown when timelock period has not expired
    67
        error TIMELOCK_NOT_EXPIRED();
    68
        /// @notice Thrown when a validator is not registered
    69
        error VALIDATOR_NOT_REGISTERED();
    70
        /// @notice Thrown when a validator is already registered
    71
        error VALIDATOR_ALREADY_REGISTERED();
    72
        /// @notice Thrown when trying to change active PPS oracle directly
    73
        error MUST_USE_TIMELOCK_FOR_CHANGE();
    74
        /// @notice Thrown when a SuperBank hook Merkle root is not registered but expected to be
    75
        error INVALID_TIMESTAMP();
    76
        /// @notice Thrown when attempting to set an invalid quorum value (typically zero)
    77
        error INVALID_QUORUM();
    78
        /// @notice Thrown when no active PPS oracle is set but one is required
    79
        error NO_ACTIVE_PPS_ORACLE();
    80
        /// @notice Thrown when no proposed PPS oracle exists but one is expected
    81
        error NO_PROPOSED_PPS_ORACLE();
    82
        /// @notice Error thrown when manager takeovers are frozen
    83
        error MANAGER_TAKEOVERS_FROZEN();
    84
        /// @notice Thrown when no proposed Merkle root exists but one is expected
    85
        error NO_PROPOSED_MERKLE_ROOT();
    86
        /// @notice Thrown when no proposed Merkle root exists but one is expected
    87
        error ZERO_PROPOSED_MERKLE_ROOT();
    88
        /// @notice Thrown when no proposed minimum staleness exists but one is expected
    89
        error NO_PROPOSED_MIN_STALENESS();
    90
        /// @notice Thrown when the provided maxStaleness is less than the minimum required staleness
    91
        error MAX_STALENESS_TOO_LOW();
    92
        /// @notice Thrown when a relayer is not registered
    93
        error RELAYER_NOT_REGISTERED();
    94
        /// @notice Thrown when a relayer is already registered
    95
        error RELAYER_ALREADY_REGISTERED();
    96
        /// @notice Thrown when an executor is not registered
    97
        error EXECUTOR_NOT_REGISTERED();
    98
        /// @notice Thrown when an executor is already registered
    99
        error EXECUTOR_ALREADY_REGISTERED();
    100
        /// @notice Thrown when there's no pending change but one is expected
    101
        error NO_PENDING_CHANGE();
    102
        /// @notice Thrown when a manager is not registered
    103
        error MANAGER_NOT_REGISTERED();
    104
        /// @notice Thrown when a manager is already registered
    105
        error MANAGER_ALREADY_REGISTERED();
    106
        /// @notice Thrown when a token is already whitelisted
    107
        error TOKEN_ALREADY_WHITELISTED();
    108
        /// @notice Thrown when a token is not proposed for whitelisting but expected to be
    109
        error NOT_PROPOSED_INCENTIVE_TOKEN();
    110
        /// @notice Thrown when a token is not whitelisted but expected to be
    111
        error NOT_WHITELISTED_INCENTIVE_TOKEN();
    112
        /// @notice Thrown when trying to register a keeper that is already registered
    113
        error KEEPER_ALREADY_REGISTERED();
    114
        /// @notice Thrown when trying to unregister a keeper that is not registered
    115
        error KEEPER_NOT_REGISTERED();
    116
        /// @notice Thrown when the price is not found
    117
        error PRICE_NOT_FOUND();
    118
        /// @notice Thrown when the price is stale
    119
        error STALE_ORACLE_PRICE();
    120
        /// @notice Thrown when the super oracle is not found
    121
        error SUPER_ORACLE_NOT_FOUND();
    122
        /// @notice Thrown when the up token is not found
    123
        error UP_NOT_FOUND();
    124
        /// @notice Thrown when the gas info is invalid
    125
        error INVALID_GAS_INFO();
    126
    
                                                    
                                                
    127
        /*//////////////////////////////////////////////////////////////
    128
                                      EVENTS
    129
        //////////////////////////////////////////////////////////////*/
    130
        /// @notice Emitted when an address is set in the registry
    131
        /// @param key The key used to reference the address
    132
        /// @param value The address value
    133
        event AddressSet(bytes32 indexed key, address indexed value);
    134
    
                                                    
                                                
    135
        /// @notice Emitted when a hook is approved
    136
        /// @param hook The address of the approved hook
    137
        event HookApproved(address indexed hook);
    138
    
                                                    
                                                
    139
        /// @notice Emitted when a hook is removed
    140
        /// @param hook The address of the removed hook
    141
        event HookRemoved(address indexed hook);
    142
    
                                                    
                                                
    143
        /// @notice Emitted when a fulfill requests hook is registered
    144
        /// @param hook The address of the registered fulfill requests hook
    145
        event FulfillRequestsHookRegistered(address indexed hook);
    146
    
                                                    
                                                
    147
        /// @notice Emitted when a fulfill requests hook is unregistered
    148
        /// @param hook The address of the unregistered fulfill requests hook
    149
        event FulfillRequestsHookUnregistered(address indexed hook);
    150
    
                                                    
                                                
    151
        /// @notice Emitted when a validator is registered
    152
        /// @param validator The address of the registered validator
    153
        event ValidatorAdded(address indexed validator);
    154
    
                                                    
                                                
    155
        /// @notice Emitted when a validator is removed
    156
        /// @param validator The address of the removed validator
    157
        event ValidatorRemoved(address indexed validator);
    158
    
                                                    
                                                
    159
        /// @notice Emitted when revenue share is updated
    160
        /// @param share The new revenue share percentage
    161
        event RevenueShareUpdated(uint256 share);
    162
    
                                                    
                                                
    163
        /// @notice Emitted when a new fee is proposed
    164
        /// @param feeType The type of fee being proposed
    165
        /// @param value The proposed fee value (in basis points)
    166
        /// @param effectiveTime The timestamp when the fee will be effective
    167
        event FeeProposed(FeeType indexed feeType, uint256 value, uint256 effectiveTime);
    168
    
                                                    
                                                
    169
        /// @notice Emitted when a fee is updated
    170
        /// @param feeType The type of fee being updated
    171
        /// @param value The new fee value (in basis points)
    172
        event FeeUpdated(FeeType indexed feeType, uint256 value);
    173
    
                                                    
                                                
    174
        /// @notice Emitted when a new SuperBank hook Merkle root is proposed
    175
        /// @param hook The hook address for which the Merkle root is being proposed
    176
        /// @param newRoot The new Merkle root
    177
        /// @param effectiveTime The timestamp when the new root will be effective
    178
        event SuperBankHookMerkleRootProposed(address indexed hook, bytes32 newRoot, uint256 effectiveTime);
    179
    
                                                    
                                                
    180
        /// @notice Emitted when the SuperBank hook Merkle root is updated.
    181
        /// @param hook The address of the hook for which the Merkle root was updated.
    182
        /// @param newRoot The new Merkle root.
    183
        event SuperBankHookMerkleRootUpdated(address indexed hook, bytes32 newRoot);
    184
    
                                                    
                                                
    185
        /// @notice Emitted when the VaultBank hook Merkle root is proposed
    186
        /// @param hook The hook address for which the Merkle root is being proposed
    187
        /// @param newRoot The new Merkle root
    188
        /// @param effectiveTime The timestamp when the new root will be effective
    189
        event VaultBankHookMerkleRootProposed(address indexed hook, bytes32 newRoot, uint256 effectiveTime);
    190
    
                                                    
                                                
    191
        /// @notice Emitted when the VaultBank hook Merkle root is updated.
    192
        /// @param hook The address of the hook for which the Merkle root was updated.
    193
        /// @param newRoot The new Merkle root.
    194
        event VaultBankHookMerkleRootUpdated(address indexed hook, bytes32 newRoot);
    195
    
                                                    
                                                
    196
        /// @notice Emitted when the active PPS Oracle's quorum requirement is updated
    197
        /// @param quorum The new quorum value
    198
        event PPSOracleQuorumUpdated(uint256 quorum);
    199
    
                                                    
                                                
    200
        /// @notice Emitted when an active PPS oracle is initially set
    201
        /// @param oracle The address of the set oracle
    202
        event ActivePPSOracleSet(address indexed oracle);
    203
    
                                                    
                                                
    204
        /// @notice Emitted when a new PPS oracle is proposed
    205
        /// @param oracle The address of the proposed oracle
    206
        /// @param effectiveTime The timestamp when the proposal will be effective
    207
        event ActivePPSOracleProposed(address indexed oracle, uint256 effectiveTime);
    208
    
                                                    
                                                
    209
        /// @notice Emitted when the active PPS oracle is changed
    210
        /// @param oldOracle The address of the previous oracle
    211
        /// @param newOracle The address of the new oracle
    212
        event ActivePPSOracleChanged(address indexed oldOracle, address indexed newOracle);
    213
    
                                                    
                                                
    214
        /// @notice Event emitted when manager takeovers are permanently frozen
    215
        event ManagerTakeoversFrozen();
    216
    
                                                    
                                                
    217
        /// @notice Emitted when a relayer is added
    218
        /// @param relayer The address of the added relayer
    219
        event RelayerAdded(address indexed relayer);
    220
    
                                                    
                                                
    221
        /// @notice Emitted when a relayer is removed
    222
        /// @param relayer The address of the removed relayer
    223
        event RelayerRemoved(address indexed relayer);
    224
    
                                                    
                                                
    225
        /// @notice Emitted when a vault bank is added
    226
        /// @param chainId The chain ID of the added vault bank
    227
        /// @param vaultBank The address of the added vault bank
    228
        event VaultBankAddressAdded(uint64 indexed chainId, address indexed vaultBank);
    229
    
                                                    
                                                
    230
        /// @notice Emitted when an executor is added
    231
        /// @param executor The address of the added executor
    232
        event ExecutorAdded(address indexed executor);
    233
    
                                                    
                                                
    234
        /// @notice Emitted when an executor is removed
    235
        /// @param executor The address of the removed executor
    236
        event ExecutorRemoved(address indexed executor);
    237
    
                                                    
                                                
    238
        /// @notice Emitted when a prover is set
    239
        /// @param prover The address of the prover
    240
        event ProverSet(address indexed prover);
    241
    
                                                    
                                                
    242
        /// @notice Emitted when a change to upkeep payments status is proposed
    243
        /// @param enabled The proposed status (enabled/disabled)
    244
        /// @param effectiveTime The timestamp when the status change will be effective
    245
        event UpkeepPaymentsChangeProposed(bool enabled, uint256 effectiveTime);
    246
    
                                                    
                                                
    247
        /// @notice Emitted when upkeep payments status is changed
    248
        /// @param enabled The new status (enabled/disabled)
    249
        event UpkeepPaymentsChanged(bool enabled);
    250
    
                                                    
                                                
    251
        /// @notice Emitted when a new minimum staleness is proposed
    252
        /// @param newMinStaleness The proposed minimum staleness value
    253
        /// @param effectiveTime The timestamp when the new value will be effective
    254
        event MinStalenesProposed(uint256 newMinStaleness, uint256 effectiveTime);
    255
    
                                                    
                                                
    256
        /// @notice Emitted when the minimum staleness is changed
    257
        /// @param newMinStaleness The new minimum staleness value
    258
        event MinStalenesChanged(uint256 newMinStaleness);
    259
    
                                                    
                                                
    260
        /// @notice Emitted when a superform manager is added
    261
        /// @param manager The address of the added manager
    262
        event SuperformManagerAdded(address indexed manager);
    263
    
                                                    
                                                
    264
        /// @notice Emitted when a superform manager is removed
    265
        /// @param manager The address of the removed manager
    266
        event SuperformManagerRemoved(address indexed manager);
    267
    
                                                    
                                                
    268
        /// @notice Emitted when incentive tokens are proposed for whitelisting
    269
        /// @param tokens The addresses of the proposed tokens
    270
        /// @param effectiveTime The timestamp when the proposal will be effective
    271
        event WhitelistedIncentiveTokensProposed(address[] tokens, uint256 effectiveTime);
    272
    
                                                    
                                                
    273
        /// @notice Emitted when whitelisted incentive tokens are added
    274
        /// @param tokens The addresses of the added tokens
    275
        event WhitelistedIncentiveTokensAdded(address[] tokens);
    276
    
                                                    
                                                
    277
        /// @notice Emitted when whitelisted incentive tokens are removed
    278
        /// @param tokens The addresses of the removed tokens
    279
        event WhitelistedIncentiveTokensRemoved(address[] tokens);
    280
    
                                                    
                                                
    281
        /// @notice Emitted when a protected keeper is registered
    282
        /// @param keeper Address of the keeper being registered
    283
        event ProtectedKeeperRegistered(address indexed keeper);
    284
    
                                                    
                                                
    285
        /// @notice Emitted when a protected keeper is unregistered
    286
        /// @param keeper Address of the keeper being unregistered
    287
        event ProtectedKeeperUnregistered(address indexed keeper);
    288
    
                                                    
                                                
    289
        /// @notice Emitted when gas info is set
    290
        /// @param oracle The address of the oracle
    291
        /// @param baseGasBatch The base gas for the oracle
    292
        /// @param gasIncreasePerEntryBatch The gas increase per entry for the oracle
    293
        event GasInfoSet(address indexed oracle, uint256 baseGasBatch, uint256 gasIncreasePerEntryBatch);
    294
    
                                                    
                                                
    295
        /*//////////////////////////////////////////////////////////////
    296
                           CONTRACT REGISTRY FUNCTIONS
    297
        //////////////////////////////////////////////////////////////*/
    298
        /// @notice Sets an address in the registry
    299
        /// @param key The key to associate with the address
    300
        /// @param value The address value
    301
        function setAddress(bytes32 key, address value) external;
    302
    
                                                    
                                                
    303
        /*//////////////////////////////////////////////////////////////
    304
                            PERIPHERY CONFIGURATIONS
    305
        //////////////////////////////////////////////////////////////*/
    306
        /// @notice Sets the prover address
    307
        /// @param prover The address of the prover
    308
        function setProver(address prover) external;
    309
    
                                                    
                                                
    310
        /// @notice Change the primary manager for a strategy
    311
        /// @dev Only SuperGovernor can call this function directly
    312
        /// @param strategy The strategy address
    313
        /// @param newManager The new primary manager address
    314
        function changePrimaryManager(address strategy, address newManager) external;
    315
    
                                                    
                                                
    316
        /// @notice Permanently freezes all manager takeovers globally
    317
        function freezeManagerTakeover() external;
    318
    
                                                    
                                                
    319
        /// @notice Changes the hooks root update timelock duration
    320
        /// @param newTimelock New timelock duration in seconds
    321
        function changeHooksRootUpdateTimelock(uint256 newTimelock) external;
    322
    
                                                    
                                                
    323
        /// @notice Proposes a new global hooks Merkle root
    324
        /// @dev Only GOVERNOR_ROLE can call this function
    325
        /// @param newRoot New Merkle root for global hooks validation
    326
        function proposeGlobalHooksRoot(bytes32 newRoot) external;
    327
    
                                                    
                                                
    328
        /// @notice Sets veto status for global hooks Merkle root
    329
        /// @dev Only GUARDIAN_ROLE can call this function
    330
        /// @param vetoed Whether to veto (true) or unveto (false) the global hooks root
    331
        function setGlobalHooksRootVetoStatus(bool vetoed) external;
    332
    
                                                    
                                                
    333
        /// @notice Sets veto status for a strategy-specific hooks Merkle root
    334
        /// @dev Only GUARDIAN_ROLE can call this function
    335
        /// @param strategy Address of the strategy to affect
    336
        /// @param vetoed Whether to veto (true) or unveto (false) the strategy hooks root
    337
        function setStrategyHooksRootVetoStatus(address strategy, bool vetoed) external;
    338
    
                                                    
                                                
    339
        /// @notice Sets the superasset manager for a superasset
    340
        /// @param superAsset The superasset address
    341
        /// @param superAssetManager The new superasset manager address
    342
        function setSuperAssetManager(address superAsset, address superAssetManager) external;
    343
    
                                                    
                                                
    344
        /// @notice Adds an ICC to the whitelist
    345
        /// @param icc The ICC address to add
    346
        function addICCToWhitelist(address icc) external;
    347
    
                                                    
                                                
    348
        /// @notice Removes an ICC from the whitelist
    349
        /// @param icc The ICC address to remove
    350
        function removeICCFromWhitelist(address icc) external;
    351
    
                                                    
                                                
    352
        /// @notice Sets the maximum staleness period for all oracle feeds
    353
        /// @param newMaxStaleness The new maximum staleness period in seconds
    354
        function setOracleMaxStaleness(uint256 newMaxStaleness) external;
    355
    
                                                    
                                                
    356
        /// @notice Sets the maximum staleness period for a specific oracle feed
    357
        /// @param feed The address of the feed to set staleness for
    358
        /// @param newMaxStaleness The new maximum staleness period in seconds
    359
        function setOracleFeedMaxStaleness(address feed, uint256 newMaxStaleness) external;
    360
    
                                                    
                                                
    361
        /// @notice Sets the maximum staleness periods for multiple oracle feeds in batch
    362
        /// @param feeds The addresses of the feeds to set staleness for
    363
        /// @param newMaxStalenessList The new maximum staleness periods in seconds
    364
        function setOracleFeedMaxStalenessBatch(
    365
            address[] calldata feeds,
    366
            uint256[] calldata newMaxStalenessList
    367
        )
    368
            external;
    369
    
                                                    
                                                
    370
        /// @notice Queues an oracle update for execution after timelock period
    371
        /// @param bases Base asset addresses
    372
        /// @param quotes Quote asset addresses
    373
        /// @param providers Provider identifiers
    374
        /// @param feeds Feed addresses
    375
        function queueOracleUpdate(
    376
            address[] calldata bases,
    377
            address[] calldata quotes,
    378
            bytes32[] calldata providers,
    379
            address[] calldata feeds
    380
        )
    381
            external;
    382
    
                                                    
                                                
    383
        /// @notice Queues a provider removal for execution after timelock period
    384
        /// @param providers The providers to remove
    385
        function queueOracleProviderRemoval(bytes32[] calldata providers) external;
    386
    
                                                    
                                                
    387
        /// @notice Sets uptime feeds for multiple data oracles in batch (Layer 2 only)
    388
        /// @param dataOracles Array of data oracle addresses to set uptime feeds for
    389
        /// @param uptimeOracles Array of uptime feed addresses to set
    390
        /// @param gracePeriods Array of grace periods in seconds after sequencer restart
    391
        function batchSetOracleUptimeFeed(
    392
            address[] calldata dataOracles,
    393
            address[] calldata uptimeOracles,
    394
            uint256[] calldata gracePeriods
    395
        )
    396
            external;
    397
    
                                                    
                                                
    398
        /// @notice Sets the emergency price for a token
    399
        /// @param token The address of the token
    400
        /// @param price The emergency price to set
    401
        function setEmergencyPrice(address token, uint256 price) external;
    402
    
                                                    
                                                
    403
        /// @notice Sets the emergency price for multiple tokens o
    404
        /// @param tokens Array of token addresses
    405
        /// @param prices Array of emergency prices
    406
        function batchSetEmergencyPrices(address[] calldata tokens, uint256[] calldata prices) external;
    407
    
                                                    
                                                
    408
        /*//////////////////////////////////////////////////////////////
    409
                              HOOK MANAGEMENT
    410
        //////////////////////////////////////////////////////////////*/
    411
        /// @notice Registers a hook for use in SuperVaults
    412
        /// @param hook The address of the hook to register
    413
        /// @param isFulfillRequestsHook Whether the hook is a fulfill requests hook
    414
        function registerHook(address hook, bool isFulfillRequestsHook) external;
    415
    
                                                    
                                                
    416
        /// @notice Unregisters a hook from the approved list
    417
        /// @param hook The address of the hook to unregister
    418
        function unregisterHook(address hook) external;
    419
    
                                                    
                                                
    420
        /*//////////////////////////////////////////////////////////////
    421
                            EXECUTOR MANAGEMENT
    422
        //////////////////////////////////////////////////////////////*/
    423
        /// @notice Adds an executor to the approved list
    424
        /// @param executor The address of the executor to add
    425
        function addExecutor(address executor) external;
    426
    
                                                    
                                                
    427
        /// @notice Removes an executor from the approved list
    428
        /// @param executor The address of the executor to remove
    429
        function removeExecutor(address executor) external;
    430
    
                                                    
                                                
    431
        /*//////////////////////////////////////////////////////////////
    432
                            RELAYER MANAGEMENT
    433
        //////////////////////////////////////////////////////////////*/
    434
        /// @notice Adds a relayer to the approved list
    435
        /// @param relayer The address of the relayer to add
    436
        function addRelayer(address relayer) external;
    437
    
                                                    
                                                
    438
        /// @notice Removes a relayer from the approved list
    439
        /// @param relayer The address of the relayer to remove
    440
        function removeRelayer(address relayer) external;
    441
    
                                                    
                                                
    442
        /*//////////////////////////////////////////////////////////////
    443
                          VALIDATOR MANAGEMENT
    444
        //////////////////////////////////////////////////////////////*/
    445
        /// @notice Adds a validator to the approved list
    446
        /// @param validator The address of the validator to add
    447
        function addValidator(address validator) external;
    448
    
                                                    
                                                
    449
        /// @notice Removes a validator from the approved list
    450
        /// @param validator The address of the validator to remove
    451
        function removeValidator(address validator) external;
    452
    
                                                    
                                                
    453
        /*//////////////////////////////////////////////////////////////
    454
                           PPS ORACLE MANAGEMENT
    455
        //////////////////////////////////////////////////////////////*/
    456
        /// @notice Sets the active PPS oracle (only if there is no active oracle yet)
    457
        /// @param oracle Address of the PPS oracle to set as active
    458
        function setActivePPSOracle(address oracle) external;
    459
    
                                                    
                                                
    460
        /// @notice Proposes a new active PPS oracle (when there is already an active one)
    461
        /// @param oracle Address of the PPS oracle to propose as active
    462
        function proposeActivePPSOracle(address oracle) external;
    463
    
                                                    
                                                
    464
        /// @notice Executes a previously proposed PPS oracle change after timelock has expired
    465
        function executeActivePPSOracleChange() external;
    466
    
                                                    
                                                
    467
        /// @notice Sets the quorum requirement for the active PPS Oracle
    468
        /// @param quorum The new quorum value
    469
        function setPPSOracleQuorum(uint256 quorum) external;
    470
    
                                                    
                                                
    471
        /*//////////////////////////////////////////////////////////////
    472
                          REVENUE SHARE MANAGEMENT
    473
        //////////////////////////////////////////////////////////////*/
    474
        /// @notice Proposes a new fee value
    475
        /// @param feeType The type of fee to propose
    476
        /// @param value The proposed fee value (in basis points)
    477
        function proposeFee(FeeType feeType, uint256 value) external;
    478
    
                                                    
                                                
    479
        /// @notice Executes a previously proposed fee update after timelock has expired
    480
        /// @param feeType The type of ffee to execute the update for
    481
        function executeFeeUpdate(FeeType feeType) external;
    482
    
                                                    
                                                
    483
        /// @notice Executes an upkeep claim on `SuperVaultAggregator`
    484
        /// @param amount The amount to claim
    485
        function executeUpkeepClaim(uint256 amount) external;
    486
    
                                                    
                                                
    487
        /*//////////////////////////////////////////////////////////////
    488
                          UPKEEP COST MANAGEMENT
    489
        //////////////////////////////////////////////////////////////*/
    490
        /// @notice Sets gas info for an oracle
    491
        /// @param oracle The address of the oracle
    492
        /// @param baseGasBatch The base gas for the oracle
    493
        /// @param gasIncreasePerEntryBatch The gas increase per entry for the oracle
    494
        function setGasInfo(address oracle, uint256 baseGasBatch, uint256 gasIncreasePerEntryBatch) external;
    495
    
                                                    
                                                
    496
        /// @notice Proposes a change to upkeep payments enabled status
    497
        /// @param enabled The proposed enabled status
    498
        function proposeUpkeepPaymentsChange(bool enabled) external;
    499
    
                                                    
                                                
    500
        /// @notice Executes a previously proposed upkeep payments status change
    501
        function executeUpkeepPaymentsChange() external;
    502
    
                                                    
                                                
    503
        /*//////////////////////////////////////////////////////////////
    504
                            MIN STALENESS MANAGEMENT
    505
        //////////////////////////////////////////////////////////////*/
    506
        /// @notice Proposes a new minimum staleness value to prevent maxStaleness from being set too low
    507
        /// @param newMinStaleness The proposed new minimum staleness value in seconds
    508
        function proposeMinStaleness(uint256 newMinStaleness) external;
    509
    
                                                    
                                                
    510
        /// @notice Executes a previously proposed minimum staleness change after timelock has expired
    511
        function executeMinStalenesChange() external;
    512
    
                                                    
                                                
    513
        /*//////////////////////////////////////////////////////////////
    514
                            SUPERFORM MANAGER MANAGEMENT
    515
        //////////////////////////////////////////////////////////////*/
    516
    
                                                    
                                                
    517
        /// @notice Adds a manager to the superform managers list
    518
        /// @param manager Address of the manager to add
    519
        function addSuperformManager(address manager) external;
    520
    
                                                    
                                                
    521
        /// @notice Removes a manager from the superform managers list
    522
        /// @param manager Address of the manager to remove
    523
        function removeSuperformManager(address manager) external;
    524
    
                                                    
                                                
    525
        /*//////////////////////////////////////////////////////////////
    526
                               VAULT HOOKS MGMT
    527
        //////////////////////////////////////////////////////////////*/
    528
        /// @notice Proposes a new Merkle root for a specific hook's allowed targets.
    529
        /// @param hook The address of the hook to update the Merkle root for.
    530
        /// @param proposedRoot The proposed new Merkle root.
    531
        function proposeVaultBankHookMerkleRoot(address hook, bytes32 proposedRoot) external;
    532
    
                                                    
                                                
    533
        /// @notice Executes a previously proposed Merkle root update for a specific hook if the effective time has passed.
    534
        /// @param hook The address of the hook to execute the update for.
    535
        function executeVaultBankHookMerkleRootUpdate(address hook) external;
    536
    
                                                    
                                                
    537
        /*//////////////////////////////////////////////////////////////
    538
                               SUPERBANK HOOKS MGMT
    539
        //////////////////////////////////////////////////////////////*/
    540
        /// @notice Proposes a new Merkle root for a specific hook's allowed targets.
    541
        /// @param hook The address of the hook to update the Merkle root for.
    542
        /// @param proposedRoot The proposed new Merkle root.
    543
        function proposeSuperBankHookMerkleRoot(address hook, bytes32 proposedRoot) external;
    544
    
                                                    
                                                
    545
        /// @notice Executes a previously proposed Merkle root update for a specific hook if the effective time has passed.
    546
        /// @param hook The address of the hook to execute the update for.
    547
        function executeSuperBankHookMerkleRootUpdate(address hook) external;
    548
    
                                                    
                                                
    549
        /*//////////////////////////////////////////////////////////////
    550
                            VAULT BANK MANAGEMENT
    551
        //////////////////////////////////////////////////////////////*/
    552
        /// @notice Adds a vault bank address for a specific chain ID
    553
        /// @param chainId The chain ID to add the vault bank for
    554
        /// @param vaultBank The address of the vault bank to add
    555
        function addVaultBank(uint64 chainId, address vaultBank) external;
    556
    
                                                    
                                                
    557
        /*//////////////////////////////////////////////////////////////
    558
                            INCENTIVE TOKEN MANAGEMENT
    559
        //////////////////////////////////////////////////////////////*/
    560
        /// @notice Proposes whitelisted incentive tokens
    561
        /// @param tokens The addresses of the tokens to add
    562
        function proposeAddIncentiveTokens(address[] memory tokens) external;
    563
    
                                                    
                                                
    564
        /// @notice Executes a previously proposed whitelisted incentive token update after timelock has expired
    565
        function executeAddIncentiveTokens() external;
    566
    
                                                    
                                                
    567
        /// @notice Proposes a new whitelisted incentive token
    568
        /// @param tokens The addresses of the tokens to add
    569
        function proposeRemoveIncentiveTokens(address[] memory tokens) external;
    570
    
                                                    
                                                
    571
        /// @notice Executes a previously proposed whitelisted incentive tokens removal after timelock has expired
    572
        function executeRemoveIncentiveTokens() external;
    573
    
                                                    
                                                
    574
        /*//////////////////////////////////////////////////////////////
    575
                            EXTERNAL VIEW FUNCTIONS
    576
        //////////////////////////////////////////////////////////////*/
    577
        /// @notice The identifier of the role that grants access to critical governance functions
    578
        function SUPER_GOVERNOR_ROLE() external view returns (bytes32);
    579
    
                                                    
                                                
    580
        /// @notice The identifier of the role that grants access to daily operations like hooks and validators
    581
        function GOVERNOR_ROLE() external view returns (bytes32);
    582
    
                                                    
                                                
    583
        /// @notice The identifier of the role that grants access to bank management functions
    584
        function BANK_MANAGER_ROLE() external view returns (bytes32);
    585
    
                                                    
                                                
    586
        /// @notice The identifier of the role that grants access to gas management functions
    587
        function GAS_MANAGER_ROLE() external view returns (bytes32);
    588
    
                                                    
                                                
    589
        /// @notice The identifier of the role that grants access to guardian functions
    590
        function GUARDIAN_ROLE() external view returns (bytes32);
    591
    
                                                    
                                                
    592
        /// @notice The identifier of the role that grants access to superasset factory
    593
        function SUPER_ASSET_FACTORY() external view returns (bytes32);
    594
    
                                                    
                                                
    595
        /// @notice Gets an address from the registry
    596
        /// @param key The key of the address to get
    597
        /// @return The address value
    598
        function getAddress(bytes32 key) external view returns (address);
    599
    
                                                    
                                                
    600
        /// @notice Checks if manager takeovers are frozen
    601
        /// @return True if manager takeovers are frozen, false otherwise
    602
        function isManagerTakeoverFrozen() external view returns (bool);
    603
    
                                                    
                                                
    604
        /// @notice Gets the vault bank address for a specific chain ID
    605
        /// @param chainId The chain ID to get the vault bank for
    606
        /// @return The vault bank address
    607
        function getVaultBank(uint64 chainId) external view returns (address);
    608
    
                                                    
                                                
    609
        /// @notice Checks if a hook is registered
    610
        /// @param hook The address of the hook to check
    611
        /// @return True if the hook is registered, false otherwise
    612
        function isHookRegistered(address hook) external view returns (bool);
    613
    
                                                    
                                                
    614
        /// @notice Checks if a hook is registered as a fulfill requests hook
    615
        /// @param hook The address of the hook to check
    616
        /// @return True if the hook is registered as a fulfill requests hook, false otherwise
    617
        function isFulfillRequestsHookRegistered(address hook) external view returns (bool);
    618
    
                                                    
                                                
    619
        /// @notice Gets all registered hooks
    620
        /// @return An array of registered hook addresses
    621
        function getRegisteredHooks() external view returns (address[] memory);
    622
    
                                                    
                                                
    623
        /// @notice Gets all registered fulfill requests hooks
    624
        /// @return An array of registered fulfill requests hook addresses
    625
        function getRegisteredFulfillRequestsHooks() external view returns (address[] memory);
    626
    
                                                    
                                                
    627
        /// @notice Checks if an address is an approved validator
    628
        /// @param validator The address to check
    629
        /// @return True if the address is an approved validator, false otherwise
    630
        function isValidator(address validator) external view returns (bool);
    631
    
                                                    
                                                
    632
        /// @notice Checks if an address has the guardian role
    633
        /// @param guardian Address to check
    634
        /// @return true if the address has the GUARDIAN_ROLE
    635
        function isGuardian(address guardian) external view returns (bool);
    636
    
                                                    
                                                
    637
        /// @notice Checks if an address is an approved relayer
    638
        /// @param relayer The address to check
    639
        /// @return True if the address is an approved relayer, false otherwise
    640
        function isRelayer(address relayer) external view returns (bool);
    641
    
                                                    
                                                
    642
        /// @notice Checks if an address is an approved executor
    643
        /// @param executor The address to check
    644
        /// @return True if the address is an approved executor, false otherwise
    645
        function isExecutor(address executor) external view returns (bool);
    646
    
                                                    
                                                
    647
        /// @notice Returns all registered validators
    648
        /// @return List of validator addresses
    649
        function getValidators() external view returns (address[] memory);
    650
    
                                                    
                                                
    651
        /// @notice Returns all registered relayers
    652
        /// @return List of relayer addresses
    653
        function getRelayers() external view returns (address[] memory);
    654
    
                                                    
                                                
    655
        /// @notice Returns all registered executors
    656
        /// @return List of executor addresses
    657
        function getExecutors() external view returns (address[] memory);
    658
    
                                                    
                                                
    659
        /// @notice Gets the proposed active PPS oracle and its effective time
    660
        /// @return proposedOracle The proposed oracle address
    661
        /// @return effectiveTime The timestamp when the proposed oracle will become effective
    662
        function getProposedActivePPSOracle() external view returns (address proposedOracle, uint256 effectiveTime);
    663
    
                                                    
                                                
    664
        /// @notice Gets the current quorum requirement for the active PPS Oracle
    665
        /// @return The current quorum requirement
    666
        function getPPSOracleQuorum() external view returns (uint256);
    667
    
                                                    
                                                
    668
        /// @notice Gets the active PPS oracle
    669
        /// @return The active PPS oracle address
    670
        function getActivePPSOracle() external view returns (address);
    671
    
                                                    
                                                
    672
        /// @notice Checks if an address is the current active PPS oracle
    673
        /// @param oracle The address to check
    674
        /// @return True if the address is the active PPS oracle, false otherwise
    675
        function isActivePPSOracle(address oracle) external view returns (bool);
    676
    
                                                    
                                                
    677
        /// @notice Gets the current fee value for a specific fee type
    678
        /// @param feeType The type of fee to get
    679
        /// @return The current fee value (in basis points)
    680
        function getFee(FeeType feeType) external view returns (uint256);
    681
    
                                                    
                                                
    682
        /// @notice Gets the current upkeep cost per batch update for PPS updates
    683
        /// @param oracle The address of the PPS oracle
    684
        /// @param chargeableEntries The number of chargeable entries
    685
        /// @return The current upkeep cost per batch update in UP tokens
    686
        function getUpkeepCostPerBatchUpdate(address oracle, uint256 chargeableEntries) external view returns (uint256);
    687
    
                                                    
                                                
    688
        /// @notice Gets the proposed upkeep cost per update and its effective time
    689
        /// @notice Gets the current minimum staleness value
    690
        /// @return The current minimum staleness value in seconds
    691
        function getMinStaleness() external view returns (uint256);
    692
    
                                                    
                                                
    693
        /// @notice Gets the proposed minimum staleness value and its effective time
    694
        /// @return proposedMinStaleness The proposed new minimum staleness value
    695
        /// @return effectiveTime The timestamp when the new value will become effective
    696
        function getProposedMinStaleness() external view returns (uint256 proposedMinStaleness, uint256 effectiveTime);
    697
    
                                                    
                                                
    698
        /// @notice Returns the current Merkle root for a specific hook's allowed targets.
    699
        /// @param hook The address of the hook to get the Merkle root for.
    700
        /// @return The Merkle root for the hook's allowed targets.
    701
        function getSuperBankHookMerkleRoot(address hook) external view returns (bytes32);
    702
    
                                                    
                                                
    703
        /// @notice Returns the current Merkle root for a specific hook's allowed targets.
    704
        /// @param hook The address of the hook to get the Merkle root for.
    705
        /// @return The Merkle root for the hook's allowed targets.
    706
        function getVaultBankHookMerkleRoot(address hook) external view returns (bytes32);
    707
    
                                                    
                                                
    708
        /// @notice Gets the proposed Merkle root and its effective time for a specific hook.
    709
        /// @param hook The address of the hook to get the proposed Merkle root for.
    710
        /// @return proposedRoot The proposed Merkle root.
    711
        /// @return effectiveTime The timestamp when the proposed root will become effective.
    712
        function getProposedSuperBankHookMerkleRoot(address hook)
    713
            external
    714
            view
    715
            returns (bytes32 proposedRoot, uint256 effectiveTime);
    716
    
                                                    
                                                
    717
        /// @notice Gets the proposed Merkle root and its effective time for a specific hook.
    718
        /// @param hook The address of the hook to get the proposed Merkle root for.
    719
        /// @return proposedRoot The proposed Merkle root.
    720
        /// @return effectiveTime The timestamp when the proposed root will become effective.
    721
        function getProposedVaultBankHookMerkleRoot(address hook)
    722
            external
    723
            view
    724
            returns (bytes32 proposedRoot, uint256 effectiveTime);
    725
    
                                                    
                                                
    726
        /// @notice Checks if a token is whitelisted as an incentive token
    727
        /// @param token The address of the token to check
    728
        /// @return True if the token is whitelisted as an incentive token, false otherwise
    729
        function isWhitelistedIncentiveToken(address token) external view returns (bool);
    730
    
                                                    
                                                
    731
        /// @notice Gets the prover address
    732
        /// @return The address of the prover
    733
        function getProver() external view returns (address);
    734
    
                                                    
                                                
    735
        /// @notice Checks if upkeep payments are currently enabled
    736
        /// @return enabled True if upkeep payments are enabled
    737
        function isUpkeepPaymentsEnabled() external view returns (bool);
    738
    
                                                    
                                                
    739
        /// @notice Gets the proposed upkeep payments status and effective time
    740
        /// @return enabled The proposed status
    741
        /// @return effectiveTime The timestamp when the change becomes effective
    742
        function getProposedUpkeepPaymentsStatus() external view returns (bool enabled, uint256 effectiveTime);
    743
    
                                                    
                                                
    744
        /// @notice Checks if an address is a registered superform manager
    745
        /// @param manager The address to check
    746
        /// @return isSuperform True if the address is a superform manager
    747
        function isSuperformManager(address manager) external view returns (bool);
    748
    
                                                    
                                                
    749
        /// @notice Gets the list of all superform managers
    750
        /// @return managers The list of all superform manager addresses
    751
        function getAllSuperformManagers() external view returns (address[] memory);
    752
    
                                                    
                                                
    753
        /// @notice Returns up to `limit` superform managers starting from `cursor`
    754
        /// @param cursor The index to start reading from (0 … len-1)
    755
        /// @param limit The maximum number of records to return
    756
        /// @return chunkOfManagers The array slice [cursor … cursor+limit-1]
    757
        /// @return next The next cursor value the caller should use, or 0 to indicate done
    758
        function getManagersPaginated(
    759
            uint256 cursor,
    760
            uint256 limit
    761
        )
    762
            external
    763
            view
    764
            returns (address[] memory chunkOfManagers, uint256 next);
    765
    
                                                    
                                                
    766
        /// @notice Gets the number of superform managers
    767
        /// @return The number of superform managers
    768
        function getSuperformManagersCount() external view returns (uint256);
    769
    
                                                    
                                                
    770
        /// @notice Gets the SUP ID
    771
        /// @return The ID of the SUP token
    772
        function SUP() external view returns (bytes32);
    773
    
                                                    
                                                
    774
        /// @notice Gets the UP ID
    775
        /// @return The ID of the UP token
    776
        function UP() external view returns (bytes32);
    777
    
                                                    
                                                
    778
        /// @notice Gets the Treasury ID
    779
        /// @return The ID for the Treasury in the registry
    780
        function TREASURY() external view returns (bytes32);
    781
    
                                                    
                                                
    782
        /// @notice Gets the SuperOracle ID
    783
        /// @return The ID for the SuperOracle in the registry
    784
        function SUPER_ORACLE() external view returns (bytes32);
    785
    
                                                    
                                                
    786
        /// @notice Gets the ECDSA PPS Oracle ID
    787
        /// @return The ID for the ECDSA PPS Oracle in the registry
    788
        function ECDSAPPSORACLE() external view returns (bytes32);
    789
    
                                                    
                                                
    790
        /// @notice Gets the SuperVaultAggregator ID
    791
        /// @return The ID for the SuperVaultAggregator in the registry
    792
        function SUPER_VAULT_AGGREGATOR() external view returns (bytes32);
    793
    
                                                    
                                                
    794
        /// @notice Gets the SuperBank ID
    795
        /// @return The ID for the SuperBank in the registry
    796
        function SUPER_BANK() external view returns (bytes32);
    797
    
                                                    
                                                
    798
        /// @notice Registers a keeper that cannot be added as authorized caller by managers
    799
        /// @dev Only governance can register protected keepers
    800
        /// @param keeper Address of the keeper to register
    801
        function registerProtectedKeeper(address keeper) external;
    802
    
                                                    
                                                
    803
        /// @notice Unregisters a protected keeper
    804
        /// @dev Only governance can unregister protected keepers
    805
        /// @param keeper Address of the keeper to unregister
    806
        function unregisterProtectedKeeper(address keeper) external;
    807
    
                                                    
                                                
    808
        /// @notice Checks if an address is a registered protected keeper
    809
        /// @param keeper Address to check
    810
        /// @return True if the address is a registered protected keeper
    811
        function isProtectedKeeper(address keeper) external view returns (bool);
    812
    
                                                    
                                                
    813
        /// @notice Gets all registered protected keepers
    814
        /// @return Array of all registered protected keeper addresses
    815
        function getProtectedKeepers() external view returns (address[] memory);
    816
    
                                                    
                                                
    817
        /// @notice Gets the number of registered protected keepers
    818
        /// @return The number of registered protected keepers
    819
        function getProtectedKeepersCount() external view returns (uint256);
    820
    
                                                    
                                                
    821
        /// @notice Gets the gas info for a specific SuperVault PPS Oracle
    822
        /// @param oracle_ The address of the oracle to get gas info for
    823
        /// @return The gas info for the specified oracle
    824
        function getGasInfo(address oracle_) external view returns (GasInfo memory);
    825
    }
    826
    
                                                    
                                                
    0.0% src/interfaces/SuperAsset/ISuperAssetFactory.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    /**
    5
     * @title ISuperAssetFactory
    6
     * @notice Interface for the SuperAssetFactory contract which deploys SuperAsset and its dependencies
    7
     */
    8
    interface ISuperAssetFactory {
    9
        /*//////////////////////////////////////////////////////////////
    10
                                EVENTS
    11
        //////////////////////////////////////////////////////////////*/
    12
    
                                                    
                                                
    13
        /// @notice Emitted when a new SuperAsset and its dependencies are created
    14
        /// @param superAsset Address of the deployed SuperAsset contract
    15
        /// @param incentiveFund Address of the deployed IncentiveFundContract
    16
        /// @param incentiveCalc Address of the deployed IncentiveCalculationContract
    17
        /// @param name Name of the SuperAsset token
    18
        /// @param symbol Symbol of the SuperAsset token
    19
        event SuperAssetCreated(
    20
            address indexed superAsset, address indexed incentiveFund, address incentiveCalc, string name, string symbol
    21
        );
    22
    
                                                    
                                                
    23
        /*//////////////////////////////////////////////////////////////
    24
                                ERRORS
    25
        //////////////////////////////////////////////////////////////*/
    26
    
                                                    
                                                
    27
        /// @notice Thrown when an address parameter is zero
    28
        error ZERO_ADDRESS();
    29
    
                                                    
                                                
    30
        /// @notice Thrown when the caller is not authorized
    31
        error UNAUTHORIZED();
    32
    
                                                    
                                                
    33
        /// @notice Thrown when ICC is not whitelisted
    34
        error ICC_NOT_WHITELISTED();
    35
    
                                                    
                                                
    36
        /*//////////////////////////////////////////////////////////////
    37
                                STRUCTS
    38
        //////////////////////////////////////////////////////////////*/
    39
    
                                                    
                                                
    40
        /// @notice Parameters required for creating a new SuperAsset
    41
        /// @param name Name of the SuperAsset token
    42
        /// @param symbol Symbol of the SuperAsset token
    43
        /// @param swapFeeInPercentage Initial swap fee percentage for deposits
    44
        /// @param swapFeeOutPercentage Initial swap fee percentage for redemptions
    45
        /// @param asset Address of the primary asset
    46
        /// @param superAssetManager Address of the manager
    47
        /// @param superAssetStrategist Address of the strategist
    48
        /// @param incentiveFundManager Address of the incentive fund manager
    49
        /// @param incentiveCalculationContract Address of the incentive calculation contract
    50
        /// @param tokenInIncentive Address of the token for incoming incentives
    51
        /// @param tokenOutIncentive Address of the token for outgoing incentives
    52
        struct AssetCreationParams {
    53
            string name;
    54
            string symbol;
    55
            uint256 swapFeeInPercentage;
    56
            uint256 swapFeeOutPercentage;
    57
            address asset;
    58
            address superAssetManager;
    59
            address superAssetStrategist;
    60
            address incentiveFundManager;
    61
            address incentiveCalculationContract;
    62
            address tokenInIncentive;
    63
            address tokenOutIncentive;
    64
        }
    65
    
                                                    
                                                
    66
        /// @notice Data for a SuperAsset
    67
        /// @param superAssetStrategist Address of the strategist
    68
        /// @param superAssetManager Address of the manager
    69
        /// @param incentiveFundManager Address of the incentive fund manager
    70
        /// @param incentiveCalculationContract Address of the incentive calculation contract
    71
        /// @param incentiveFundContract Address of the incentive fund contract
    72
        struct SuperAssetData {
    73
            address superAssetStrategist;
    74
            address superAssetManager;
    75
            address incentiveFundManager;
    76
            address incentiveCalculationContract;
    77
            address incentiveFundContract;
    78
        }
    79
    
                                                    
                                                
    80
        /*//////////////////////////////////////////////////////////////
    81
                                EXTERNAL METHODS
    82
        //////////////////////////////////////////////////////////////*/
    83
    
                                                    
                                                
    84
        /// @notice Sets the manager for a SuperAsset
    85
        /// @param superAsset Address of the SuperAsset contract
    86
        /// @param _superAssetManager Address of the manager
    87
        function setSuperAssetManager(address superAsset, address _superAssetManager) external;
    88
    
                                                    
                                                
    89
        /// @notice Sets the strategist for a SuperAsset
    90
        /// @param superAsset Address of the SuperAsset contract
    91
        /// @param _superAssetStrategist Address of the strategist
    92
        function setSuperAssetStrategist(address superAsset, address _superAssetStrategist) external;
    93
    
                                                    
                                                
    94
        /// @notice Sets the incentive fund manager for a SuperAsset
    95
        /// @param superAsset Address of the SuperAsset contract
    96
        /// @param _incentiveFundManager Address of the incentive fund manager
    97
        function setIncentiveFundManager(address superAsset, address _incentiveFundManager) external;
    98
    
                                                    
                                                
    99
        /// @notice Sets the incentive calculation contract for a SuperAsset
    100
        /// @param superAsset Address of the SuperAsset contract
    101
        /// @param incentiveCalculationContract Address of the incentive calculation contract
    102
        function setIncentiveCalculationContract(address superAsset, address incentiveCalculationContract) external;
    103
    
                                                    
                                                
    104
        /// @notice Gets the manager for a SuperAsset
    105
        /// @param superAsset Address of the SuperAsset contract
    106
        /// @return superAssetManager Address of the manager
    107
        function getSuperAssetManager(address superAsset) external view returns (address);
    108
    
                                                    
                                                
    109
        /// @notice Gets the strategist for a SuperAsset
    110
        /// @param superAsset Address of the SuperAsset contract
    111
        /// @return superAssetStrategist Address of the strategist
    112
        function getSuperAssetStrategist(address superAsset) external view returns (address);
    113
    
                                                    
                                                
    114
        /// @notice Gets the incentive fund manager for a SuperAsset
    115
        /// @param superAsset Address of the SuperAsset contract
    116
        /// @return incentiveFundManager Address of the incentive fund manager
    117
        function getIncentiveFundManager(address superAsset) external view returns (address);
    118
    
                                                    
                                                
    119
        /// @notice Gets the incentive calculation contract for a SuperAsset
    120
        /// @param superAsset Address of the SuperAsset contract
    121
        /// @return incentiveCalculationContract Address of the incentive calculation contract
    122
        function getIncentiveCalculationContract(address superAsset) external view returns (address);
    123
    
                                                    
                                                
    124
        /// @notice Gets the incentive fund contract for a SuperAsset
    125
        /// @param superAsset Address of the SuperAsset contract
    126
        /// @return incentiveFundContract Address of the incentive fund contract
    127
        function getIncentiveFundContract(address superAsset) external view returns (address);
    128
    
                                                    
                                                
    129
        /// @notice Adds an Incentive Calculation Contract to the whitelist
    130
        /// @param icc Address of the Incentive Calculation Contract
    131
        function addICCToWhitelist(address icc) external;
    132
    
                                                    
                                                
    133
        /// @notice Removes an Incentive Calculation Contract from the whitelist
    134
        /// @param icc Address of the Incentive Calculation Contract
    135
        function removeICCFromWhitelist(address icc) external;
    136
    
                                                    
                                                
    137
        /// @notice Checks if an Incentive Calculation Contract is whitelisted
    138
        /// @param icc Address of the Incentive Calculation Contract
    139
        /// @return isValid Whether the Incentive Calculation Contract is whitelisted
    140
        function isICCWhitelisted(address icc) external view returns (bool);
    141
    
                                                    
                                                
    142
        /// @notice Creates a new SuperAsset instance with its dependencies
    143
        /// @param params Parameters for creating the SuperAsset
    144
        /// @return superAsset Address of the deployed SuperAsset contract
    145
        /// @return incentiveFund Address of the deployed IncentiveFundContract
    146
        function createSuperAsset(AssetCreationParams calldata params)
    147
            external
    148
            returns (address superAsset, address incentiveFund);
    149
    }
    150
    
                                                    
                                                
    0.0% src/interfaces/SuperVault/ISuperVault.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    /// @title ISuperVault
    5
    /// @notice Interface for SuperVault core contract that manages share minting
    6
    /// @author Superform Labs
    7
    interface ISuperVault {
    8
        /*//////////////////////////////////////////////////////////////
    9
                                    ERRORS
    10
        //////////////////////////////////////////////////////////////*/
    11
        error INVALID_ASSET();
    12
        error INVALID_STRATEGY();
    13
        error INVALID_ESCROW();
    14
        error ZERO_ADDRESS();
    15
        error ZERO_AMOUNT();
    16
        error INVALID_OWNER_OR_OPERATOR();
    17
        error INVALID_AMOUNT();
    18
        error REQUEST_NOT_FOUND();
    19
        error UNAUTHORIZED();
    20
        error DEADLINE_PASSED();
    21
        error INVALID_SIGNATURE();
    22
        error NOT_IMPLEMENTED();
    23
        error INVALID_NONCE();
    24
        error INVALID_WITHDRAW_PRICE();
    25
        error TRANSFER_FAILED();
    26
        error CAP_EXCEEDED();
    27
        error INVALID_PPS();
    28
        error INVALID_CONTROLLER();
    29
        error CONTROLLER_MUST_EQUAL_OWNER();
    30
    
                                                    
                                                
    31
        /*//////////////////////////////////////////////////////////////
    32
                                    EVENTS
    33
        //////////////////////////////////////////////////////////////*/
    34
    
                                                    
                                                
    35
        event RedeemClaimable(
    36
            address indexed user,
    37
            uint256 indexed requestId,
    38
            uint256 assets,
    39
            uint256 shares,
    40
            uint256 averageWithdrawPrice,
    41
            uint256 accumulatorShares,
    42
            uint256 accumulatorCostBasis
    43
        );
    44
    
                                                    
                                                
    45
        event NonceInvalidated(address indexed sender, bytes32 indexed nonce);
    46
    
                                                    
                                                
    47
        event RedeemRequestCancelled(address indexed controller, address indexed sender);
    48
    
                                                    
                                                
    49
        event SuperGovernorSet(address indexed superGovernor);
    50
    
                                                    
                                                
    51
        /*//////////////////////////////////////////////////////////////
    52
                                EXTERNAL METHODS
    53
        //////////////////////////////////////////////////////////////*/
    54
    
                                                    
                                                
    55
        function cancelRedeem(address controller) external;
    56
    
                                                    
                                                
    57
        /// @notice Burn shares, only callable by strategy
    58
        /// @param amount The amount of shares to burn
    59
        function burnShares(uint256 amount) external;
    60
    
                                                    
                                                
    61
        /// @notice Callback function for when a redeem becomes claimable
    62
        /// @param user The user whose redeem is claimable
    63
        /// @param assets The amount of assets to be received
    64
        /// @param shares The amount of shares redeemed
    65
        /// @param averageWithdrawPrice The average price of the redeem
    66
        /// @param accumulatorShares The amount of shares in the accumulator
    67
        /// @param accumulatorCostBasis The cost basis of the accumulator
    68
        function onRedeemClaimable(
    69
            address user,
    70
            uint256 assets,
    71
            uint256 shares,
    72
            uint256 averageWithdrawPrice,
    73
            uint256 accumulatorShares,
    74
            uint256 accumulatorCostBasis
    75
        )
    76
            external;
    77
    }
    78
    
                                                    
                                                
    0.0% src/interfaces/SuperVault/ISuperVaultAggregator.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    5
    import { ISuperVaultStrategy } from "../SuperVault/ISuperVaultStrategy.sol";
    6
    
                                                    
                                                
    7
    /// @title ISuperVaultAggregator
    8
    /// @author Superform Labs
    9
    /// @notice Interface for the SuperVaultAggregator contract
    10
    /// @dev Registry and PPS oracle for all SuperVaults
    11
    interface ISuperVaultAggregator {
    12
        /*//////////////////////////////////////////////////////////////
    13
                                     STRUCTS
    14
        //////////////////////////////////////////////////////////////*/
    15
        /// @notice Arguments for forwarding PPS updates to avoid stack too deep errors
    16
        /// @param strategy Address of the strategy being updated
    17
        /// @param isExempt Whether the update is exempt from paying upkeep
    18
        /// @param pps New price-per-share value
    19
        /// @param ppsStdev Standard deviation of the price-per-share
    20
        /// @param validatorSet Number of validators who calculated this PPS
    21
        /// @param totalValidators Total number of validators in the network
    22
        /// @param timestamp Timestamp when the value was generated
    23
        /// @param upkeepCost Amount of upkeep tokens to charge if not exempt
    24
        struct PPSUpdateData {
    25
            address strategy;
    26
            bool isExempt;
    27
            uint256 pps;
    28
            uint256 ppsStdev;
    29
            uint256 validatorSet;
    30
            uint256 totalValidators;
    31
            uint256 timestamp;
    32
            uint256 upkeepCost;
    33
        }
    34
    
                                                    
                                                
    35
        /// @notice Local variables for vault creation to avoid stack too deep
    36
        /// @param currentNonce Current vault creation nonce
    37
        /// @param salt Salt for deterministic proxy creation
    38
        /// @param success Whether asset decimals retrieval was successful
    39
        /// @param assetDecimals Decimals of the underlying asset
    40
        /// @param underlyingDecimals Final decimals to use (18 if retrieval failed)
    41
        /// @param initialPPS Initial price-per-share value
    42
        struct VaultCreationLocalVars {
    43
            uint256 currentNonce;
    44
            bytes32 salt;
    45
            bool success;
    46
            uint8 assetDecimals;
    47
            uint8 underlyingDecimals;
    48
            uint256 initialPPS;
    49
        }
    50
    
                                                    
                                                
    51
        /// @notice Strategy configuration and state data
    52
        /// @param pps Current price-per-share value
    53
        /// @param lastUpdateTimestamp Last time PPS was updated
    54
        /// @param minUpdateInterval Minimum time interval between PPS updates
    55
        /// @param maxStaleness Maximum time allowed between PPS updates before staleness
    56
        /// @param isPaused Whether the strategy is paused
    57
        /// @param mainManager Address of the primary manager controlling the strategy
    58
        /// @param secondaryManagers Set of secondary managers that can manage the strategy
    59
        /// @param authorizedCallers List of callers authorized to update PPS without paying upkeep
    60
        struct StrategyData {
    61
            uint256 pps;
    62
            uint256 ppsStdev;
    63
            uint256 lastUpdateTimestamp;
    64
            uint256 minUpdateInterval;
    65
            uint256 maxStaleness;
    66
            bool isPaused;
    67
            address mainManager;
    68
            EnumerableSet.AddressSet secondaryManagers;
    69
            EnumerableSet.AddressSet authorizedCallers;
    70
            // Manager change proposal data
    71
            address proposedManager;
    72
            uint256 managerChangeEffectiveTime;
    73
            // Hook validation data
    74
            bytes32 managerHooksRoot;
    75
            // Hook root update proposal data
    76
            bytes32 proposedHooksRoot;
    77
            uint256 hooksRootEffectiveTime;
    78
            // Veto status
    79
            bool hooksRootVetoed;
    80
            // PPS Verification thresholds
    81
            uint256 dispersionThreshold; // Threshold for standard deviation / mean
    82
            uint256 deviationThreshold; // Threshold for abs(new - current) / current
    83
            uint256 mnThreshold; // Threshold for validatorSet / totalValidators ratio, scaled by 1e18
    84
            // Banned global leaves mapping
    85
            mapping(bytes32 => bool) bannedLeaves; // Mapping of leaf hash to banned status
    86
        }
    87
    
                                                    
                                                
    88
        /// @notice Parameters for creating a new SuperVault trio
    89
        /// @param asset Address of the underlying asset
    90
        /// @param name Name of the vault token
    91
        /// @param symbol Symbol of the vault token
    92
        /// @param mainManager Address of the vault mainManager
    93
        /// @param minUpdateInterval Minimum time interval between PPS updates
    94
        /// @param maxStaleness Maximum time allowed between PPS updates before staleness
    95
        /// @param feeConfig Fee configuration for the vault
    96
        struct VaultCreationParams {
    97
            address asset;
    98
            string name;
    99
            string symbol;
    100
            address mainManager;
    101
            address[] secondaryManagers;
    102
            uint256 minUpdateInterval;
    103
            uint256 maxStaleness;
    104
            ISuperVaultStrategy.FeeConfig feeConfig;
    105
        }
    106
    
                                                    
                                                
    107
        /// @notice Struct to hold cached hook validation state variables to avoid stack too deep
    108
        /// @param globalHooksRootVetoed Cached global hooks root veto status
    109
        /// @param globalHooksRoot Cached global hooks root
    110
        /// @param strategyHooksRootVetoed Cached strategy hooks root veto status
    111
        /// @param strategyRoot Cached strategy hooks root
    112
        struct HookValidationCache {
    113
            bool globalHooksRootVetoed;
    114
            bytes32 globalHooksRoot;
    115
            bool strategyHooksRootVetoed;
    116
            bytes32 strategyRoot;
    117
        }
    118
    
                                                    
                                                
    119
        /// @notice Arguments for validating a hook to avoid stack too deep
    120
        /// @param hookAddress Address of the hook contract
    121
        /// @param hookArgs Encoded arguments for the hook operation
    122
        /// @param globalProof Merkle proof for the global root
    123
        /// @param strategyProof Merkle proof for the strategy-specific root
    124
        struct ValidateHookArgs {
    125
            address hookAddress;
    126
            bytes hookArgs;
    127
            bytes32[] globalProof;
    128
            bytes32[] strategyProof;
    129
        }
    130
    
                                                    
                                                
    131
        /*//////////////////////////////////////////////////////////////
    132
                                     EVENTS
    133
        //////////////////////////////////////////////////////////////*/
    134
        /// @notice Emitted when a new vault trio is created
    135
        /// @param vault Address of the created SuperVault
    136
        /// @param strategy Address of the created SuperVaultStrategy
    137
        /// @param escrow Address of the created SuperVaultEscrow
    138
        /// @param asset Address of the underlying asset
    139
        /// @param name Name of the vault token
    140
        /// @param symbol Symbol of the vault token
    141
        /// @param nonce The nonce used for vault creation
    142
        event VaultDeployed(
    143
            address indexed vault,
    144
            address indexed strategy,
    145
            address escrow,
    146
            address asset,
    147
            string name,
    148
            string symbol,
    149
            uint256 indexed nonce
    150
        );
    151
    
                                                    
                                                
    152
        /// @notice Emitted when a PPS value is updated
    153
        /// @param strategy Address of the strategy
    154
        /// @param pps New price-per-share value
    155
        /// @param ppsStdev Standard deviation of price-per-share value
    156
        /// @param validatorSet Number of validators who calculated this PPS
    157
        /// @param totalValidators Total number of validators in the network
    158
        /// @param timestamp Timestamp of the update
    159
        event PPSUpdated(
    160
            address indexed strategy,
    161
            uint256 pps,
    162
            uint256 ppsStdev,
    163
            uint256 validatorSet,
    164
            uint256 totalValidators,
    165
            uint256 timestamp
    166
        );
    167
    
                                                    
                                                
    168
        /// @notice Emitted when a strategy is paused due to missed updates
    169
        /// @param strategy Address of the paused strategy
    170
        event StrategyPaused(address indexed strategy);
    171
    
                                                    
                                                
    172
        /// @notice Emitted when a strategy is unpaused
    173
        /// @param strategy Address of the unpaused strategy
    174
        event StrategyUnpaused(address indexed strategy);
    175
    
                                                    
                                                
    176
        /// @notice Emitted when a strategy validation check fails but execution continues
    177
        /// @param strategy Address of the strategy that failed the check
    178
        /// @param reason String description of which check failed
    179
        event StrategyCheckFailed(address indexed strategy, string reason);
    180
    
                                                    
                                                
    181
        /// @notice Emitted when upkeep tokens are deposited
    182
        /// @param manager Address of the manager
    183
        /// @param amount Amount of UP tokens deposited
    184
        event UpkeepDeposited(address indexed manager, uint256 amount);
    185
    
                                                    
                                                
    186
        /// @notice Emitted when upkeep tokens are withdrawn
    187
        /// @param manager Address of the manager
    188
        /// @param amount Amount of UP tokens withdrawn
    189
        event UpkeepWithdrawn(address indexed manager, uint256 amount);
    190
    
                                                    
                                                
    191
        /// @notice Emitted when upkeep tokens are spent for validation
    192
        /// @param manager Address of the manager
    193
        /// @param amount Amount of UP tokens spent
    194
        event UpkeepSpent(address indexed manager, uint256 amount);
    195
    
                                                    
                                                
    196
        /// @notice Emitted when stake tokens are deposited
    197
        /// @param manager Address of the manager
    198
        /// @param amount Amount of UP tokens deposited as stake
    199
        event StakeDeposited(address indexed manager, uint256 amount);
    200
    
                                                    
                                                
    201
        /// @notice Emitted when stake tokens are withdrawn
    202
        /// @param manager Address of the manager
    203
        /// @param amount Amount of UP tokens withdrawn from stake
    204
        event StakeWithdrawn(address indexed manager, uint256 amount);
    205
    
                                                    
                                                
    206
        /// @notice Emitted when a manager's stake is slashed
    207
        /// @param manager The manager whose stake was slashed
    208
        /// @param amount The amount of UP tokens slashed
    209
        event StakeSlashed(address indexed manager, uint256 amount);
    210
    
                                                    
                                                
    211
        /// @notice Emitted when an authorized caller is added for a strategy
    212
        /// @param strategy Address of the strategy
    213
        /// @param caller Address of the authorized caller
    214
        event AuthorizedCallerAdded(address indexed strategy, address indexed caller);
    215
    
                                                    
                                                
    216
        /// @notice Emitted when an authorized caller is removed for a strategy
    217
        /// @param strategy Address of the strategy
    218
        /// @param caller Address of the removed caller
    219
        event AuthorizedCallerRemoved(address indexed strategy, address indexed caller);
    220
    
                                                    
                                                
    221
        /// @notice Emitted when a secondary manager is added to a strategy
    222
        /// @param strategy Address of the strategy
    223
        /// @param manager Address of the manager added
    224
        event SecondaryManagerAdded(address indexed strategy, address indexed manager);
    225
    
                                                    
                                                
    226
        /// @notice Emitted when a secondary manager is removed from a strategy
    227
        /// @param strategy Address of the strategy
    228
        /// @param manager Address of the manager removed
    229
        event SecondaryManagerRemoved(address indexed strategy, address indexed manager);
    230
    
                                                    
                                                
    231
        /// @notice Emitted when a primary manager is changed
    232
        /// @param strategy Address of the strategy
    233
        /// @param oldManager Address of the old primary manager
    234
        /// @param newManager Address of the new primary manager
    235
        event PrimaryManagerChanged(
    236
            address indexed strategy, address indexed oldManager, address indexed newManager
    237
        );
    238
    
                                                    
                                                
    239
        /// @notice Emitted when a primary manager is changed to a superform manager
    240
        /// @param strategy Address of the strategy
    241
        /// @param oldManager Address of the old primary manager
    242
        /// @param newManager Address of the new primary manager (superform manager)
    243
        event PrimaryManagerChangedToSuperform(
    244
            address indexed strategy, address indexed oldManager, address indexed newManager
    245
        );
    246
    
                                                    
                                                
    247
        /// @notice Emitted when a change to primary manager is proposed by a secondary manager
    248
        /// @param strategy Address of the strategy
    249
        /// @param proposer Address of the secondary manager who made the proposal
    250
        /// @param newManager Address of the proposed new primary manager
    251
        /// @param effectiveTime Timestamp when the proposal can be executed
    252
        event PrimaryManagerChangeProposed(
    253
            address indexed strategy, address indexed proposer, address indexed newManager, uint256 effectiveTime
    254
        );
    255
    
                                                    
                                                
    256
        /// @notice Emitted when a PPS update is stale (Validators could get slashed for innactivity)
    257
        /// @param strategy Address of the strategy
    258
        /// @param updateAuthority Address of the update authority
    259
        /// @param timestamp Timestamp of the stale update
    260
        event StaleUpdate(address indexed strategy, address indexed updateAuthority, uint256 timestamp);
    261
    
                                                    
                                                
    262
        /// @notice Emitted when the upkeep cost per update is changed
    263
        /// @param oldCost Previous upkeep cost per update
    264
        /// @param newCost New upkeep cost per update
    265
        event UpkeepCostUpdated(uint256 oldCost, uint256 newCost);
    266
    
                                                    
                                                
    267
        /// @notice Emitted when the global hooks Merkle root is being updated
    268
        /// @param root New root value
    269
        /// @param effectiveTime Timestamp when the root becomes effective
    270
        event GlobalHooksRootUpdateProposed(bytes32 indexed root, uint256 effectiveTime);
    271
    
                                                    
                                                
    272
        /// @notice Emitted when the global hooks Merkle root is updated
    273
        /// @param oldRoot Previous root value
    274
        /// @param newRoot New root value
    275
        event GlobalHooksRootUpdated(bytes32 indexed oldRoot, bytes32 newRoot);
    276
    
                                                    
                                                
    277
        /// @notice Emitted when a strategy-specific hooks Merkle root is updated
    278
        /// @param strategy Address of the strategy
    279
        /// @param oldRoot Previous root value (may be zero)
    280
        /// @param newRoot New root value
    281
        event StrategyHooksRootUpdated(address indexed strategy, bytes32 oldRoot, bytes32 newRoot);
    282
    
                                                    
                                                
    283
        /// @notice Emitted when a strategy-specific hooks Merkle root is proposed
    284
        /// @param strategy Address of the strategy
    285
        /// @param proposer Address of the account proposing the new root
    286
        /// @param root New root value
    287
        /// @param effectiveTime Timestamp when the root becomes effective
    288
        event StrategyHooksRootUpdateProposed(
    289
            address indexed strategy, address indexed proposer, bytes32 root, uint256 effectiveTime
    290
        );
    291
    
                                                    
                                                
    292
        /// @notice Emitted when a proposed global hooks root update is vetoed by SuperGovernor
    293
        /// @param vetoed Whether the root is being vetoed (true) or unvetoed (false)
    294
        /// @param root The root value affected
    295
        event GlobalHooksRootVetoStatusChanged(bool vetoed, bytes32 indexed root);
    296
    
                                                    
                                                
    297
        /// @notice Emitted when a strategy's hooks Merkle root veto status changes
    298
        /// @param strategy Address of the strategy
    299
        /// @param vetoed Whether the root is being vetoed (true) or unvetoed (false)
    300
        /// @param root The root value affected
    301
        event StrategyHooksRootVetoStatusChanged(address indexed strategy, bool vetoed, bytes32 indexed root);
    302
    
                                                    
                                                
    303
        /// @notice Emitted when a strategy's PPS verification thresholds are updated
    304
        /// @param strategy Address of the strategy
    305
        /// @param dispersionThreshold New dispersion threshold (stddev/mean)
    306
        /// @param deviationThreshold New deviation threshold (abs diff/current)
    307
        /// @param mnThreshold New M/N threshold (validatorSet/totalValidators)
    308
        event PPSVerificationThresholdsUpdated(
    309
            address indexed strategy, uint256 dispersionThreshold, uint256 deviationThreshold, uint256 mnThreshold
    310
        );
    311
    
                                                    
                                                
    312
        /// @notice Emitted when the hooks root update timelock is changed
    313
        /// @param newTimelock New timelock duration in seconds
    314
        event HooksRootUpdateTimelockChanged(uint256 newTimelock);
    315
    
                                                    
                                                
    316
        /// @notice Emitted when global leaves status is changed for a strategy
    317
        /// @param strategy Address of the strategy
    318
        /// @param leaves Array of leaf hashes that had their status changed
    319
        /// @param statuses Array of new banned statuses (true = banned, false = allowed)
    320
        event GlobalLeavesStatusChanged(address indexed strategy, bytes32[] leaves, bool[] statuses);
    321
    
                                                    
                                                
    322
        /// @notice Emitted when a proposed global hooks root update is vetoed by a guardian
    323
        /// @param guardian Address of the guardian who vetoed the update
    324
        /// @param root The vetoed root value
    325
        event GlobalHooksRootVetoed(address indexed guardian, bytes32 indexed root);
    326
    
                                                    
                                                
    327
        /// @notice Emitted when a proposed strategy hooks root update is vetoed by a guardian
    328
        /// @param guardian Address of the guardian who vetoed the update
    329
        /// @param strategy Address of the strategy whose root update was vetoed
    330
        /// @param root The vetoed root value
    331
        event StrategyHooksRootVetoed(address indexed guardian, address indexed strategy, bytes32 indexed root);
    332
    
                                                    
                                                
    333
        /// @notice Emitted when upkeep is claimed
    334
        /// @param superBank Address of the superBank
    335
        /// @param amount Amount of upkeep claimed
    336
        event UpkeepClaimed(address indexed superBank, uint256 amount);
    337
    
                                                    
                                                
    338
        /// @notice Emitted when PPS update is too frequent (before minUpdateInterval)
    339
        event UpdateTooFrequent();
    340
    
                                                    
                                                
    341
        /// @notice Emitted when PPS update timestamp is not monotonically increasing
    342
        event TimestampNotMonotonic();
    343
    
                                                    
                                                
    344
        /// @notice Emitted when a manager does not have enough upkeep balance
    345
        event InsufficientUpkeep(address indexed strategy, address indexed manager, uint256 balance, uint256 cost);
    346
    
                                                    
                                                
    347
        /// @notice Emitted when the provided timestamp is too large
    348
        event ProvidedTimestampExceedsBlockTimestamp(address indexed strategy, uint256 argsTimestamp, uint256 blockTimestamp);
    349
    
                                                    
                                                
    350
        /// @notice Emitted when a strategy is unknown
    351
        event UnknownStrategy(address indexed strategy);
    352
    
                                                    
                                                
    353
        /// @notice Emitted when a strategy is managed by Superform
    354
        event SuperformManager(address indexed strategy, address indexed manager);
    355
    
                                                    
                                                
    356
        /// @notice Emitted when the caller is authorized
    357
        event AuthorizedCaller(address indexed strategy, address indexed caller);
    358
    
                                                    
                                                
    359
        /*///////////////////////////////////////////////////////////////
    360
                                     ERRORS
    361
        //////////////////////////////////////////////////////////////*/
    362
        /// @notice Thrown when address provided is zero
    363
        error ZERO_ADDRESS();
    364
        /// @notice Thrown when amount provided is zero
    365
        error ZERO_AMOUNT();
    366
        /// @notice Thrown when array length is zero
    367
        error ZERO_ARRAY_LENGTH();
    368
        /// @notice Thrown when array length is zero
    369
        error ARRAY_LENGTH_MISMATCH();
    370
        /// @notice Thrown when asset is invalid
    371
        error INVALID_ASSET();
    372
        /// @notice Thrown when insufficient upkeep balance for operation
    373
        error INSUFFICIENT_UPKEEP();
    374
        /// @notice Thrown when vault is paused but operation requires active state
    375
        error VAULT_PAUSED();
    376
        /// @notice Thrown when caller is not an approved PPS oracle
    377
        error UNAUTHORIZED_PPS_ORACLE();
    378
        /// @notice Thrown when PPS update is too stale (after maxStaleness)
    379
        error UPDATE_TOO_STALE();
    380
        /// @notice Thrown when caller is not authorized for update
    381
        error UNAUTHORIZED_UPDATE_AUTHORITY();
    382
        /// @notice Thrown when strategy address is not a known SuperVault strategy
    383
        error UNKNOWN_STRATEGY();
    384
        /// @notice Thrown when withdrawing more upkeep than available
    385
        error INSUFFICIENT_UPKEEP_BALANCE();
    386
        /// @notice Thrown when withdrawing more stake than available
    387
        error INSUFFICIENT_STAKE_BALANCE();
    388
        /// @notice Thrown when caller is already authorized
    389
        error CALLER_ALREADY_AUTHORIZED();
    390
        /// @notice Thrown when caller is not authorized
    391
        error CALLER_NOT_AUTHORIZED();
    392
        /// @notice Thrown when array index is out of bounds
    393
        error INDEX_OUT_OF_BOUNDS();
    394
        /// @notice Thrown when attempting to remove the last manager
    395
        error CANNOT_REMOVE_LAST_MANAGER();
    396
        /// @notice Thrown when attempting to add a manager that already exists
    397
        error MANAGER_ALREADY_EXISTS();
    398
        /// @notice Thrown when there is no pending global hooks root change
    399
        error NO_PENDING_GLOBAL_ROOT_CHANGE();
    400
        /// @notice Thrown when attempting to execute an in-progress manager change before timelock elapsed
    401
        error MANAGER_CHANGE_NOT_READY();
    402
        /// @notice Thrown when attempting to execute a hooks root change before timelock has elapsed
    403
        error ROOT_UPDATE_NOT_READY();
    404
        /// @notice Thrown when a provided hook fails Merkle proof validation
    405
        error HOOK_VALIDATION_FAILED();
    406
        /// @notice Thrown when a non-guardian tries to veto a root update
    407
        error NOT_A_GUARDIAN();
    408
        /// @notice Thrown when trying to veto a root update that doesn't exist
    409
        error NO_PENDING_ROOT_UPDATE();
    410
        /// @notice Thrown when manager is not found
    411
        error MANAGER_NOT_FOUND();
    412
        /// @notice Thrown when there is no pending manager change proposal
    413
        error NO_PENDING_MANAGER_CHANGE();
    414
        /// @notice Thrown when caller is not authorized to update settings
    415
        error UNAUTHORIZED_CALLER();
    416
        /// @notice Thrown when the timelock for a proposed change has not expired
    417
        error TIMELOCK_NOT_EXPIRED();
    418
        /// @notice Thrown when an array length is invalid
    419
        error INVALID_ARRAY_LENGTH();
    420
        /// @notice Thrown when trying to add a protected keeper as an authorized caller
    421
        error CANNOT_ADD_PROTECTED_KEEPER();
    422
        /// @notice Thrown when the provided maxStaleness is less than the minimum required staleness
    423
        error MAX_STALENESS_TOO_LOW();
    424
        /// @notice Thrown when arrays have mismatched lengths
    425
        error MISMATCHED_ARRAY_LENGTHS();
    426
        /// @notice Thrown when timestamp is invalid
    427
        error INVALID_TIMESTAMP(uint256 index);
    428
        /// @notice Thrown when too many secondary managers are added
    429
        error TOO_MANY_SECONDARY_MANAGERS();
    430
        /// @notice Thrown when the number of strategies exceeds the maximum allowed
    431
        error MAX_STRATEGIES_EXCEEDED();
    432
        /// @notice Thrown when provided timestamp is too large
    433
        error TIMESTAMP_EXCEEDS_BLOCK();
    434
    
                                                    
                                                
    435
        /*//////////////////////////////////////////////////////////////
    436
                                VAULT CREATION
    437
        //////////////////////////////////////////////////////////////*/
    438
        /// @notice Creates a new SuperVault trio (SuperVault, SuperVaultStrategy, SuperVaultEscrow)
    439
        /// @param params Parameters for the new vault creation
    440
        /// @return superVault Address of the created SuperVault
    441
        /// @return strategy Address of the created SuperVaultStrategy
    442
        /// @return escrow Address of the created SuperVaultEscrow
    443
        function createVault(VaultCreationParams calldata params)
    444
            external
    445
            returns (address superVault, address strategy, address escrow);
    446
    
                                                    
                                                
    447
        /*//////////////////////////////////////////////////////////////
    448
                              PPS UPDATE FUNCTIONS
    449
        //////////////////////////////////////////////////////////////*/
    450
        /// @notice Arguments for batch forwarding PPS updates
    451
        /// @param strategies Array of strategy addresses
    452
        /// @param ppss Array of price-per-share values
    453
        /// @param ppsStdevs Array of standard deviations of price-per-share values
    454
        /// @param validatorSets Array of numbers of validators who calculated each PPS
    455
        /// @param totalValidators Total number of validators in the network
    456
        /// @param timestamps Array of timestamps when values were generated
    457
        struct ForwardPPSArgs {
    458
            address[] strategies;
    459
            uint256[] ppss;
    460
            uint256[] ppsStdevs;
    461
            uint256[] validatorSets;
    462
            uint256[] totalValidators;
    463
            uint256[] timestamps;
    464
            address updateAuthority;
    465
        }
    466
    
                                                    
                                                
    467
        /// @notice Batch forwards validated PPS updates to multiple strategies
    468
        /// @param args Struct containing all batch PPS update parameters
    469
        function forwardPPS(ForwardPPSArgs calldata args) external;
    470
    
                                                    
                                                
    471
        /*//////////////////////////////////////////////////////////////
    472
                            UPKEEP MANAGEMENT
    473
        //////////////////////////////////////////////////////////////*/
    474
    
                                                    
                                                
    475
        /// @notice Deposits UP tokens for manager upkeep
    476
        /// @param manager Address of the manager to deposit for
    477
        /// @param amount Amount of UP tokens to deposit
    478
        function depositUpkeep(address manager, uint256 amount) external;
    479
    
                                                    
                                                
    480
        /// @notice Withdraws UP tokens from manager upkeep balance
    481
        /// @param amount Amount of UP tokens to withdraw
    482
        function withdrawUpkeep(uint256 amount) external;
    483
    
                                                    
                                                
    484
        /// @notice Claims upkeep tokens from the contract
    485
        /// @param amount Amount of UP tokens to claim
    486
        function claimUpkeep(uint256 amount) external;
    487
    
                                                    
                                                
    488
        /*//////////////////////////////////////////////////////////////
    489
                            STAKE MANAGEMENT
    490
        //////////////////////////////////////////////////////////////*/
    491
    
                                                    
                                                
    492
        /// @notice Deposits UP tokens as stake for manager economic security
    493
        /// @param manager Address of the manager to deposit stake for
    494
        /// @param amount Amount of UP tokens to deposit as stake
    495
        function depositStake(address manager, uint256 amount) external;
    496
    
                                                    
                                                
    497
        /// @notice Withdraws UP tokens from manager stake balance
    498
        /// @param amount Amount of UP tokens to withdraw from stake
    499
        function withdrawStake(uint256 amount) external;
    500
    
                                                    
                                                
    501
        /// @notice Slashes a manager's stake balance by a specified amount
    502
        /// @param manager The manager whose stake will be slashed
    503
        /// @param amount The amount of UP tokens to slash from the manager's stake balance
    504
        function slashStake(address manager, uint256 amount) external;
    505
    
                                                    
                                                
    506
        /*//////////////////////////////////////////////////////////////
    507
                            AUTHORIZED CALLER MANAGEMENT
    508
        //////////////////////////////////////////////////////////////*/
    509
    
                                                    
                                                
    510
        /// @notice Adds an authorized caller for a strategy
    511
        /// @param strategy Address of the strategy
    512
        /// @param caller Address of the caller to authorize
    513
        function addAuthorizedCaller(address strategy, address caller) external;
    514
    
                                                    
                                                
    515
        /// @notice Removes an authorized caller for a strategy
    516
        /// @param strategy Address of the strategy
    517
        /// @param caller Address of the caller to remove
    518
        function removeAuthorizedCaller(address strategy, address caller) external;
    519
    
                                                    
                                                
    520
        /*//////////////////////////////////////////////////////////////
    521
                           MANAGER MANAGEMENT FUNCTIONS
    522
        //////////////////////////////////////////////////////////////*/
    523
    
                                                    
                                                
    524
        /// @notice Adds a secondary manager to a strategy
    525
        /// @notice A manager can either be secondary or primary
    526
        /// @param strategy Address of the strategy
    527
        /// @param manager Address of the manager to add
    528
        function addSecondaryManager(address strategy, address manager) external;
    529
    
                                                    
                                                
    530
        /// @notice Removes a secondary manager from a strategy
    531
        /// @param strategy Address of the strategy
    532
        /// @param manager Address of the manager to remove
    533
        function removeSecondaryManager(address strategy, address manager) external;
    534
    
                                                    
                                                
    535
        /// @notice Changes the primary manager of a strategy immediately (only callable by SuperGovernor)
    536
        /// @notice A manager can either be secondary or primary
    537
        /// @param strategy Address of the strategy
    538
        /// @param newManager Address of the new primary manager
    539
        function changePrimaryManager(address strategy, address newManager) external;
    540
    
                                                    
                                                
    541
        /// @notice Proposes a change to the primary manager (callable by secondary managers)
    542
        /// @notice A manager can either be secondary or primary
    543
        /// @param strategy Address of the strategy
    544
        /// @param newManager Address of the proposed new primary manager
    545
        function proposeChangePrimaryManager(address strategy, address newManager) external;
    546
    
                                                    
                                                
    547
        /// @notice Executes a previously proposed change to the primary manager after timelock
    548
        /// @param strategy Address of the strategy
    549
        function executeChangePrimaryManager(address strategy) external;
    550
    
                                                    
                                                
    551
        /*//////////////////////////////////////////////////////////////
    552
                            HOOK VALIDATION FUNCTIONS
    553
        //////////////////////////////////////////////////////////////*/
    554
        /// @notice Sets a new hooks root update timelock duration
    555
        /// @param newTimelock The new timelock duration in seconds
    556
        function setHooksRootUpdateTimelock(uint256 newTimelock) external;
    557
    
                                                    
                                                
    558
        /// @notice Proposes an update to the global hooks Merkle root
    559
        /// @dev Only callable by SUPER_GOVERNOR
    560
        /// @param newRoot New Merkle root for global hooks validation
    561
        function proposeGlobalHooksRoot(bytes32 newRoot) external;
    562
    
                                                    
                                                
    563
        /// @notice Executes a previously proposed global hooks root update after timelock period
    564
        /// @dev Can be called by anyone after the timelock period has elapsed
    565
        function executeGlobalHooksRootUpdate() external;
    566
    
                                                    
                                                
    567
        /// @notice Proposes an update to a strategy-specific hooks Merkle root
    568
        /// @dev Only callable by the main manager for the strategy
    569
        /// @param strategy Address of the strategy
    570
        /// @param newRoot New Merkle root for strategy-specific hooks
    571
        function proposeStrategyHooksRoot(address strategy, bytes32 newRoot) external;
    572
    
                                                    
                                                
    573
        /// @notice Executes a previously proposed strategy hooks root update after timelock period
    574
        /// @dev Can be called by anyone after the timelock period has elapsed
    575
        /// @param strategy Address of the strategy whose root update to execute
    576
        function executeStrategyHooksRootUpdate(address strategy) external;
    577
    
                                                    
                                                
    578
        /// @notice Set veto status for the global hooks root
    579
        /// @dev Only callable by SuperGovernor
    580
        /// @param vetoed Whether to veto (true) or unveto (false) the global hooks root
    581
        function setGlobalHooksRootVetoStatus(bool vetoed) external;
    582
    
                                                    
                                                
    583
        /// @notice Set veto status for a strategy-specific hooks root
    584
        /// @notice Sets the veto status of a strategy's hooks Merkle root
    585
        /// @param strategy Address of the strategy
    586
        /// @param vetoed Whether to veto (true) or unveto (false)
    587
        function setStrategyHooksRootVetoStatus(address strategy, bool vetoed) external;
    588
    
                                                    
                                                
    589
        /// @notice Updates the PPS verification thresholds for a strategy
    590
        /// @param strategy Address of the strategy
    591
        /// @param dispersionThreshold_ New dispersion threshold (stddev/mean ratio, scaled by 1e18)
    592
        /// @param deviationThreshold_ New deviation threshold (abs diff/current ratio, scaled by 1e18)
    593
        /// @param mnThreshold_ New M/N threshold (validatorSet/totalValidators ratio, scaled by 1e18)
    594
        function updatePPSVerificationThresholds(
    595
            address strategy,
    596
            uint256 dispersionThreshold_,
    597
            uint256 deviationThreshold_,
    598
            uint256 mnThreshold_
    599
        )
    600
            external;
    601
    
                                                    
                                                
    602
        /// @notice Changes the banned status of global leaves for a specific strategy
    603
        /// @dev Only callable by the primary manager of the strategy
    604
        /// @param leaves Array of leaf hashes to change status for
    605
        /// @param statuses Array of banned statuses (true = banned, false = allowed)
    606
        /// @param strategy Address of the strategy to change banned leaves for
    607
        function changeGlobalLeavesStatus(
    608
            bytes32[] memory leaves,
    609
            bool[] memory statuses,
    610
            address strategy
    611
        )
    612
            external;
    613
    
                                                    
                                                
    614
        /*//////////////////////////////////////////////////////////////
    615
                                  VIEW FUNCTIONS
    616
        //////////////////////////////////////////////////////////////*/
    617
    
                                                    
                                                
    618
        /// @notice Returns the current vault creation nonce
    619
        /// @dev This nonce is incremented every time a new vault is created
    620
        /// @return Current vault creation nonce
    621
        function getCurrentNonce() external view returns (uint256);
    622
    
                                                    
                                                
    623
        /// @notice Check if the global hooks root is currently vetoed
    624
        /// @return vetoed True if the global hooks root is vetoed
    625
        function isGlobalHooksRootVetoed() external view returns (bool vetoed);
    626
    
                                                    
                                                
    627
        /// @notice Check if a strategy hooks root is currently vetoed
    628
        /// @param strategy Address of the strategy to check
    629
        /// @return vetoed True if the strategy hooks root is vetoed
    630
        function isStrategyHooksRootVetoed(address strategy) external view returns (bool vetoed);
    631
    
                                                    
                                                
    632
        /// @notice Gets the current hooks root update timelock duration
    633
        /// @return The current timelock duration in seconds
    634
        function getHooksRootUpdateTimelock() external view returns (uint256);
    635
    
                                                    
                                                
    636
        /// @notice Gets the current PPS (price-per-share) for a strategy
    637
        /// @param strategy Address of the strategy
    638
        /// @return pps Current price-per-share value
    639
        function getPPS(address strategy) external view returns (uint256 pps);
    640
    
                                                    
                                                
    641
        /// @notice Gets the current PPS and its standard deviation for a strategy
    642
        /// @param strategy Address of the strategy
    643
        /// @return pps Current price-per-share value
    644
        /// @return ppsStdev Standard deviation of price-per-share value
    645
        function getPPSWithStdDev(address strategy) external view returns (uint256 pps, uint256 ppsStdev);
    646
    
                                                    
                                                
    647
        /// @notice Gets the last update timestamp for a strategy's PPS
    648
        /// @param strategy Address of the strategy
    649
        /// @return timestamp Last update timestamp
    650
        function getLastUpdateTimestamp(address strategy) external view returns (uint256 timestamp);
    651
    
                                                    
                                                
    652
        /// @notice Gets the minimum update interval for a strategy
    653
        /// @param strategy Address of the strategy
    654
        /// @return interval Minimum time between updates
    655
        function getMinUpdateInterval(address strategy) external view returns (uint256 interval);
    656
    
                                                    
                                                
    657
        /// @notice Gets the maximum staleness period for a strategy
    658
        /// @param strategy Address of the strategy
    659
        /// @return staleness Maximum time allowed between updates
    660
        function getMaxStaleness(address strategy) external view returns (uint256 staleness);
    661
    
                                                    
                                                
    662
        /// @notice Gets the PPS verification thresholds for a strategy
    663
        /// @param strategy Address of the strategy
    664
        /// @return dispersionThreshold The current dispersion threshold (stddev/mean ratio, scaled by 1e18)
    665
        /// @return deviationThreshold The current deviation threshold (abs diff/current ratio, scaled by 1e18)
    666
        /// @return mnThreshold The current M/N threshold (validatorSet/totalValidators ratio, scaled by 1e18)
    667
        function getPPSVerificationThresholds(address strategy)
    668
            external
    669
            view
    670
            returns (uint256 dispersionThreshold, uint256 deviationThreshold, uint256 mnThreshold);
    671
    
                                                    
                                                
    672
        /// @notice Checks if a strategy is currently paused
    673
        /// @param strategy Address of the strategy
    674
        /// @return isPaused True if paused, false otherwise
    675
        function isStrategyPaused(address strategy) external view returns (bool isPaused);
    676
    
                                                    
                                                
    677
        /// @notice Gets the current upkeep balance for a manager
    678
        /// @param manager Address of the manager
    679
        /// @return balance Current upkeep balance in UP tokens
    680
        function getUpkeepBalance(address manager) external view returns (uint256 balance);
    681
    
                                                    
                                                
    682
        /// @notice Gets the current stake balance for a manager
    683
        /// @param manager Address of the manager
    684
        /// @return balance Current stake balance in UP tokens
    685
        function getStakeBalance(address manager) external view returns (uint256 balance);
    686
    
                                                    
                                                
    687
        /// @notice Gets all authorized callers for a strategy
    688
        /// @param strategy Address of the strategy
    689
        /// @return callers Array of authorized callers
    690
        function getAuthorizedCallers(address strategy) external view returns (address[] memory callers);
    691
    
                                                    
                                                
    692
        /// @notice Gets the main manager for a strategy
    693
        /// @param strategy Address of the strategy
    694
        /// @return manager Address of the main manager
    695
        function getMainManager(address strategy) external view returns (address manager);
    696
    
                                                    
                                                
    697
        /// @notice Checks if an address is the main manager for a strategy
    698
        /// @param manager Address of the manager
    699
        /// @param strategy Address of the strategy
    700
        /// @return isMainManager True if the address is the main manager, false otherwise
    701
        function isMainManager(address manager, address strategy) external view returns (bool isMainManager);
    702
    
                                                    
                                                
    703
        /// @notice Gets all secondary managers for a strategy
    704
        /// @param strategy Address of the strategy
    705
        /// @return secondaryManagers Array of secondary manager addresses
    706
        function getSecondaryManagers(address strategy) external view returns (address[] memory secondaryManagers);
    707
    
                                                    
                                                
    708
        /// @notice Checks if an address is a secondary manager for a strategy
    709
        /// @param manager Address of the manager
    710
        /// @param strategy Address of the strategy
    711
        /// @return isSecondaryManager True if the address is a secondary manager, false otherwise
    712
        function isSecondaryManager(
    713
            address manager,
    714
            address strategy
    715
        )
    716
            external
    717
            view
    718
            returns (bool isSecondaryManager);
    719
    
                                                    
                                                
    720
        /// @dev Internal helper function to check if an address is any kind of manager (primary or secondary)
    721
        /// @param manager Address to check
    722
        /// @param strategy The strategy to check against
    723
        /// @return True if the address is either the primary manager or a secondary manager
    724
        function isAnyManager(address manager, address strategy) external view returns (bool);
    725
    
                                                    
                                                
    726
        /// @notice Gets all created SuperVaults
    727
        /// @return Array of SuperVault addresses
    728
        function getAllSuperVaults() external view returns (address[] memory);
    729
    
                                                    
                                                
    730
        /// @notice Gets a SuperVault by index
    731
        /// @param index The index of the SuperVault
    732
        /// @return The SuperVault address at the given index
    733
        function superVaults(uint256 index) external view returns (address);
    734
    
                                                    
                                                
    735
        /// @notice Gets all created SuperVaultStrategies
    736
        /// @return Array of SuperVaultStrategy addresses
    737
        function getAllSuperVaultStrategies() external view returns (address[] memory);
    738
    
                                                    
                                                
    739
        /// @notice Gets a SuperVaultStrategy by index
    740
        /// @param index The index of the SuperVaultStrategy
    741
        /// @return The SuperVaultStrategy address at the given index
    742
        function superVaultStrategies(uint256 index) external view returns (address);
    743
    
                                                    
                                                
    744
        /// @notice Gets all created SuperVaultEscrows
    745
        /// @return Array of SuperVaultEscrow addresses
    746
        function getAllSuperVaultEscrows() external view returns (address[] memory);
    747
    
                                                    
                                                
    748
        /// @notice Gets a SuperVaultEscrow by index
    749
        /// @param index The index of the SuperVaultEscrow
    750
        /// @return The SuperVaultEscrow address at the given index
    751
        function superVaultEscrows(uint256 index) external view returns (address);
    752
    
                                                    
                                                
    753
        /// @notice Validates a hook against both global and strategy-specific Merkle roots
    754
        /// @param strategy Address of the strategy
    755
        /// @param args Arguments for hook validation
    756
        /// @return isValid True if the hook is valid against either root
    757
        function validateHook(
    758
            address strategy,
    759
            ValidateHookArgs calldata args
    760
        )
    761
            external
    762
            view
    763
            returns (bool isValid);
    764
    
                                                    
                                                
    765
        /// @notice Batch validates multiple hooks against Merkle roots
    766
        /// @param strategy Address of the strategy
    767
        /// @param argsArray Array of hook validation arguments
    768
        /// @return validHooks Array of booleans indicating which hooks are valid
    769
        function validateHooks(
    770
            address strategy,
    771
            ValidateHookArgs[] calldata argsArray
    772
        )
    773
            external
    774
            view
    775
            returns (bool[] memory validHooks);
    776
    
                                                    
                                                
    777
        /// @notice Gets the current global hooks Merkle root
    778
        /// @return root The current global hooks Merkle root
    779
        function getGlobalHooksRoot() external view returns (bytes32 root);
    780
    
                                                    
                                                
    781
        /// @notice Gets the proposed global hooks root and effective time
    782
        /// @return root The proposed global hooks Merkle root
    783
        /// @return effectiveTime The timestamp when the proposed root becomes effective
    784
        function getProposedGlobalHooksRoot() external view returns (bytes32 root, uint256 effectiveTime);
    785
    
                                                    
                                                
    786
        /// @notice Checks if the global hooks root is active (timelock period has passed)
    787
        /// @return isActive True if the global hooks root is active
    788
        function isGlobalHooksRootActive() external view returns (bool);
    789
    
                                                    
                                                
    790
        /// @notice Gets the hooks Merkle root for a specific strategy
    791
        /// @param strategy Address of the strategy
    792
        /// @return root The strategy-specific hooks Merkle root
    793
        function getStrategyHooksRoot(address strategy) external view returns (bytes32 root);
    794
    
                                                    
                                                
    795
        /// @notice Gets the proposed strategy hooks root and effective time
    796
        /// @param strategy Address of the strategy
    797
        /// @return root The proposed strategy hooks Merkle root
    798
        /// @return effectiveTime The timestamp when the proposed root becomes effective
    799
        function getProposedStrategyHooksRoot(address strategy)
    800
            external
    801
            view
    802
            returns (bytes32 root, uint256 effectiveTime);
    803
    }
    804
    
                                                    
                                                
    0.0% src/interfaces/SuperVault/ISuperVaultEscrow.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    /// @title ISuperVaultEscrow
    5
    /// @notice Interface for SuperVault escrow contract that holds shares during request/claim process
    6
    /// @author Superform Labs
    7
    interface ISuperVaultEscrow {
    8
        /*//////////////////////////////////////////////////////////////
    9
                                    ERRORS
    10
        //////////////////////////////////////////////////////////////*/
    11
        error ALREADY_INITIALIZED();
    12
        error UNAUTHORIZED();
    13
        error ZERO_ADDRESS();
    14
    
                                                    
                                                
    15
        /*//////////////////////////////////////////////////////////////
    16
                                INITIALIZATION
    17
        //////////////////////////////////////////////////////////////*/
    18
    
                                                    
                                                
    19
        /// @notice Initialize the escrow with required parameters
    20
        /// @param vaultAddress The vault contract address
    21
        /// @param strategyAddress The strategy contract address
    22
        function initialize(address vaultAddress, address strategyAddress) external;
    23
    
                                                    
                                                
    24
        /*//////////////////////////////////////////////////////////////
    25
                                VAULT FUNCTIONS
    26
        //////////////////////////////////////////////////////////////*/
    27
    
                                                    
                                                
    28
        /// @notice Transfer shares from user to escrow during redeem request
    29
        /// @param from The address to transfer shares from
    30
        /// @param amount The amount of shares to transfer
    31
        function escrowShares(address from, uint256 amount) external;
    32
    
                                                    
                                                
    33
        /// @notice Return shares from escrow to user during redeem cancellation
    34
        /// @param to The address to return shares to
    35
        /// @param amount The amount of shares to return
    36
        function returnShares(address to, uint256 amount) external;
    37
    }
    38
    
                                                    
                                                
    0.0% src/interfaces/SuperVault/ISuperVaultStrategy.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    5
    import { ISuperHook, Execution } from "@superform-v2-core/src/interfaces/ISuperHook.sol";
    6
    
                                                    
                                                
    7
    /// @title ISuperVaultStrategy
    8
    /// @author Superform Labs
    9
    /// @notice Interface for SuperVault strategy implementation that manages yield sources and executes strategies
    10
    interface ISuperVaultStrategy {
    11
        /*//////////////////////////////////////////////////////////////
    12
                                    ERRORS
    13
        //////////////////////////////////////////////////////////////*/
    14
    
                                                    
                                                
    15
        error ZERO_LENGTH();
    16
        error INVALID_HOOK();
    17
        error ZERO_ADDRESS();
    18
        error ACCESS_DENIED();
    19
        error INVALID_AMOUNT();
    20
        error INVALID_MANAGER();
    21
        error OPERATION_FAILED();
    22
        error INVALID_TIMESTAMP();
    23
        error REQUEST_NOT_FOUND();
    24
        error INVALID_HOOK_ROOT();
    25
        error INVALID_HOOK_TYPE();
    26
        error INSUFFICIENT_FUNDS();
    27
        error ZERO_OUTPUT_AMOUNT();
    28
        error INSUFFICIENT_SHARES();
    29
        error ZERO_EXPECTED_VALUE();
    30
        error INVALID_ARRAY_LENGTH();
    31
        error ACTION_TYPE_DISALLOWED();
    32
        error YIELD_SOURCE_NOT_FOUND();
    33
        error INVALID_EMERGENCY_ADMIN();
    34
        error INVALID_PERIPHERY_REGISTRY();
    35
        error YIELD_SOURCE_ALREADY_EXISTS();
    36
        error INVALID_PERFORMANCE_FEE_BPS();
    37
        error INVALID_EMERGENCY_WITHDRAWAL();
    38
        error ASYNC_REQUEST_BLOCKING();
    39
        error MINIMUM_PREVIOUS_HOOK_OUT_AMOUNT_NOT_MET();
    40
        error MINIMUM_OUTPUT_AMOUNT_ASSETS_NOT_MET();
    41
        error INVALID_REDEEM_CLAIM();
    42
        error MANAGER_NOT_AUTHORIZED();
    43
        error PPS_UPDATE_RATE_LIMITED();
    44
        error PPS_OUT_OF_BOUNDS();
    45
        error CALCULATION_BLOCK_TOO_OLD();
    46
        error INVALID_PPS();
    47
        error INVALID_REDEEM_FILL();
    48
        error SLIPPAGE_EXCEEDED();
    49
        error INVALID_VAULT();
    50
        error STAKE_TOO_LOW();
    51
        error OPERATIONS_BLOCKED_BY_VETO();
    52
        error HOOK_VALIDATION_FAILED();
    53
        error STRATEGY_PAUSED();
    54
        error INVALID_MAX_SLIPPAGE_BPS();
    55
        error NO_PROPOSAL();
    56
    
                                                    
                                                
    57
        /*//////////////////////////////////////////////////////////////
    58
                                    EVENTS
    59
        //////////////////////////////////////////////////////////////*/
    60
    
                                                    
                                                
    61
        event SuperGovernorSet(address indexed superGovernor);
    62
        event Initialized(address indexed vault);
    63
        event YieldSourceAdded(address indexed source, address indexed oracle);
    64
        event YieldSourceOracleUpdated(address indexed source, address indexed oldOracle, address indexed newOracle);
    65
        event YieldSourceRemoved(address indexed source);
    66
    
                                                    
                                                
    67
        event HookRootUpdated(bytes32 newRoot);
    68
        event HookRootProposed(bytes32 proposedRoot, uint256 effectiveTime);
    69
        event EmergencyWithdrawableProposed(bool newWithdrawable, uint256 effectiveTime);
    70
        event EmergencyWithdrawableUpdated(bool withdrawable);
    71
        event EmergencyWithdrawableProposalCanceled();
    72
        event EmergencyWithdrawal(address indexed recipient, uint256 assets);
    73
        event VaultFeeConfigUpdated(uint256 performanceFeeBps, uint256 managementFeeBps, address indexed recipient);
    74
        event VaultFeeConfigProposed(
    75
            uint256 performanceFeeBps, uint256 managementFeeBps, address indexed recipient, uint256 effectiveTime
    76
        );
    77
        event MaxPPSSlippageUpdated(uint256 maxSlippageBps);
    78
        event HooksExecuted(address[] hooks);
    79
        event RedeemRequestPlaced(address indexed controller, address indexed owner, uint256 shares);
    80
        event RedeemRequestFulfilled(address indexed controller, address indexed receiver, uint256 assets, uint256 shares);
    81
        event RedeemRequestCanceled(address indexed controller, uint256 shares);
    82
        event HookExecuted(
    83
            address indexed hook,
    84
            address indexed prevHook,
    85
            address indexed targetedYieldSource,
    86
            bool usePrevHookAmount,
    87
            bytes hookCalldata
    88
        );
    89
        event FulfillHookExecuted(address indexed hook, address indexed targetedYieldSource, bytes hookCalldata);
    90
    
                                                    
                                                
    91
        event PPSUpdated(uint256 newPPS, uint256 calculationBlock);
    92
    
                                                    
                                                
    93
        event RedeemRequestsFulfilled(address[] hooks, address[] controllers, uint256 processedShares, uint256 currentPPS);
    94
    
                                                    
                                                
    95
        event FeePaid(address indexed recipient, uint256 amount, uint256 performanceFeeBps);
    96
        event ManagementFeePaid(address indexed controller, address indexed recipient, uint256 feeAssets, uint256 feeBps);
    97
        event DepositHandled(address indexed controller, uint256 assets, uint256 shares);
    98
    
                                                    
                                                
    99
        /*//////////////////////////////////////////////////////////////
    100
                                    STRUCTS
    101
        //////////////////////////////////////////////////////////////*/
    102
    
                                                    
                                                
    103
        struct FeeConfig {
    104
            uint256 performanceFeeBps; // On profit at fulfill time
    105
            uint256 managementFeeBps; // Entry fee on deposit/mint (asset-side)
    106
            address recipient; // Fee sink (entry + performance)
    107
        }
    108
    
                                                    
                                                
    109
        /// @notice Structure for hook execution arguments
    110
        struct ExecuteArgs {
    111
            /// @notice Array of hooks to execute
    112
            address[] hooks;
    113
            /// @notice Calldata for each hook (must match hooks array length)
    114
            bytes[] hookCalldata;
    115
            /// @notice Expected output amounts or output shares
    116
            uint256[] expectedAssetsOrSharesOut;
    117
            /// @notice Global Merkle proofs for hook validation (must match hooks array length)
    118
            bytes32[][] globalProofs;
    119
            /// @notice Strategy-specific Merkle proofs for hook validation (must match hooks array length)
    120
            bytes32[][] strategyProofs;
    121
        }
    122
    
                                                    
                                                
    123
        struct FulfillArgs {
    124
            address[] controllers;
    125
            address[] hooks;
    126
            bytes[] hookCalldata;
    127
            uint256[] expectedAssetsOrSharesOut;
    128
            bytes32[][] globalProofs;
    129
            bytes32[][] strategyProofs;
    130
        }
    131
    
                                                    
                                                
    132
        struct YieldSource {
    133
            address oracle; // Associated yield source oracle address
    134
        }
    135
    
                                                    
                                                
    136
        /// @notice Comprehensive information about a yield source including its address and configuration
    137
        struct YieldSourceInfo {
    138
            address sourceAddress; // Address of the yield source
    139
            address oracle; // Associated yield source oracle address
    140
        }
    141
    
                                                    
                                                
    142
        /// @notice State specific to asynchronous redeem requests
    143
        struct SuperVaultState {
    144
            uint256 pendingRedeemRequest; // Shares requested
    145
            uint256 maxWithdraw; // Assets claimable after fulfillment
    146
            uint256 averageRequestPPS; // Average PPS at the time of redeem request
    147
            // Accumulators needed for fee calculation on redeem
    148
            uint256 accumulatorShares;
    149
            uint256 accumulatorCostBasis;
    150
            uint256 averageWithdrawPrice; // Average price for claimable assets
    151
        }
    152
    
                                                    
                                                
    153
        struct ExecutionVars {
    154
            bool success;
    155
            address targetedYieldSource;
    156
            uint256 outAmount;
    157
            ISuperHook hookContract;
    158
            Execution[] executions;
    159
        }
    160
    
                                                    
                                                
    161
        struct OutflowExecutionVars {
    162
            bool success;
    163
            address targetedYieldSource;
    164
            address svAsset;
    165
            uint256 outAmount;
    166
            uint256 superVaultShares;
    167
            uint256 amountOfAssets;
    168
            uint256 amountConvertedToUnderlyingShares;
    169
            uint256 balanceAssetBefore;
    170
            Execution[] executions;
    171
            ISuperHook hookContract;
    172
            ISuperHook.HookType hookType;
    173
        }
    174
    
                                                    
                                                
    175
        /*//////////////////////////////////////////////////////////////
    176
                                    ENUMS
    177
        //////////////////////////////////////////////////////////////*/
    178
        enum Operation {
    179
            Deposit,
    180
            RedeemRequest,
    181
            CancelRedeem,
    182
            ClaimRedeem,
    183
            Claim,
    184
            UpdateDepositAccumulators
    185
        }
    186
    
                                                    
                                                
    187
        /*//////////////////////////////////////////////////////////////
    188
                            CORE STRATEGY OPERATIONS
    189
        //////////////////////////////////////////////////////////////*/
    190
    
                                                    
                                                
    191
        /// @notice Initializes the strategy with required parameters
    192
        /// @param vaultAddress Address of the associated SuperVault
    193
        /// @param feeConfigData Fee configuration
    194
        function initialize(address vaultAddress, FeeConfig memory feeConfigData) external;
    195
    
                                                    
                                                
    196
        /// @notice Execute a 4626 deposit by processing assets.
    197
        /// @param controller The controller address
    198
        /// @param assetsGross The amount of gross assets user has to deposit
    199
        /// @return sharesNet The amount of net shares to mint
    200
        function handleOperations4626Deposit(
    201
            address controller,
    202
            uint256 assetsGross
    203
        )
    204
            external
    205
            returns (uint256 sharesNet);
    206
    
                                                    
                                                
    207
        /// @notice Execute a 4626 mint by processing shares.
    208
        /// @param controller The controller address
    209
        /// @param sharesNet The amount of shares to mint
    210
        /// @param assetsGross The amount of gross assets user has to deposit
    211
        /// @param assetsNet The amount of net assets that strategy will receive
    212
        function handleOperations4626Mint(
    213
            address controller,
    214
            uint256 sharesNet,
    215
            uint256 assetsGross,
    216
            uint256 assetsNet
    217
        )
    218
            external;
    219
    
                                                    
                                                
    220
        /// @notice Quotes the amount of assets that will be received for a given amount of shares.
    221
        /// @param shares The amount of shares to mint
    222
        /// @return assetsGross The amount of gross assets that will be received
    223
        /// @return assetsNet The amount of net assets that will be received
    224
        function quoteMintAssetsGross(uint256 shares) external view returns (uint256 assetsGross, uint256 assetsNet);
    225
    
                                                    
                                                
    226
        /// @notice Execute async redeem requests (redeem, cancel, claim).
    227
        /// @param op The operation type (RedeemRequest, CancelRedeem, ClaimRedeem)
    228
        /// @param controller The controller address
    229
        /// @param receiver The receiver address
    230
        /// @param amount The amount of assets or shares
    231
        function handleOperations7540(Operation op, address controller, address receiver, uint256 amount) external;
    232
    
                                                    
                                                
    233
        /*//////////////////////////////////////////////////////////////
    234
                    MANAGER EXTERNAL ACCESS FUNCTIONS
    235
        //////////////////////////////////////////////////////////////*/
    236
    
                                                    
                                                
    237
        /// @notice Execute hooks for general strategy management (rebalancing, etc.).
    238
        /// @param args Execution arguments containing hooks, calldata, proofs, expectations.
    239
        function executeHooks(ExecuteArgs calldata args) external payable;
    240
    
                                                    
                                                
    241
        /// @notice Fulfills pending redeem requests by executing specific fulfill hooks.
    242
        /// @param args Execution arguments containing fulfill hooks, calldata, and expected outputs (proofs ignored).
    243
        function fulfillRedeemRequests(FulfillArgs calldata args) external;
    244
    
                                                    
                                                
    245
        /*//////////////////////////////////////////////////////////////
    246
                            YIELD SOURCE MANAGEMENT
    247
        //////////////////////////////////////////////////////////////*/
    248
    
                                                    
                                                
    249
        /// @notice Manage a single yield source: add, update oracle, or remove
    250
        /// @param source Address of the yield source
    251
        /// @param oracle Address of the oracle (used for adding/updating, ignored for removal)
    252
        /// @param actionType Type of action: 0=Add, 1=UpdateOracle, 2=Remove
    253
        function manageYieldSource(address source, address oracle, uint8 actionType) external;
    254
    
                                                    
                                                
    255
        /// @notice Batch manage multiple yield sources in a single transaction
    256
        /// @param sources Array of yield source addresses
    257
        /// @param oracles Array of oracle addresses (used for adding/updating, ignored for removal)
    258
        /// @param actionTypes Array of action types: 0=Add, 1=UpdateOracle, 2=Remove
    259
        function manageYieldSources(
    260
            address[] calldata sources,
    261
            address[] calldata oracles,
    262
            uint8[] calldata actionTypes
    263
        )
    264
            external;
    265
    
                                                    
                                                
    266
        /// @notice Propose or execute a hook root update
    267
        /// @notice Propose changes to vault-specific fee configuration
    268
        /// @param performanceFeeBps New performance fee in basis points
    269
        /// @param managementFeeBps New management fee in basis points
    270
        /// @param recipient New fee recipient
    271
        function proposeVaultFeeConfigUpdate(
    272
            uint256 performanceFeeBps,
    273
            uint256 managementFeeBps,
    274
            address recipient
    275
        )
    276
            external;
    277
    
                                                    
                                                
    278
        /// @notice Execute the proposed vault fee configuration update after timelock
    279
        function executeVaultFeeConfigUpdate() external;
    280
    
                                                    
                                                
    281
        /// @notice Update the maximum allowed PPS slippage for redemptions
    282
        /// @param maxSlippageBps Maximum slippage in basis points (e.g., 100 = 1%)
    283
        function updateMaxPPSSlippage(uint256 maxSlippageBps) external;
    284
    
                                                    
                                                
    285
        /// @notice Manage emergency withdrawals
    286
        /// @param action Type of action: 1=Propose, 2=ExecuteActivation, 3=Withdraw, 4=CancelProposal
    287
        /// @param recipient The recipient of the withdrawn assets (for action 3)
    288
        /// @param amount The amount of assets to withdraw (for action 3)
    289
        function manageEmergencyWithdraw(uint8 action, address recipient, uint256 amount) external;
    290
    
                                                    
                                                
    291
        /*//////////////////////////////////////////////////////////////
    292
                            ACCOUNTING MANAGEMENT
    293
        //////////////////////////////////////////////////////////////*/
    294
        /// @notice Move accumulator shares and cost basis pro-rata during share transfers
    295
        /// @param from The address transferring shares
    296
        /// @param to The address receiving shares
    297
        /// @param shares The amount of shares being transferred
    298
        function moveAccumulatorOnTransfer(address from, address to, uint256 shares) external;
    299
    
                                                    
                                                
    300
        /*//////////////////////////////////////////////////////////////
    301
                                VIEW FUNCTIONS
    302
        //////////////////////////////////////////////////////////////*/
    303
    
                                                    
                                                
    304
        /// @notice Get the vault info
    305
        function getVaultInfo() external view returns (address vault, address asset, uint8 vaultDecimals);
    306
    
                                                    
                                                
    307
        /// @notice Get the fee configurations
    308
        function getConfigInfo() external view returns (FeeConfig memory feeConfig);
    309
    
                                                    
                                                
    310
        /// @notice Returns the currently stored PPS value.
    311
        function getStoredPPS() external view returns (uint256);
    312
    
                                                    
                                                
    313
        /// @notice Get a yield source's configuration
    314
        function getYieldSource(address source) external view returns (YieldSource memory);
    315
    
                                                    
                                                
    316
        /// @notice Get all yield sources with their information
    317
        /// @return Array of YieldSourceInfo structs
    318
        function getYieldSourcesList() external view returns (YieldSourceInfo[] memory);
    319
    
                                                    
                                                
    320
        /// @notice Get all yield source addresses
    321
        /// @return Array of yield source addresses
    322
        function getYieldSources() external view returns (address[] memory);
    323
    
                                                    
                                                
    324
        /// @notice Get the count of yield sources
    325
        /// @return Number of yield sources
    326
        function getYieldSourcesCount() external view returns (uint256);
    327
    
                                                    
                                                
    328
        /// @notice Check if a yield source exists
    329
        /// @param source Address of the yield source
    330
        /// @return True if the yield source exists
    331
        function containsYieldSource(address source) external view returns (bool);
    332
    
                                                    
                                                
    333
        /// @notice Get the average withdraw price for a controller
    334
        /// @param controller The controller address
    335
        /// @return averageWithdrawPrice The average withdraw price
    336
        function getAverageWithdrawPrice(address controller) external view returns (uint256 averageWithdrawPrice);
    337
    
                                                    
                                                
    338
        /// @notice Get the super vault state for a controller
    339
        /// @param controller The controller address
    340
        /// @return state The super vault state
    341
        function getSuperVaultState(address controller) external view returns (SuperVaultState memory state);
    342
    
                                                    
                                                
    343
        /// @notice Previews the fee that would be taken for redeeming a specific amount of shares
    344
        /// @param controller The address of the controller requesting the redemption
    345
        /// @param sharesToRedeem The number of shares to redeem
    346
        /// @return totalFee The estimated fee that would be taken in asset terms
    347
        /// @return superformFee The portion of the fee that would go to Superform treasury
    348
        /// @return recipientFee The portion of the fee that would go to the fee recipient
    349
        function previewPerformanceFee(
    350
            address controller,
    351
            uint256 sharesToRedeem
    352
        )
    353
            external
    354
            view
    355
            returns (uint256 totalFee, uint256 superformFee, uint256 recipientFee);
    356
    
                                                    
                                                
    357
        /// @notice Get the pending redeem request amount (shares) for a controller
    358
        /// @param controller The controller address
    359
        /// @return pendingShares The amount of shares pending redemption
    360
        function pendingRedeemRequest(address controller) external view returns (uint256 pendingShares);
    361
    
                                                    
                                                
    362
        /// @notice Get the claimable withdraw amount (assets) for a controller
    363
        /// @param controller The controller address
    364
        /// @return claimableAssets The amount of assets claimable
    365
        function claimableWithdraw(address controller) external view returns (uint256 claimableAssets);
    366
    }
    367
    
                                                    
                                                
    0.0% src/interfaces/oracles/IECDSAPPSOracle.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    /// @title ECDSAPPSOracle
    5
    /// @author Superform Labs
    6
    /// @notice Interface for PPS oracles that provide price-per-share updates
    7
    /// @dev All PPS oracle implementations must conform to this interface
    8
    interface IECDSAPPSOracle {
    9
        /*//////////////////////////////////////////////////////////////
    10
                                     ERRORS
    11
        //////////////////////////////////////////////////////////////*/
    12
        /// @notice Thrown when the proof is invalid or cannot be verified
    13
        error INVALID_PROOF();
    14
        /// @notice Thrown when a validator is not registered or authorized
    15
        error INVALID_VALIDATOR();
    16
        /// @notice Thrown when the quorum of validators is not met
    17
        error QUORUM_NOT_MET();
    18
        /// @notice Thrown when the input arrays have different lengths
    19
        error ARRAY_LENGTH_MISMATCH();
    20
        /// @notice Thrown when the input array is empty
    21
        error ZERO_LENGTH_ARRAY();
    22
        /// @notice Thrown when the timestamp in the proof is invalid
    23
        error INVALID_TIMESTAMP();
    24
        /// @notice Thrown when the strategy address in the proof does not match
    25
        error STRATEGY_MISMATCH();
    26
        /// @notice Thrown when the pps value in the proof does not match
    27
        error PPS_MISMATCH();
    28
        /// @notice Thrown when the oracle is not set as the active PPS Oracle in SuperGovernor
    29
        error NOT_ACTIVE_PPS_ORACLE();
    30
        /// @notice Thrown when the dispersion (standard deviation / mean) is too high
    31
        error HIGH_PPS_DISPERSION();
    32
        /// @notice Thrown when the deviation from previous PPS is too high
    33
        error HIGH_PPS_DEVIATION();
    34
        /// @notice Thrown when too few validators participated in the round
    35
        error INSUFFICIENT_VALIDATOR_PARTICIPATION();
    36
        /// @notice Thrown when the reported validator count doesn't match the actual number of valid signatures
    37
        error VALIDATOR_COUNT_MISMATCH();
    38
        /// @notice Thrown when the validatorSet doesn't match the actual number of valid signatures  
    39
        error INVALID_VALIDATOR_SET();
    40
        /// @notice Thrown when the totalValidators doesn't match the actual total number of validators
    41
        error INVALID_TOTAL_VALIDATORS();
    42
    
                                                    
                                                
    43
        /*//////////////////////////////////////////////////////////////
    44
                                     EVENTS
    45
        //////////////////////////////////////////////////////////////*/
    46
        /// @notice Emitted when a PPS update is validated and forwarded
    47
        /// @param strategy Address of the strategy
    48
        /// @param pps The validated price-per-share value
    49
        /// @param ppsStdev The standard deviation of the price-per-share
    50
        /// @param validatorSet Number of validators who calculated the PPS
    51
        /// @param totalValidators Total number of validators in the network
    52
        /// @param timestamp Timestamp when the value was generated
    53
        /// @param sender Address that submitted the update
    54
        event PPSValidated(
    55
            address indexed strategy,
    56
            uint256 pps,
    57
            uint256 ppsStdev,
    58
            uint256 validatorSet,
    59
            uint256 totalValidators,
    60
            uint256 timestamp,
    61
            address indexed sender
    62
        );
    63
    
                                                    
                                                
    64
        /// @notice Emitted when proof validation failed
    65
        /// @param strategy Address of the strategy
    66
        /// @param reason Revert reason
    67
        event ProofValidationFailed(address indexed strategy, string reason);
    68
    
                                                    
                                                
    69
        /// @notice Emitted when proof validation failed
    70
        /// @param strategy Address of the strategy
    71
        /// @param data Revert encoded data
    72
        event ProofValidationFailedLowLevel(address indexed strategy, bytes data);
    73
    
                                                    
                                                
    74
        /// @notice Emitted when batch forward PPS failed
    75
        /// @param reason Revert reason
    76
        event BatchForwardPPSFailed(string reason);
    77
    
                                                    
                                                
    78
        /// @notice Emitted when batch forward PPS failed
    79
        /// @param lowLevelData Revert encoded data
    80
        event BatchForwardPPSFailedLowLevel(bytes lowLevelData);
    81
    
                                                    
                                                
    82
        /*//////////////////////////////////////////////////////////////
    83
                                STRUCTS
    84
        //////////////////////////////////////////////////////////////*/
    85
        /// @notice Parameters for validating PPS proofs
    86
        /// @param strategy Address of the strategy
    87
        /// @param proofs Array of cryptographic proofs
    88
        /// @param pps Price-per-share value (mean)
    89
        /// @param ppsStdev Standard deviation of the price-per-share
    90
        /// @param validatorSet Number of validators who calculated this PPS
    91
        /// @param totalValidators Total number of validators in the network
    92
        /// @param timestamp Timestamp when the value was generated
    93
        struct ValidationParams {
    94
            address strategy;
    95
            bytes[] proofs;
    96
            uint256 pps;
    97
            uint256 ppsStdev;
    98
            uint256 validatorSet;
    99
            uint256 totalValidators;
    100
            uint256 timestamp;
    101
        }
    102
    
                                                    
                                                
    103
        /// @notice Arguments for batch updating PPS for multiple strategies
    104
        /// @param strategies Array of strategy addresses
    105
        /// @param proofsArray Array of arrays of cryptographic proofs (one array of proofs per strategy)
    106
        /// @param ppss Array of price-per-share values (means)
    107
        /// @param ppsStdevs Array of standard deviations of price-per-share values
    108
        /// @param validatorSets Array of numbers of validators who calculated each PPS
    109
        /// @param totalValidators Array of total number of validators in the network for each update
    110
        /// @param timestamps The time and therefore the blockchain(s) state(s) (plural important) this PPS refers to
    111
        struct UpdatePPSArgs {
    112
            address[] strategies;
    113
            bytes[][] proofsArray;
    114
            uint256[] ppss;
    115
            uint256[] ppsStdevs;
    116
            uint256[] validatorSets;
    117
            uint256[] totalValidators;
    118
            uint256[] timestamps;
    119
        }
    120
    
                                                    
                                                
    121
        /*//////////////////////////////////////////////////////////////
    122
                                  VIEW FUNCTIONS
    123
        //////////////////////////////////////////////////////////////*/
    124
        /// @notice Returns the current nonce
    125
        /// @param strategy_ Address of the strategy
    126
        /// @return The current nonce
    127
        function noncePerStrategy(address strategy_) external view returns (uint256);
    128
    
                                                    
                                                
    129
        /// @notice Returns the domain separator for the contract
    130
        /// @return The domain separator
    131
        function domainSeparator() external view returns (bytes32);
    132
    
                                                    
                                                
    133
        /// @notice Returns the signature typehash
    134
        /// @return The typehash
    135
        function UPDATE_PPS_TYPEHASH() external view returns (bytes32);
    136
    
                                                    
                                                
    137
        /// @notice Validates an array of proofs for a strategy's PPS update
    138
        /// @param params Validation parameters
    139
        /// @dev Reverts immediately if duplicate signers are found or quorum is not met
    140
        function validateProofs(IECDSAPPSOracle.ValidationParams memory params) external view;
    141
    
                                                    
                                                
    142
        /*//////////////////////////////////////////////////////////////
    143
                                EXTERNAL FUNCTIONS
    144
        //////////////////////////////////////////////////////////////*/
    145
        /// @notice Updates the PPS for multiple strategies in a batch
    146
        /// @param args Struct containing all parameters for batch PPS update
    147
        function updatePPS(UpdatePPSArgs calldata args) external;
    148
    }
    149
    
                                                    
                                                
    0.0% src/interfaces/oracles/ISuperOracle.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    /// @title ISuperOracle
    5
    /// @author Superform Labs
    6
    /// @notice Interface for SuperOracle
    7
    interface ISuperOracle {
    8
        /*//////////////////////////////////////////////////////////////
    9
                                    ERRORS
    10
        //////////////////////////////////////////////////////////////*/
    11
        /// @notice Error when address is zero
    12
        error ZERO_ADDRESS();
    13
    
                                                    
                                                
    14
        /// @notice Error when array length is zero
    15
        error ZERO_ARRAY_LENGTH();
    16
    
                                                    
                                                
    17
        /// @notice Error when oracle provider index is invalid
    18
        error INVALID_ORACLE_PROVIDER();
    19
    
                                                    
                                                
    20
        /// @notice Error when no oracles are configured for base asset
    21
        error NO_ORACLES_CONFIGURED();
    22
    
                                                    
                                                
    23
        /// @notice Error when no valid reported prices are found
    24
        error NO_VALID_REPORTED_PRICES();
    25
    
                                                    
                                                
    26
        /// @notice Error when caller is not admin
    27
        error NOT_ADMIN();
    28
    
                                                    
                                                
    29
        /// @notice Error when arrays have mismatched lengths
    30
        error ARRAY_LENGTH_MISMATCH();
    31
    
                                                    
                                                
    32
        /// @notice Error when timelock period has not elapsed
    33
        error TIMELOCK_NOT_ELAPSED();
    34
    
                                                    
                                                
    35
        /// @notice Error when there is already a pending update
    36
        error PENDING_UPDATE_EXISTS();
    37
    
                                                    
                                                
    38
        /// @notice Error when oracle data is untrusted
    39
        error ORACLE_UNTRUSTED_DATA();
    40
    
                                                    
                                                
    41
        /// @notice Error when provider max staleness period is not set
    42
        error NO_PENDING_UPDATE();
    43
    
                                                    
                                                
    44
        /// @notice Error when quote is not supported (only USD is supported)
    45
        error UNSUPPORTED_QUOTE();
    46
    
                                                    
                                                
    47
        /// @notice Error when provider max staleness period is exceeded
    48
        error MAX_STALENESS_EXCEEDED();
    49
    
                                                    
                                                
    50
        /// @notice Error when no prices are reported
    51
        error NO_PRICES();
    52
    
                                                    
                                                
    53
        /// @notice Error when average provider is not allowed
    54
        error AVERAGE_PROVIDER_NOT_ALLOWED();
    55
    
                                                    
                                                
    56
        /// @notice Error when provider is zero
    57
        error ZERO_PROVIDER();
    58
    
                                                    
                                                
    59
        /// @notice Error when caller is not authorized to update
    60
        error UNAUTHORIZED_UPDATE_AUTHORITY();
    61
    
                                                    
                                                
    62
        /// @notice Error when oracle decimals call fails
    63
        error ORACLE_DECIMALS_CALL_FAIL(address oracle);
    64
    
                                                    
                                                
    65
        /// @notice Error when oracle round data call fails 
    66
        error ORACLE_ROUND_DATA_CALL_FAIL(address oracle);
    67
    
                                                    
                                                
    68
        /*//////////////////////////////////////////////////////////////
    69
                                    EVENTS
    70
        //////////////////////////////////////////////////////////////*/
    71
        /// @notice Emitted when oracles are configured
    72
        /// @param bases Array of base assets
    73
        /// @param providers Array of provider indexes
    74
        /// @param feeds Array of oracle addresses
    75
        event OraclesConfigured(address[] bases, address[] quotes, bytes32[] providers, address[] feeds);
    76
    
                                                    
                                                
    77
        /// @notice Emitted when oracle update is queued
    78
        /// @param bases Array of base assets
    79
        /// @param providers Array of provider indexes
    80
        /// @param feeds Array of oracle addresses
    81
        /// @param timestamp Timestamp when update was queued
    82
        event OracleUpdateQueued(
    83
            address[] bases, address[] quotes, bytes32[] providers, address[] feeds, uint256 timestamp
    84
        );
    85
    
                                                    
                                                
    86
        /// @notice Emitted when oracle update is executed
    87
        /// @param bases Array of base assets
    88
        /// @param providers Array of provider indexes
    89
        /// @param feeds Array of oracle addresses
    90
        event OracleUpdateExecuted(address[] bases, address[] quotes, bytes32[] providers, address[] feeds);
    91
    
                                                    
                                                
    92
        /// @notice Emitted when provider max staleness period is updated
    93
        /// @param feed Feed address
    94
        /// @param newMaxStaleness New maximum staleness period in seconds
    95
        event FeedMaxStalenessUpdated(address feed, uint256 newMaxStaleness);
    96
    
                                                    
                                                
    97
        /// @notice Emitted when max staleness period is updated
    98
        /// @param newMaxStaleness New maximum staleness period in seconds
    99
        event MaxStalenessUpdated(uint256 newMaxStaleness);
    100
    
                                                    
                                                
    101
        /// @notice Emitted when provider removal is queued
    102
        /// @param providers Array of provider ids to remove
    103
        /// @param timestamp Timestamp when removal was queued
    104
        event ProviderRemovalQueued(bytes32[] providers, uint256 timestamp);
    105
    
                                                    
                                                
    106
        /// @notice Emitted when provider removal is executed
    107
        /// @param providers Array of provider ids that were removed
    108
        event ProviderRemovalExecuted(bytes32[] providers);
    109
    
                                                    
                                                
    110
        /// @notice Emitted when emergency price is updated
    111
        /// @param token Token address
    112
        /// @param price Emergency price
    113
        event EmergencyPriceUpdated(address token, uint256 price);
    114
    
                                                    
                                                
    115
        /*//////////////////////////////////////////////////////////////
    116
                                    STRUCTS
    117
        //////////////////////////////////////////////////////////////*/
    118
        /// @notice Struct for pending oracle update
    119
        struct PendingUpdate {
    120
            address[] bases;
    121
            address[] quotes;
    122
            bytes32[] providers;
    123
            address[] feeds;
    124
            uint256 timestamp;
    125
        }
    126
    
                                                    
                                                
    127
        /// @notice Struct for pending provider removal
    128
        struct PendingRemoval {
    129
            bytes32[] providers;
    130
            uint256 timestamp;
    131
        }
    132
    
                                                    
                                                
    133
        /*//////////////////////////////////////////////////////////////
    134
                                EXTERNAL FUNCTIONS
    135
        //////////////////////////////////////////////////////////////*/
    136
    
                                                    
                                                
    137
        /// @notice Get oracle address for a base asset and provider
    138
        /// @param base Base asset address
    139
        /// @param quote Quote asset address
    140
        /// @param provider Provider id
    141
        /// @return oracle Oracle address
    142
        function getOracleAddress(address base, address quote, bytes32 provider) external view returns (address oracle);
    143
    
                                                    
                                                
    144
        /// @notice Get quote from specified oracle provider
    145
        /// @param baseAmount Amount of base asset
    146
        /// @param base Base asset address
    147
        /// @param quote Quote asset address
    148
        /// @param oracleProvider Id of oracle provider to use
    149
        /// @return quoteAmount The quote amount
    150
        function getQuoteFromProvider(
    151
            uint256 baseAmount,
    152
            address base,
    153
            address quote,
    154
            bytes32 oracleProvider
    155
        )
    156
            external
    157
            view
    158
            returns (uint256 quoteAmount, uint256 deviation, uint256 totalProviders, uint256 availableProviders);
    159
    
                                                    
                                                
    160
        /// @notice Queue oracle update for timelock
    161
        /// @param bases Array of base assets
    162
        /// @param providers Array of provider ids
    163
        /// @param quotes Array of quote assets
    164
        /// @param feeds Array of oracle addresses
    165
        function queueOracleUpdate(
    166
            address[] calldata bases,
    167
            address[] calldata quotes,
    168
            bytes32[] calldata providers,
    169
            address[] calldata feeds
    170
        )
    171
            external;
    172
    
                                                    
                                                
    173
        /// @notice Execute queued oracle update after timelock period
    174
        function executeOracleUpdate() external;
    175
    
                                                    
                                                
    176
        /// @notice Queue provider removal for timelock
    177
        /// @param providers Array of provider ids to remove
    178
        function queueProviderRemoval(bytes32[] calldata providers) external;
    179
    
                                                    
                                                
    180
        /// @notice Execute queued provider removal after timelock period
    181
        function executeProviderRemoval() external;
    182
    
                                                    
                                                
    183
        /// @notice Set the maximum staleness period for a specific provider
    184
        /// @param feed Feed address
    185
        /// @param newMaxStaleness New maximum staleness period in seconds
    186
        function setFeedMaxStaleness(address feed, uint256 newMaxStaleness) external;
    187
    
                                                    
                                                
    188
        /// @notice Set the maximum staleness period for all providers
    189
        /// @param newMaxStaleness New maximum staleness period in seconds
    190
        function setMaxStaleness(uint256 newMaxStaleness) external;
    191
    
                                                    
                                                
    192
        /// @notice Set the maximum staleness period for multiple providers
    193
        /// @param feeds Array of feed addresses
    194
        /// @param newMaxStalenessList Array of new maximum staleness periods in seconds
    195
        function setFeedMaxStalenessBatch(address[] calldata feeds, uint256[] calldata newMaxStalenessList) external;
    196
    
                                                    
                                                
    197
        /// @notice Get all active provider ids
    198
        /// @return Array of active provider ids
    199
        function getActiveProviders() external view returns (bytes32[] memory);
    200
    
                                                    
                                                
    201
        /// @notice Get the emergency price for a token
    202
        /// @param token Token address
    203
        /// @return Emergency price
    204
        function getEmergencyPrice(address token) external view returns (uint256);
    205
    
                                                    
                                                
    206
        /// @notice Set the emergency price for a token
    207
        /// @param token Token address
    208
        /// @param price Emergency price
    209
        function setEmergencyPrice(address token, uint256 price) external;
    210
    
                                                    
                                                
    211
        /// @notice Set the emergency price for multiple tokens in a batch
    212
        /// @param tokens Array of token addresses
    213
        /// @param prices Array of emergency prices
    214
        function batchSetEmergencyPrice(address[] calldata tokens, uint256[] calldata prices) external;
    215
    }
    216
    
                                                    
                                                
    0.0% src/interfaces/oracles/ISuperOracleL2.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    /// @title ISuperOracleL2
    5
    /// @author Superform Labs
    6
    /// @notice Interface for Layer 2 Oracle for Superform
    7
    interface ISuperOracleL2 {
    8
        /*//////////////////////////////////////////////////////////////
    9
                                ERRORS
    10
        //////////////////////////////////////////////////////////////*/
    11
        /// @notice Error when no uptime feed is configured for the data oracle
    12
        error NO_UPTIME_FEED();
    13
    
                                                    
                                                
    14
        /// @notice Error when the L2 sequencer is down
    15
        error SEQUENCER_DOWN();
    16
    
                                                    
                                                
    17
        /// @notice Error when the grace period after sequencer restart is not over
    18
        error GRACE_PERIOD_NOT_OVER();
    19
    
                                                    
                                                
    20
        /*//////////////////////////////////////////////////////////////
    21
                                EVENTS
    22
        //////////////////////////////////////////////////////////////*/
    23
        /// @notice Emitted when an uptime feed is set for a data oracle
    24
        /// @param dataOracle The data oracle address
    25
        /// @param uptimeOracle The uptime feed address
    26
        event UptimeFeedSet(address dataOracle, address uptimeOracle);
    27
    
                                                    
                                                
    28
        /// @notice Emitted when a grace period is set for an uptime oracle
    29
        /// @param uptimeOracle The uptime oracle address
    30
        /// @param gracePeriod The grace period in seconds
    31
        event GracePeriodSet(address uptimeOracle, uint256 gracePeriod);
    32
    
                                                    
                                                
    33
        /*//////////////////////////////////////////////////////////////
    34
                                EXTERNAL FUNCTIONS
    35
        //////////////////////////////////////////////////////////////*/
    36
        /// @notice Set uptime feeds for multiple data oracles in batch
    37
        /// @param dataOracles Array of data oracle addresses to set uptime feeds for
    38
        /// @param uptimeOracles Array of uptime feed addresses to set
    39
        /// @param gracePeriods Array of grace periods in seconds after sequencer restart
    40
        function batchSetUptimeFeed(
    41
            address[] calldata dataOracles,
    42
            address[] calldata uptimeOracles,
    43
            uint256[] calldata gracePeriods
    44
        ) external;
    45
    }
    46
    
                                                    
                                                
    100.0% src/libraries/AssetMetadataLib.sol
    Lines covered: 10 / 10 (100.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    import { IERC20Metadata } from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
    5
    
                                                    
                                                
    6
    /// @title AssetMetadataLib
    7
    /// @author Superform Labs
    8
    /// @notice Library for handling ERC20 metadata operations
    9
    ✓ 23.0K
    ⟲ 23.0K
    library AssetMetadataLib {
    10
        error INVALID_ASSET();
    11
    
                                                    
                                                
    12
        /**
    13
         * @notice Attempts to fetch an asset's decimals
    14
         * @dev A return value of false indicates that the attempt failed in some way
    15
         * @param asset_ The address of the token to query
    16
         * @return ok Boolean indicating if the operation was successful
    17
         * @return assetDecimals The token's decimals if successful, 0 otherwise
    18
         */
    19
    ✓ 226.3K
        function tryGetAssetDecimals(address asset_) internal view returns (bool ok, uint8 assetDecimals) {
    20
    ✓ 226.3K
    ⟲ 26.0K
            if(asset_.code.length == 0) revert INVALID_ASSET();
    21
            
    22
    ✓ 200.4K
            (bool success, bytes memory encodedDecimals) =
    23
    ✓ 200.4K
                address(asset_).staticcall(abi.encodeCall(IERC20Metadata.decimals, ()));
    24
    ✓ 200.4K
            if (success && encodedDecimals.length >= 32) {
    25
    ✓ 114.7K
                uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
    26
    ✓ 114.7K
                if (returnedDecimals <= type(uint8).max) {
    27
    ✓ 114.7K
                    return (true, uint8(returnedDecimals));
    28
                }
    29
            }
    30
    ✓ 85.6K
            return (false, 0);
    31
        }
    32
    }
    33
    
                                                    
                                                
    0.0% src/vendor/standards/ERC7540/IERC7540Vault.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: AGPL-3.0-only
    2
    pragma solidity >=0.8.0;
    3
    
                                                    
                                                
    4
    import { IERC7741 } from "../ERC7741/IERC7741.sol";
    5
    
                                                    
                                                
    6
    interface IERC7540Operator {
    7
        /**
    8
         * @dev The event emitted when an operator is set.
    9
         *
    10
         * @param controller The address of the controller.
    11
         * @param operator The address of the operator.
    12
         * @param approved The approval status.
    13
         */
    14
        event OperatorSet(address indexed controller, address indexed operator, bool approved);
    15
    
                                                    
                                                
    16
        /**
    17
         * @dev Sets or removes an operator for the caller.
    18
         *
    19
         * @param operator The address of the operator.
    20
         * @param approved The approval status.
    21
         * @return Whether the call was executed successfully or not
    22
         */
    23
        function setOperator(address operator, bool approved) external returns (bool);
    24
    
                                                    
                                                
    25
        /**
    26
         * @dev Returns `true` if the `operator` is approved as an operator for an `controller`.
    27
         *
    28
         * @param controller The address of the controller.
    29
         * @param operator The address of the operator.
    30
         * @return status The approval status
    31
         */
    32
        function isOperator(address controller, address operator) external view returns (bool status);
    33
    }
    34
    
                                                    
                                                
    35
    interface IERC7540Deposit is IERC7540Operator {
    36
        event DepositRequest(
    37
            address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    38
        );
    39
        /**
    40
         * @dev Transfers assets from sender into the Vault and submits a Request for asynchronous deposit.
    41
         *
    42
         * - MUST support ERC-20 approve / transferFrom on asset as a deposit Request flow.
    43
         * - MUST revert if all of assets cannot be requested for deposit.
    44
         * - owner MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    45
         *    approval of ERC-20 tokens from owner to sender is NOT enough.
    46
         *
    47
         * @param assets the amount of deposit assets to transfer from owner
    48
         * @param controller the controller of the request who will be able to operate the request
    49
         * @param owner the source of the deposit assets
    50
         *
    51
         * NOTE: most implementations will require pre-approval of the Vault with the Vault's underlying asset token.
    52
         */
    53
    
                                                    
                                                
    54
        function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256 requestId);
    55
    
                                                    
                                                
    56
        /**
    57
         * @dev Returns the amount of requested assets in Pending state.
    58
         *
    59
         * - MUST NOT include any assets in Claimable state for deposit or mint.
    60
         * - MUST NOT show any variations depending on the caller.
    61
         * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
    62
         */
    63
        function pendingDepositRequest(
    64
            uint256 requestId,
    65
            address controller
    66
        )
    67
            external
    68
            view
    69
            returns (uint256 pendingAssets);
    70
    
                                                    
                                                
    71
        /**
    72
         * @dev Returns the amount of requested assets in Claimable state for the controller to deposit or mint.
    73
         *
    74
         * - MUST NOT include any assets in Pending state.
    75
         * - MUST NOT show any variations depending on the caller.
    76
         * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
    77
         */
    78
        function claimableDepositRequest(
    79
            uint256 requestId,
    80
            address controller
    81
        )
    82
            external
    83
            view
    84
            returns (uint256 claimableAssets);
    85
    
                                                    
                                                
    86
        /**
    87
         * @dev Mints shares Vault shares to receiver by claiming the Request of the controller.
    88
         *
    89
         * - MUST emit the Deposit event.
    90
         * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator.
    91
         */
    92
        function deposit(uint256 assets, address receiver, address controller) external returns (uint256 shares);
    93
    
                                                    
                                                
    94
        /**
    95
         * @dev Mints exactly shares Vault shares to receiver by claiming the Request of the controller.
    96
         *
    97
         * - MUST emit the Deposit event.
    98
         * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator.
    99
         */
    100
        function mint(uint256 shares, address receiver, address controller) external returns (uint256 assets);
    101
    }
    102
    
                                                    
                                                
    103
    interface IERC7540Redeem is IERC7540Operator {
    104
        event RedeemRequest(
    105
            address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    106
        );
    107
    
                                                    
                                                
    108
        /**
    109
         * @dev Assumes control of shares from sender into the Vault and submits a Request for asynchronous redeem.
    110
         *
    111
         * - MUST support a redeem Request flow where the control of shares is taken from sender directly
    112
         *   where msg.sender has ERC-20 approval over the shares of owner.
    113
         * - MUST revert if all of shares cannot be requested for redeem.
    114
         *
    115
         * @param shares the amount of shares to be redeemed to transfer from owner
    116
         * @param controller the controller of the request who will be able to operate the request
    117
         * @param owner the source of the shares to be redeemed
    118
         *
    119
         * NOTE: most implementations will require pre-approval of the Vault with the Vault's share token.
    120
         */
    121
        function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256 requestId);
    122
    
                                                    
                                                
    123
        /**
    124
         * @dev Returns the amount of requested shares in Pending state.
    125
         *
    126
         * - MUST NOT include any shares in Claimable state for redeem or withdraw.
    127
         * - MUST NOT show any variations depending on the caller.
    128
         * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
    129
         */
    130
        function pendingRedeemRequest(
    131
            uint256 requestId,
    132
            address controller
    133
        )
    134
            external
    135
            view
    136
            returns (uint256 pendingShares);
    137
    
                                                    
                                                
    138
        /**
    139
         * @dev Returns the amount of requested shares in Claimable state for the controller to redeem or withdraw.
    140
         *
    141
         * - MUST NOT include any shares in Pending state for redeem or withdraw.
    142
         * - MUST NOT show any variations depending on the caller.
    143
         * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
    144
         */
    145
        function claimableRedeemRequest(
    146
            uint256 requestId,
    147
            address controller
    148
        )
    149
            external
    150
            view
    151
            returns (uint256 claimableShares);
    152
    }
    153
    
                                                    
                                                
    154
    interface IERC7540CancelDeposit {
    155
        event CancelDepositRequest(address indexed controller, uint256 indexed requestId, address sender);
    156
        event CancelDepositClaim(
    157
            address indexed receiver, address indexed controller, uint256 indexed requestId, address sender, uint256 assets
    158
        );
    159
    
                                                    
                                                
    160
        /**
    161
         * @dev Submits a Request for cancelling the pending deposit Request
    162
         *
    163
         * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    164
         *    approval of ERC-20 tokens from controller to sender is NOT enough.
    165
         * - MUST set pendingCancelDepositRequest to `true` for the returned requestId after request
    166
         * - MUST increase claimableCancelDepositRequest for the returned requestId after fulfillment
    167
         * - SHOULD be claimable using `claimCancelDepositRequest`
    168
         * Note: while `pendingCancelDepositRequest` is `true`, `requestDeposit` cannot be called
    169
         */
    170
        function cancelDepositRequest(uint256 requestId, address controller) external;
    171
    
                                                    
                                                
    172
        /**
    173
         * @dev Returns whether the deposit Request is pending cancelation
    174
         *
    175
         * - MUST NOT show any variations depending on the caller.
    176
         */
    177
        function pendingCancelDepositRequest(
    178
            uint256 requestId,
    179
            address controller
    180
        )
    181
            external
    182
            view
    183
            returns (bool isPending);
    184
    
                                                    
                                                
    185
        /**
    186
         * @dev Returns the amount of assets that were canceled from a deposit Request, and can now be claimed.
    187
         *
    188
         * - MUST NOT show any variations depending on the caller.
    189
         */
    190
        function claimableCancelDepositRequest(
    191
            uint256 requestId,
    192
            address controller
    193
        )
    194
            external
    195
            view
    196
            returns (uint256 claimableAssets);
    197
    
                                                    
                                                
    198
        /**
    199
         * @dev Claims the canceled deposit assets, and removes the pending cancelation Request
    200
         *
    201
         * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    202
         *    approval of ERC-20 tokens from controller to sender is NOT enough.
    203
         * - MUST set pendingCancelDepositRequest to `false` for the returned requestId after request
    204
         * - MUST set claimableCancelDepositRequest to 0 for the returned requestId after fulfillment
    205
         */
    206
        function claimCancelDepositRequest(
    207
            uint256 requestId,
    208
            address receiver,
    209
            address controller
    210
        )
    211
            external
    212
            returns (uint256 assets);
    213
    }
    214
    
                                                    
                                                
    215
    interface IERC7540CancelRedeem {
    216
        event CancelRedeemRequest(address indexed controller, uint256 indexed requestId, address sender);
    217
        event CancelRedeemClaim(
    218
            address indexed receiver, address indexed controller, uint256 indexed requestId, address sender, uint256 shares
    219
        );
    220
    
                                                    
                                                
    221
        /**
    222
         * @dev Submits a Request for cancelling the pending redeem Request
    223
         *
    224
         * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    225
         *    approval of ERC-20 tokens from controller to sender is NOT enough.
    226
         * - MUST set pendingCancelRedeemRequest to `true` for the returned requestId after request
    227
         * - MUST increase claimableCancelRedeemRequest for the returned requestId after fulfillment
    228
         * - SHOULD be claimable using `claimCancelRedeemRequest`
    229
         * Note: while `pendingCancelRedeemRequest` is `true`, `requestRedeem` cannot be called
    230
         */
    231
        function cancelRedeemRequest(uint256 requestId, address controller) external;
    232
    
                                                    
                                                
    233
        /**
    234
         * @dev Returns whether the redeem Request is pending cancelation
    235
         *
    236
         * - MUST NOT show any variations depending on the caller.
    237
         */
    238
        function pendingCancelRedeemRequest(uint256 requestId, address controller) external view returns (bool isPending);
    239
    
                                                    
                                                
    240
        /**
    241
         * @dev Returns the amount of shares that were canceled from a redeem Request, and can now be claimed.
    242
         *
    243
         * - MUST NOT show any variations depending on the caller.
    244
         */
    245
        function claimableCancelRedeemRequest(
    246
            uint256 requestId,
    247
            address controller
    248
        )
    249
            external
    250
            view
    251
            returns (uint256 claimableShares);
    252
    
                                                    
                                                
    253
        /**
    254
         * @dev Claims the canceled redeem shares, and removes the pending cancelation Request
    255
         *
    256
         * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
    257
         *    approval of ERC-20 tokens from controller to sender is NOT enough.
    258
         * - MUST set pendingCancelRedeemRequest to `false` for the returned requestId after request
    259
         * - MUST set claimableCancelRedeemRequest to 0 for the returned requestId after fulfillment
    260
         */
    261
        function claimCancelRedeemRequest(
    262
            uint256 requestId,
    263
            address receiver,
    264
            address controller
    265
        )
    266
            external
    267
            returns (uint256 shares);
    268
    }
    269
    
                                                    
                                                
    270
    /**
    271
     * @title  IERC7540
    272
     * @dev    Fully async ERC7540 implementation according to the standard
    273
     * @dev    Adapted from Centrifuge's IERC7540 implementation
    274
     */
    275
    interface IERC7540 is IERC7540Deposit, IERC7540Redeem { }
    276
    
                                                    
                                                
    277
    /**
    278
     * @title  IERC7540Vault
    279
     * @dev    This is the specific set of interfaces used by the SuperVaults
    280
     */
    281
    interface IERC7540Vault is IERC7540, IERC7741 {
    282
        event DepositClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
    283
        event RedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
    284
    }
    285
    
                                                    
                                                
    0.0% src/vendor/standards/ERC7575/IERC7575.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: AGPL-3.0-only
    2
    pragma solidity >=0.8.0;
    3
    
                                                    
                                                
    4
    /**
    5
     * @dev Interface of the ERC165 standard, as defined in the
    6
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
    7
     *
    8
     * Implementers can declare support of contract interfaces, which can then be
    9
     * queried by others.
    10
     */
    11
    interface IERC165 {
    12
        /**
    13
         * @dev Returns true if this contract implements the interface defined by
    14
         * `interfaceId`. See the corresponding
    15
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
    16
         * to learn more about how these ids are created.
    17
         *
    18
         * This function call must use less than 30 000 gas.
    19
         */
    20
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    21
    }
    22
    
                                                    
                                                
    23
    interface IERC7575 is IERC165 {
    24
        event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
    25
        event Withdraw(
    26
            address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
    27
        );
    28
    
                                                    
                                                
    29
        /**
    30
         * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
    31
         *
    32
         * - MUST be an ERC-20 token contract.
    33
         * - MUST NOT revert.
    34
         */
    35
        function asset() external view returns (address assetTokenAddress);
    36
    
                                                    
                                                
    37
        /**
    38
         * @dev Returns the address of the share token
    39
         *
    40
         * - MUST be an ERC-20 token contract.
    41
         * - MUST NOT revert.
    42
         */
    43
        function share() external view returns (address shareTokenAddress);
    44
    
                                                    
                                                
    45
        /**
    46
         * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
    47
         * scenario where all the conditions are met.
    48
         *
    49
         * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    50
         * - MUST NOT show any variations depending on the caller.
    51
         * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    52
         * - MUST NOT revert.
    53
         *
    54
         * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    55
         * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    56
         * from.
    57
         */
    58
        function convertToShares(uint256 assets) external view returns (uint256 shares);
    59
    
                                                    
                                                
    60
        /**
    61
         * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
    62
         * scenario where all the conditions are met.
    63
         *
    64
         * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    65
         * - MUST NOT show any variations depending on the caller.
    66
         * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    67
         * - MUST NOT revert.
    68
         *
    69
         * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    70
         * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    71
         * from.
    72
         */
    73
        function convertToAssets(uint256 shares) external view returns (uint256 assets);
    74
    
                                                    
                                                
    75
        /**
    76
         * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
    77
         *
    78
         * - SHOULD include any compounding that occurs from yield.
    79
         * - MUST be inclusive of any fees that are charged against assets in the Vault.
    80
         * - MUST NOT revert.
    81
         */
    82
        function totalAssets() external view returns (uint256 totalManagedAssets);
    83
    
                                                    
                                                
    84
        /**
    85
         * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
    86
         * through a deposit call.
    87
         *
    88
         * - MUST return a limited value if receiver is subject to some deposit limit.
    89
         * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
    90
         * - MUST NOT revert.
    91
         */
    92
        function maxDeposit(address receiver) external view returns (uint256 maxAssets);
    93
    
                                                    
                                                
    94
        /**
    95
         * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
    96
         * current on-chain conditions.
    97
         *
    98
         * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
    99
         *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
    100
         *   in the same transaction.
    101
         * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
    102
         *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
    103
         * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    104
         * - MUST NOT revert.
    105
         *
    106
         * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
    107
         * share price or some other type of condition, meaning the depositor will lose assets by depositing.
    108
         */
    109
        function previewDeposit(uint256 assets) external view returns (uint256 shares);
    110
    
                                                    
                                                
    111
        /**
    112
         * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
    113
         *
    114
         * - MUST emit the Deposit event.
    115
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    116
         *   deposit execution, and are accounted for during deposit.
    117
         * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
    118
         *   approving enough underlying tokens to the Vault contract, etc).
    119
         *
    120
         * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    121
         */
    122
        function deposit(uint256 assets, address receiver) external returns (uint256 shares);
    123
    
                                                    
                                                
    124
        /**
    125
         * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
    126
         * - MUST return a limited value if receiver is subject to some mint limit.
    127
         * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
    128
         * - MUST NOT revert.
    129
         */
    130
        function maxMint(address receiver) external view returns (uint256 maxShares);
    131
    
                                                    
                                                
    132
        /**
    133
         * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
    134
         * current on-chain conditions.
    135
         *
    136
         * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
    137
         *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
    138
         *   same transaction.
    139
         * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
    140
         *   would be accepted, regardless if the user has enough tokens approved, etc.
    141
         * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    142
         * - MUST NOT revert.
    143
         *
    144
         * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
    145
         * share price or some other type of condition, meaning the depositor will lose assets by minting.
    146
         */
    147
        function previewMint(uint256 shares) external view returns (uint256 assets);
    148
    
                                                    
                                                
    149
        /**
    150
         * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
    151
         *
    152
         * - MUST emit the Deposit event.
    153
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
    154
         *   execution, and are accounted for during mint.
    155
         * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
    156
         *   approving enough underlying tokens to the Vault contract, etc).
    157
         *
    158
         * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    159
         */
    160
        function mint(uint256 shares, address receiver) external returns (uint256 assets);
    161
    
                                                    
                                                
    162
        /**
    163
         * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
    164
         * Vault, through a withdraw call.
    165
         *
    166
         * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    167
         * - MUST NOT revert.
    168
         */
    169
        function maxWithdraw(address owner) external view returns (uint256 maxAssets);
    170
    
                                                    
                                                
    171
        /**
    172
         * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
    173
         * given current on-chain conditions.
    174
         *
    175
         * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
    176
         *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
    177
         *   called
    178
         *   in the same transaction.
    179
         * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
    180
         *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
    181
         * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    182
         * - MUST NOT revert.
    183
         *
    184
         * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
    185
         * share price or some other type of condition, meaning the depositor will lose assets by depositing.
    186
         */
    187
        function previewWithdraw(uint256 assets) external view returns (uint256 shares);
    188
    
                                                    
                                                
    189
        /**
    190
         * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
    191
         *
    192
         * - MUST emit the Withdraw event.
    193
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    194
         *   withdraw execution, and are accounted for during withdraw.
    195
         * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
    196
         *   not having enough shares, etc).
    197
         *
    198
         * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    199
         * Those methods should be performed separately.
    200
         */
    201
        function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
    202
    
                                                    
                                                
    203
        /**
    204
         * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
    205
         * through a redeem call.
    206
         *
    207
         * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    208
         * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
    209
         * - MUST NOT revert.
    210
         */
    211
        function maxRedeem(address owner) external view returns (uint256 maxShares);
    212
    
                                                    
                                                
    213
        /**
    214
         * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
    215
         * given current on-chain conditions.
    216
         *
    217
         * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
    218
         *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
    219
         *   same transaction.
    220
         * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
    221
         *   redemption would be accepted, regardless if the user has enough shares, etc.
    222
         * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    223
         * - MUST NOT revert.
    224
         *
    225
         * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
    226
         * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
    227
         */
    228
        function previewRedeem(uint256 shares) external view returns (uint256 assets);
    229
    
                                                    
                                                
    230
        /**
    231
         * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
    232
         *
    233
         * - MUST emit the Withdraw event.
    234
         * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    235
         *   redeem execution, and are accounted for during redeem.
    236
         * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
    237
         *   not having enough shares, etc).
    238
         *
    239
         * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    240
         * Those methods should be performed separately.
    241
         */
    242
        function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
    243
    }
    244
    
                                                    
                                                
    0.0% src/vendor/standards/ERC7741/IERC7741.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: AGPL-3.0-only
    2
    pragma solidity >=0.8.0;
    3
    
                                                    
                                                
    4
    interface IERC7741 {
    5
        /**
    6
         * @dev Grants or revokes permissions for `operator` to manage Requests on behalf of the
    7
         *      `msg.sender`, using an [EIP-712](./eip-712.md) signature.
    8
         */
    9
        function authorizeOperator(
    10
            address controller,
    11
            address operator,
    12
            bool approved,
    13
            bytes32 nonce,
    14
            uint256 deadline,
    15
            bytes memory signature
    16
        )
    17
            external
    18
            returns (bool);
    19
    
                                                    
                                                
    20
        /**
    21
         * @dev Revokes the given `nonce` for `msg.sender` as the `owner`.
    22
         */
    23
        function invalidateNonce(bytes32 nonce) external;
    24
    
                                                    
                                                
    25
        /**
    26
         * @dev Returns whether the given `nonce` has been used for the `controller`.
    27
         */
    28
        function authorizations(address controller, bytes32 nonce) external view returns (bool used);
    29
    
                                                    
                                                
    30
        /**
    31
         * @dev Returns the `DOMAIN_SEPARATOR` as defined according to EIP-712. The `DOMAIN_SEPARATOR
    32
         *      should be unique to the contract and chain to prevent replay attacks from other domains,
    33
         *      and satisfy the requirements of EIP-712, but is otherwise unconstrained.
    34
         */
    35
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    36
    }
    37
    
                                                    
                                                
    0.0% test/mocks/MockYieldSourceOracle.sol
    Lines covered: 0 / 40 (0.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity ^0.8.30;
    3
    
                                                    
                                                
    4
    import { IYieldSourceOracle } from "@superform-v2-core/src/interfaces/accounting/IYieldSourceOracle.sol";
    5
    
                                                    
                                                
    6
    // Mock YieldSourceOracle implementation for testing
    7
    contract MockYieldSourceOracle is IYieldSourceOracle {
    8
        uint256 public pricePerShare;
    9
        uint256 public tvl;
    10
        uint256 public tvlByOwner;
    11
        bool public validity;
    12
        mapping(address => bool) public validAssetMap;
    13
    
                                                    
                                                
    14
        constructor(uint256 _pricePerShare, uint256 _tvl, uint256 _tvlByOwner, bool _validity) {
    15
            pricePerShare = _pricePerShare;
    16
            tvl = _tvl;
    17
            tvlByOwner = _tvlByOwner;
    18
            validity = _validity;
    19
        }
    20
    
                                                    
                                                
    21
        function setPricePerShare(uint256 _pricePerShare) external {
    22
            pricePerShare = _pricePerShare;
    23
        }
    24
    
                                                    
                                                
    25
        function setTVL(uint256 _tvl) external {
    26
            tvl = _tvl;
    27
        }
    28
    
                                                    
                                                
    29
        function setTVLByOwner(uint256 _tvlByOwner) external {
    30
            tvlByOwner = _tvlByOwner;
    31
        }
    32
    
                                                    
                                                
    33
        function setValidity(bool _validity) external {
    34
            validity = _validity;
    35
        }
    36
    
                                                    
                                                
    37
        function setValidAsset(address asset, bool isValid) external {
    38
            validAssetMap[asset] = isValid;
    39
        }
    40
    
                                                    
                                                
    41
        function decimals(address) external pure returns (uint8) {
    42
            return 18;
    43
        }
    44
    
                                                    
                                                
    45
        function getShareOutput(address, address, uint256 assetsIn) external pure returns (uint256) {
    46
            return assetsIn;
    47
        }
    48
    
                                                    
                                                
    49
        function getAssetOutput(address, address, uint256 sharesIn) public pure returns (uint256) {
    50
            return sharesIn;
    51
        }
    52
    
                                                    
                                                
    53
        function getAssetOutputWithFees(
    54
            bytes32,
    55
            address,
    56
            address,
    57
            address,
    58
            uint256 sharesIn
    59
        )
    60
            public
    61
            pure
    62
            returns (uint256)
    63
        {
    64
            return sharesIn;
    65
        }
    66
    
                                                    
                                                
    67
        function getBalanceOfOwner(address, address) external view returns (uint256) {
    68
            return tvlByOwner;
    69
        }
    70
    
                                                    
                                                
    71
        function getPricePerShare(address) external view returns (uint256) {
    72
            return pricePerShare;
    73
        }
    74
    
                                                    
                                                
    75
        function getTVLByOwnerOfShares(address, address) external view returns (uint256) {
    76
            return tvlByOwner;
    77
        }
    78
    
                                                    
                                                
    79
        function getTVL(address) external view returns (uint256) {
    80
            return tvl;
    81
        }
    82
    
                                                    
                                                
    83
        function getPricePerShareMultiple(address[] memory) external view returns (uint256[] memory) {
    84
            uint256[] memory prices = new uint256[](1);
    85
            prices[0] = pricePerShare;
    86
            return prices;
    87
        }
    88
    
                                                    
                                                
    89
        function getTVLByOwnerOfSharesMultiple(
    90
            address[] memory yieldSources,
    91
            address[][] memory
    92
        )
    93
            external
    94
            view
    95
            returns (uint256[][] memory)
    96
        {
    97
            uint256[][] memory result = new uint256[][](yieldSources.length);
    98
            for (uint256 i = 0; i < yieldSources.length; i++) {
    99
                result[i] = new uint256[](1);
    100
                result[i][0] = tvlByOwner;
    101
            }
    102
            return result;
    103
        }
    104
    
                                                    
                                                
    105
        function getTVLMultiple(address[] memory) external view returns (uint256[] memory) {
    106
            uint256[] memory tvls = new uint256[](1);
    107
            tvls[0] = tvl;
    108
            return tvls;
    109
        }
    110
    
                                                    
                                                
    111
        function isValidUnderlyingAsset(address, address asset) external view returns (bool) {
    112
            return validAssetMap[asset];
    113
        }
    114
    
                                                    
                                                
    115
        function isValidUnderlyingAssets(address[] memory, address[] memory) external view returns (bool[] memory) {
    116
            bool[] memory validities = new bool[](1);
    117
            validities[0] = validity;
    118
            return validities;
    119
        }
    120
    }
    121
    
                                                    
                                                
    100.0% test/recon/BeforeAfter.sol
    Lines covered: 80 / 80 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    5
    import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
    6
    import {MockERC20} from "@recon/MockERC20.sol";
    7
    
                                                    
                                                
    8
    import {ISuperVaultStrategy} from "src/interfaces/SuperVault/ISuperVaultStrategy.sol";
    9
    
                                                    
                                                
    10
    import {Setup} from "./Setup.sol";
    11
    
                                                    
                                                
    12
    enum OpType {
    13
        DEFAULT,
    14
        ADD,
    15
        REMOVE,
    16
        FULFILL,
    17
        REQUEST,
    18
        TRANSFER,
    19
        CANCEL
    20
    }
    21
    
                                                    
                                                
    22
    // ghost variables for tracking state variable values before and after function calls
    23
    abstract contract BeforeAfter is Setup {
    24
        struct Vars {
    25
            mapping(address user => uint256 pendingAsAssets) pendingUserAssets;
    26
            mapping(address user => uint256 claimableAsAssets) claimableUserAssets;
    27
            mapping(address user => ISuperVaultStrategy.SuperVaultState) state;
    28
            mapping(address user => uint256 shares) superVaultShares;
    29
            uint256 oraclePPS;
    30
            uint256 naivePPS;
    31
            uint256 summedTotalShares;
    32
            uint256 summedAccumulatorShares;
    33
            uint256 summedAccumulatorCostBasis;
    34
            uint256 summedTotalAssets;
    35
            uint256 strategyAssetBalance;
    36
            uint256 summedPendingRedeem;
    37
        }
    38
    
                                                    
                                                
    39
        Vars internal _before;
    40
        Vars internal _after;
    41
        OpType internal _currentOp;
    42
    
                                                    
                                                
    43
        modifier updateGhosts() {
    44
    ✓ 199.0K
            _currentOp = OpType.DEFAULT;
    45
    ✓ 199.0K
            __before();
    46
            _;
    47
    ✓ 173.7K
            __after();
    48
        }
    49
    
                                                    
                                                
    50
        modifier updateGhostsWithOpType(OpType op) {
    51
    ✓ 741.6K
            _currentOp = op;
    52
    ✓ 741.6K
            __before();
    53
            _;
    54
    ✓ 592.2K
            __after();
    55
        }
    56
    
                                                    
                                                
    57
        function __before() internal {
    58
            (
    59
    ✓ 3.2M
                _before.summedAccumulatorShares,
    60
    ✓ 3.2M
                _before.summedAccumulatorCostBasis
    61
    ✓ 3.2M
            ) = _sumSuperVaultValues();
    62
    ✓ 3.2M
            _before.naivePPS = _calculateNaivePPS();
    63
    ✓ 3.2M
            _before.summedTotalShares = _sumTotalShares();
    64
    ✓ 3.2M
            _before.summedTotalAssets = _sumStrategyAssets();
    65
    ✓ 3.2M
            _before.summedPendingRedeem = _sumRequestedRedemptions();
    66
    
                                                    
                                                
    67
    ✓ 3.2M
            _before.pendingUserAssets[_getActor()] = _getPendingAsAssets();
    68
    ✓ 3.2M
            _before.claimableUserAssets[_getActor()] = _getClaimableAsAssets();
    69
    ✓ 3.2M
            _before.state[_getActor()] = superVaultStrategy.getSuperVaultState(
    70
    ✓ 3.2M
                _getActor()
    71
            );
    72
    ✓ 3.2M
            _before.superVaultShares[_getActor()] = superVault.balanceOf(
    73
    ✓ 3.2M
                _getActor()
    74
            );
    75
    
                                                    
                                                
    76
    ✓ 3.2M
            _before.strategyAssetBalance = MockERC20(superVault.asset()).balanceOf(
    77
    ✓ 3.2M
                address(superVaultStrategy)
    78
            );
    79
    ✓ 3.2M
            _before.oraclePPS = superVaultAggregator.getPPS(
    80
    ✓ 3.2M
                address(superVaultStrategy)
    81
            );
    82
        }
    83
    
                                                    
                                                
    84
        function __after() internal {
    85
            (
    86
    ✓ 1.6M
                _after.summedAccumulatorShares,
    87
    ✓ 1.6M
                _after.summedAccumulatorCostBasis
    88
    ✓ 1.6M
            ) = _sumSuperVaultValues();
    89
    ✓ 1.6M
            _after.naivePPS = _calculateNaivePPS();
    90
    ✓ 1.6M
            _after.summedTotalShares = _sumTotalShares();
    91
    ✓ 1.6M
            _after.summedTotalAssets = _sumStrategyAssets();
    92
    ✓ 1.6M
            _after.summedPendingRedeem = _sumRequestedRedemptions();
    93
    
                                                    
                                                
    94
    ✓ 1.6M
            _after.pendingUserAssets[_getActor()] = _getPendingAsAssets();
    95
    ✓ 1.6M
            _after.claimableUserAssets[_getActor()] = _getClaimableAsAssets();
    96
    ✓ 1.6M
            _after.state[_getActor()] = superVaultStrategy.getSuperVaultState(
    97
    ✓ 1.6M
                _getActor()
    98
            );
    99
    ✓ 1.6M
            _after.superVaultShares[_getActor()] = superVault.balanceOf(
    100
    ✓ 1.6M
                _getActor()
    101
            );
    102
    
                                                    
                                                
    103
    ✓ 1.6M
            _after.strategyAssetBalance = MockERC20(superVault.asset()).balanceOf(
    104
    ✓ 1.6M
                address(superVaultStrategy)
    105
            );
    106
    ✓ 1.6M
            _after.oraclePPS = superVaultAggregator.getPPS(
    107
    ✓ 1.6M
                address(superVaultStrategy)
    108
            );
    109
        }
    110
    
                                                    
                                                
    111
        // Helpers
    112
    
                                                    
                                                
    113
        /// @dev total shares in the system is the sum of shares in the escrow and held by all users
    114
    ✓ 4.8M
        function _sumTotalShares() internal returns (uint256) {
    115
    ✓ 4.8M
            address[] memory actors = _getActors();
    116
    ✓ 4.8M
            uint256 totalShares;
    117
    
                                                    
                                                
    118
    ✓ 4.8M
            totalShares += superVault.balanceOf(address(superVaultEscrow));
    119
    ✓ 19.2M
            for (uint256 i; i < actors.length; i++) {
    120
    ✓ 14.4M
                totalShares += superVault.balanceOf(actors[i]);
    121
            }
    122
    
                                                    
                                                
    123
            return totalShares;
    124
        }
    125
    
                                                    
                                                
    126
        /// @notice Calculates the naive price per share by summing all assets across strategy and yield sources
    127
        /// @dev inspired by the share price calculation from BaseSuperVaultTest::_updateSuperVaultPPS
    128
        /// @return naivePPS The calculated price per share (scaled by 1e18)
    129
    ✓ 4.8M
        function _calculateNaivePPS() internal returns (uint256 naivePPS) {
    130
            // Get total supply of SuperVault shares
    131
    ✓ 4.8M
            uint256 totalSupply = superVault.totalSupply();
    132
    
                                                    
                                                
    133
            // If no shares exist, PPS is 0
    134
    ✓ 4.8M
            if (totalSupply == 0) {
    135
    ✓ 2.8M
                return 0;
    136
            }
    137
    
                                                    
                                                
    138
            // Calculate total assets across all locations
    139
    ✓ 2.0M
            uint256 totalAssets = _sumStrategyAssets();
    140
    
                                                    
                                                
    141
            // Calculate naive PPS: (totalAssets * PRECISION) / totalSupply
    142
            // Using 1e18 as precision to match the system's PPS_DECIMALS
    143
    ✓ 2.0M
            naivePPS = (totalAssets * superVault.PRECISION()) / totalSupply;
    144
    
                                                    
                                                
    145
            return naivePPS;
    146
        }
    147
    
                                                    
                                                
    148
    ✓ 6.8M
        function _sumStrategyAssets() public view returns (uint256) {
    149
            // Get the underlying asset
    150
    ✓ 6.8M
            address asset = superVault.asset();
    151
    
                                                    
                                                
    152
    ✓ 6.8M
            uint256 totalAssets;
    153
            // 1. Assets held directly in SuperVaultStrategy
    154
    ✓ 6.8M
            totalAssets += IERC20(asset).balanceOf(address(superVaultStrategy));
    155
    
                                                    
                                                
    156
            // 2. Loop through all yield sources and sum underlying asset balances
    157
    ✓ 6.8M
            address[] memory yieldSources = _getYieldSources();
    158
    ✓ 27.9M
            for (uint256 i = 0; i < yieldSources.length; i++) {
    159
    ✓ 21.1M
                if (yieldSources[i] != address(0)) {
    160
                    // Get the underlying asset balance held in each yield source
    161
    ✓ 21.1M
                    totalAssets += IERC20(asset).balanceOf(yieldSources[i]);
    162
                }
    163
            }
    164
    
                                                    
                                                
    165
    ✓ 6.8M
            return totalAssets;
    166
        }
    167
    
                                                    
                                                
    168
        function _sumSuperVaultValues()
    169
            internal
    170
            view
    171
    ✓ 4.8M
            returns (uint256 sumAccumulatorShares, uint256 sumAccumulatorCostBasis)
    172
        {
    173
    ✓ 4.8M
            address[] memory actors = _getActors();
    174
    
                                                    
                                                
    175
    ✓ 19.2M
            for (uint256 i; i < actors.length; i++) {
    176
    ✓ 14.4M
                sumAccumulatorShares += superVaultStrategy
    177
    ✓ 14.4M
                    .getSuperVaultState(actors[i])
    178
                    .accumulatorShares;
    179
    ✓ 14.4M
                sumAccumulatorCostBasis += superVaultStrategy
    180
    ✓ 14.4M
                    .getSuperVaultState(actors[i])
    181
                    .accumulatorCostBasis;
    182
            }
    183
        }
    184
    
                                                    
                                                
    185
    ✓ 4.8M
        function _sumRequestedRedemptions() internal returns (uint256) {
    186
    ✓ 4.8M
            address[] memory actors = _getActors();
    187
    ✓ 4.8M
            uint256 totalRequested;
    188
    ✓ 19.2M
            for (uint256 i; i < actors.length; i++) {
    189
    ✓ 14.4M
                totalRequested += superVault.pendingRedeemRequest(0, actors[i]);
    190
            }
    191
    
                                                    
                                                
    192
            return totalRequested;
    193
        }
    194
    
                                                    
                                                
    195
    ✓ 4.8M
        function _getPendingAsAssets() internal returns (uint256) {
    196
    ✓ 4.8M
            uint256 pendingRedemptions = superVault.pendingRedeemRequest(
    197
                0,
    198
    ✓ 9.6M
                _getActor()
    199
            );
    200
    ✓ 9.6M
            uint256 pendingRedemptionsAsAssets = superVault.convertToAssets(
    201
                pendingRedemptions
    202
            );
    203
    
                                                    
                                                
    204
            return pendingRedemptionsAsAssets;
    205
        }
    206
    
                                                    
                                                
    207
    ✓ 4.8M
        function _getClaimableAsAssets() internal returns (uint256) {
    208
    ✓ 4.8M
            uint256 claimableRedemptions = superVault.claimableRedeemRequest(
    209
                0,
    210
    ✓ 4.8M
                _getActor()
    211
            );
    212
            uint256 claimableRedemptionsAsAssets = superVault.convertToAssets(
    213
                claimableRedemptions
    214
            );
    215
    
                                                    
                                                
    216
            return claimableRedemptionsAsAssets;
    217
        }
    218
    }
    219
    
                                                    
                                                
    100.0% test/recon/CryticTester.sol
    Lines covered: 2 / 2 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {CryticAsserts} from "@chimera/CryticAsserts.sol";
    5
    
                                                    
                                                
    6
    import {TargetFunctions} from "./TargetFunctions.sol";
    7
    
                                                    
                                                
    8
    // echidna test/recon/CryticTester.sol --contract CryticTester --config echidna.yaml --format text --test-limit 1000000 --disable-slither
    9
    // medusa fuzz
    10
    ✓ 21.9M
    ⟲ 31.6K
    contract CryticTester is TargetFunctions, CryticAsserts {
    11
        constructor() payable {
    12
    ✓ 1
            setup();
    13
        }
    14
    }
    15
    
                                                    
                                                
    31.0% test/recon/Properties.sol
    Lines covered: 100 / 323 (31.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {Asserts} from "@chimera/Asserts.sol";
    5
    import {MockERC20} from "@recon/MockERC20.sol";
    6
    import {vm} from "@chimera/Hevm.sol";
    7
    import {ERC7540Properties} from "@properties-7540/ERC7540Properties.sol";
    8
    import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
    9
    
                                                    
                                                
    10
    import {ISuperVaultStrategy} from "src/interfaces/SuperVault/ISuperVaultStrategy.sol";
    11
    
                                                    
                                                
    12
    import {OpType} from "test/recon/BeforeAfter.sol";
    13
    import {BeforeAfter} from "./BeforeAfter.sol";
    14
    
                                                    
                                                
    15
    abstract contract Properties is BeforeAfter, Asserts, ERC7540Properties {
    16
        using Math for uint256;
    17
    ✓ 1
        uint256 internal TOLERANCE = 10;
    18
    
                                                    
                                                
    19
        // `!!!`-prefixed reasons are load-bearing for cross-fuzzer bug dedup in the analysis
    20
        // pipeline. Handler assertion failures surface natively on every fuzzer: as Panic(0x01)
    21
        // under echidna/medusa/recon and as recorded assertion failures under Foundry
    22
        // (`assertions_revert = false`, upstream handler-bug reporting).
    23
        string constant ASSERTION_REDEEM_MAX_REDEEM_SHOULD_NOT_REVERT =
    24
            "!!! redeeming maxRedeem should not revert";
    25
        string constant ASSERTION_WITHDRAW_MAX_WITHDRAW_SHOULD_NOT_REVERT =
    26
            "!!! withdraw of maxWithdraw should not revert";
    27
        string constant ASSERTION_PREVIEW_DEPOSIT_EQUIVALENCE =
    28
            "!!! previewDeposit and deposit equivalence";
    29
        string constant ASSERTION_PREVIEW_MINT_EQUIVALENCE =
    30
            "!!! previewMint and mint equivalence";
    31
        string constant ASSERTION_MINT_REDEEM_SYMMETRICAL =
    32
            "!!! user loses assets in mint/redeem flow";
    33
        string constant ASSERTION_DEPOSIT_WITHDRAW_SYMMETRICAL =
    34
            "!!! user loses assets in deposit/withdraw flow";
    35
        string constant ASSERTION_MAX_REDEEM_RESETS_AFTER_FULL_REDEMPTION =
    36
            "!!! maxRedeem should be reset to 0 after full redemption";
    37
        string constant ASSERTION_MAX_WITHDRAW_RESETS_AFTER_FULL_WITHDRAWAL =
    38
            "!!! maxWithdraw should be reset to 0 after full withdrawal";
    39
        string constant ASSERTION_FULFILL_DOESNT_OVER_REDEEM_MULTIPLE_ACTORS =
    40
            "!!! total shares redeemed must not exceed sum of requested shares";
    41
        string constant ASSERTION_PRIMARY_MANAGER_ALWAYS_CHANGEABLE =
    42
            "!!! Primary manager should always be changeable if not paused";
    43
        string constant ASSERTION_ALL_USERS_CAN_WITHDRAW_WHEN_UNPAUSED =
    44
            "!!! users should always be able to withdraw unless the system is paused";
    45
        string constant ASSERTION_CANCEL_REDEEM_PENDING_REQUEST_ZERO =
    46
            "!!! pendingRedeemRequests should be 0 after cancelling a redemption";
    47
        string constant ASSERTION_CANCEL_REDEEM_AVG_REQUEST_PPS_ZERO =
    48
            "!!! averageRequestPPS should be 0 after cancelling a redemption";
    49
        string constant ASSERTION_CANCEL_REDEEM_NO_OVERPAY =
    50
            "!!! user should not receive more than convertToAssets(pendingRedeemRequest) after cancelRedeem";
    51
        string constant ASSERTION_PREVIEW_DEPOSIT_MATCHES_EXECUTION =
    52
            "!!! previewDeposit returns the correct amount compared to executing a deposit";
    53
        string constant ASSERTION_PREVIEW_MINT_MATCHES_EXECUTION =
    54
            "!!! previewMint returns the correct amount compared to executing a mint";
    55
        string constant ASSERTION_TRANSFER_SHARES_CONSERVED =
    56
            "!!! shares are lost on transfer";
    57
        string constant ASSERTION_TRANSFER_COST_BASIS_CONSERVED =
    58
            "!!! cost basis is lost on transfer";
    59
        string constant ASSERTION_REDEEM_SHOULD_NOT_REVERT_INVALID_REDEEM_CLAIM =
    60
            "!!! Claiming redemptions should never revert with INVALID_REDEEM_CLAIM";
    61
        string constant ASSERTION_UPDATE_SHOULD_NOT_REVERT_TRANSFER =
    62
            "!!! _update should never revert in transfer";
    63
        string constant ASSERTION_UPDATE_SHOULD_NOT_REVERT_TRANSFER_FROM =
    64
            "!!! _update should never revert in transferFrom";
    65
        string constant ASSERTION_STRATEGY_NO_LOSS_ON_FULFILLMENT =
    66
            "!!! strategy incurs loss on fulfillment";
    67
        string constant ASSERTION_GLOBAL_PREVIEW_EQUIVALENCE_FROM_SHARES =
    68
            "!!! previewMint and previewDeposit equivalence (from shares)";
    69
        string constant ASSERTION_GLOBAL_PREVIEW_EQUIVALENCE_FROM_ASSETS =
    70
            "!!! previewMint and previewDeposit equivalence (from assets)";
    71
        string constant ASSERTION_GLOBAL_PREVIEW_MINT_GTE_CONVERT_TO_ASSETS =
    72
            "!!! previewMint is >= convertToAssets";
    73
        string constant ASSERTION_GLOBAL_CONVERT_TO_SHARES_GTE_PREVIEW_DEPOSIT =
    74
            "!!! convertToShares is >= previewDepositShares (equivalent without fees)";
    75
        string constant ASSERTION_ERC7540_4_DEPOSIT =
    76
            "!!! ERC7540-4: deposit with more than max should revert";
    77
        string constant ASSERTION_ERC7540_4_MINT =
    78
            "!!! ERC7540-4: mint with more than max should revert";
    79
        string constant ASSERTION_ERC7540_4_WITHDRAW =
    80
            "!!! ERC7540-4: withdraw with more than max should revert";
    81
        string constant ASSERTION_ERC7540_4_REDEEM =
    82
            "!!! ERC7540-4: redeem with more than max should revert";
    83
        string constant ASSERTION_ERC7540_5 =
    84
            "!!! ERC7540-5: requestRedeem should revert if insufficient share balance";
    85
        string constant ASSERTION_ERC7540_7_WITHDRAW =
    86
            "!!! ERC7540-7: withdraw should not revert when amount <= max";
    87
        string constant ASSERTION_ERC7540_7_REDEEM =
    88
            "!!! ERC7540-7: redeem should not revert when amount <= max";
    89
        string constant ASSERTION_CANARY =
    90
            "!!! canary assertion";
    91
        string constant INVARIANT_CANARY_GLOBAL_INVARIANT_FAILURE =
    92
            "Canary invariant";
    93
    
                                                    
                                                
    94
        /// @dev Property: oracle PPS doesn't change on deposit/mint/redeem/withdraw
    95
        function invariant_oraclePPSDoesntChangeOnAddOrRemove() public returns (bool) {
    96
            if (_currentOp == OpType.ADD || _currentOp == OpType.REMOVE) {
    97
                eq(
    98
                    _before.oraclePPS,
    99
                    _after.oraclePPS,
    100
                    "deposit/withdrawal changes oracle PPS"
    101
                );
    102
            }
    103
            return true;
    104
        }
    105
    
                                                    
                                                
    106
        /// @dev Property: naive PPS doesn't change on deposit/mint
    107
        // NOTE: removed because it's expected behavior that fulfillment burns shares but doesn't transfer assets to users so would change the naively calculated price
    108
        // function property_naivePPSDoesntChangeOnDepositOrMint() public {
    109
        //     if (
    110
        //         (_currentOp == OpType.ADD) && _before.naivePPS != 0 // price starts as zero when no shares minted
    111
        //     ) {
    112
        //         gte(
    113
        //             _after.naivePPS,
    114
        //             _before.naivePPS,
    115
        //             "deposit/mint cannot decrease naive PPS"
    116
        //         );
    117
        //     }
    118
        // }
    119
    
                                                    
                                                
    120
        /// @dev Property: naive PPS doesn't change on redeem/withdraw
    121
        // NOTE: removed because it's expected behavior that fulfillment burns shares but doesn't transfer assets to users so would change the naively calculated price
    122
        // function property_naivePPSDoesntChangeOnRedeemOrWithdraw() public {
    123
        //     if (
    124
        //         (_currentOp == OpType.REMOVE) && _before.naivePPS != 0 // price starts as zero when no shares minted
    125
        //     ) {
    126
        //         gte(
    127
        //             _after.naivePPS,
    128
        //             _before.naivePPS,
    129
        //             "redeem/withdraw cannot decrease naive PPS"
    130
        //         );
    131
        //     }
    132
        // }
    133
    
                                                    
                                                
    134
        /// @dev Property: fulfillRedeemRequest doesn't change naive PPS
    135
        // NOTE: removed because it's expected behavior that fulfillment burns shares but doesn't transfer assets to users so would change the naively calculated price
    136
        // function property_naivePPSDoesntChangeOnRedeem() public {
    137
        //     if (_currentOp == OpType.FULFILL) {
    138
        //         eq(
    139
        //             _before.naivePPS,
    140
        //             _after.naivePPS,
    141
        //             "fulfilling redemption changes naive PPS"
    142
        //         );
    143
        //     }
    144
        // }
    145
    
                                                    
                                                
    146
        /// @dev Property: maxRedeem and maxWithdraw should always be equivalent
    147
        function invariant_maxRedeemMaxWithdrawSymmetry() public returns (bool) {
    148
            uint256 maxWithdraw = superVault.maxWithdraw(_getActor());
    149
            // convertToShares uses current price instead of avg from fulfillment
    150
            uint256 withdrawPrice = superVaultStrategy.getAverageWithdrawPrice(
    151
                _getActor()
    152
            );
    153
            uint256 maxWithdrawAsShares = maxWithdraw.mulDiv(
    154
                superVault.PRECISION(),
    155
                withdrawPrice,
    156
                Math.Rounding.Floor
    157
            );
    158
    
                                                    
                                                
    159
            uint256 maxRedeem = superVault.maxRedeem(_getActor());
    160
    
                                                    
                                                
    161
            eq(maxWithdrawAsShares, maxRedeem, "maxWithdrawAsShares != maxRedeem");
    162
            return true;
    163
        }
    164
    
                                                    
                                                
    165
        /// @dev Property: requestRedeem should never reduce SuperVault shares
    166
        function invariant_totalSharesDontDecreaseOnRedemptionRequest() public returns (bool) {
    167
            if (_currentOp == OpType.REQUEST) {
    168
                eq(
    169
                    _before.summedTotalShares,
    170
                    _after.summedTotalShares,
    171
                    "requestRedeem should never reduce SuperVault shares"
    172
                );
    173
            }
    174
            return true;
    175
        }
    176
    
                                                    
                                                
    177
        /// @dev Property: `SuperVault::totalSupply` == SUM(user balances) + balanceOf(escrow)
    178
        function invariant_shareSolvency() public returns (bool) {
    179
            uint256 sumOfShares = _sumTotalShares();
    180
            eq(superVault.totalSupply(), sumOfShares, "vault shares are insolvent");
    181
            return true;
    182
        }
    183
    
                                                    
                                                
    184
        /// @dev Property: balanceOf(escrow) >= SUM(controllers.pendingRedeemRequest)
    185
        function invariant_escrowBalance() public returns (bool) {
    186
            address[] memory actors = _getActors();
    187
    
                                                    
                                                
    188
            uint256 escrowBalance = superVault.balanceOf(address(superVaultEscrow));
    189
            uint256 pendingActorShares;
    190
            for (uint256 i; i < actors.length; i++) {
    191
                pendingActorShares += superVault.pendingRedeemRequest(0, actors[i]);
    192
            }
    193
    
                                                    
                                                
    194
            gte(
    195
                escrowBalance,
    196
                pendingActorShares,
    197
                "balanceOf(escrow) < SUM(controllers.pendingRedeemRequest)"
    198
            );
    199
            return true;
    200
        }
    201
    
                                                    
                                                
    202
        /// @dev Property: maxMint should be 0 when aggregator is paused
    203
        function invariant_maxMintZeroWhenPaused() public returns (bool) {
    204
            bool paused = superVaultAggregator.isStrategyPaused(
    205
                address(superVaultStrategy)
    206
            );
    207
            uint256 maxMint = superVault.maxMint(_getActor());
    208
    
                                                    
                                                
    209
            if (paused) {
    210
                eq(maxMint, 0, "actor has nonzero maxMint when strategy is paused");
    211
            }
    212
            return true;
    213
        }
    214
    
                                                    
                                                
    215
        /// @dev Property: maxDeposit should be 0 when strategy is paused
    216
        function invariant_maxDepositZeroWhenPaused() public returns (bool) {
    217
            bool paused = superVaultAggregator.isStrategyPaused(
    218
                address(superVaultStrategy)
    219
            );
    220
            uint256 maxDeposit = superVault.maxDeposit(_getActor());
    221
    
                                                    
                                                
    222
            if (paused) {
    223
                eq(
    224
                    maxDeposit,
    225
                    0,
    226
                    "actor has nonzero maxDeposit when strategy is paused"
    227
                );
    228
            }
    229
            return true;
    230
        }
    231
    
                                                    
                                                
    232
        /// @dev Property: SUM(accumulatorShares) doesn't change on SuperVault share transfers
    233
        function invariant_accumulatorSharesSolvency() public returns (bool) {
    234
            if (_currentOp == OpType.TRANSFER) {
    235
                eq(
    236
                    _before.summedAccumulatorShares,
    237
                    _after.summedAccumulatorShares, // TODO: think about way to handle transfer on recipient
    238
                    "SUM(accumulatorShares) changed on SuperVault share transfers"
    239
                );
    240
            }
    241
            return true;
    242
        }
    243
    
                                                    
                                                
    244
        /// @dev Property: SUM(accumulatorCostBasis) doesn't change on SuperVault share transfers
    245
        function invariant_accumulatorCostBasisSolvency() public returns (bool) {
    246
            if (_currentOp == OpType.TRANSFER) {
    247
                eq(
    248
                    _before.summedAccumulatorCostBasis,
    249
                    _after.summedAccumulatorCostBasis,
    250
                    "SUM(accumulatorCostBasis) changed on SuperVault share transfers"
    251
                );
    252
            }
    253
            return true;
    254
        }
    255
    
                                                    
                                                
    256
        /// @dev Property: cancelRedeem should never alter the supply of SuperVault tokens
    257
        function invariant_cancelDoesntChangeTotalSupply() public returns (bool) {
    258
            if (_currentOp == OpType.CANCEL) {
    259
                eq(
    260
                    _before.summedTotalShares,
    261
                    _after.summedTotalShares,
    262
                    "cancelRedeem should never alter the supply of SuperVault tokens"
    263
                );
    264
            }
    265
            return true;
    266
        }
    267
    
                                                    
                                                
    268
        /// @dev Property: if totalSupply > 0, then totalAssets > 0
    269
        function invariant_assetBacking() public returns (bool) {
    270
            uint256 summedTotalAssets = _sumStrategyAssets();
    271
    
                                                    
                                                
    272
            if (superVault.totalSupply() > 0) {
    273
                gt(
    274
                    summedTotalAssets,
    275
                    0,
    276
                    "if totalSupply > 0, then totalAssets > 0"
    277
                );
    278
            }
    279
            return true;
    280
        }
    281
    
                                                    
                                                
    282
        /// @dev Property: SUM(shares) * PPS == totalAssets
    283
        function invariant_totalAssets() public returns (bool) {
    284
            uint256 totalShares = _sumTotalShares();
    285
            uint256 pps = superVaultAggregator.getPPS(address(superVaultStrategy));
    286
            uint256 implicitTotalAssets = (totalShares * pps) /
    287
                superVault.PRECISION();
    288
    
                                                    
                                                
    289
            uint256 vaultTotalAssets = superVault.totalAssets();
    290
            eq(
    291
                implicitTotalAssets,
    292
                vaultTotalAssets,
    293
                "totalShares * pps != totalAssets"
    294
            );
    295
            return true;
    296
        }
    297
    
                                                    
                                                
    298
        /// @dev Property: When a user requests a redemption and the PPS is >= the user PPS, user averageRequestPPS must not decrease
    299
        function invariant_avgPPSDoesntDecrease() public returns (bool) {
    300
            uint256 currentPrice = _before.oraclePPS;
    301
            uint256 beforeAvgPPS = _before.state[_getActor()].averageRequestPPS;
    302
            uint256 afterAvgPPS = _after.state[_getActor()].averageRequestPPS;
    303
    
                                                    
                                                
    304
            if (_currentOp == OpType.REQUEST && currentPrice >= beforeAvgPPS) {
    305
                gte(
    306
                    afterAvgPPS,
    307
                    beforeAvgPPS,
    308
                    "when a user requests a redemption and the PPS is >= the user PPS, user averageRequestPPS must not decrease"
    309
                );
    310
            }
    311
            return true;
    312
        }
    313
    
                                                    
                                                
    314
        /// @dev Property: previewMint and previewDeposit equivalence (from shares)
    315
        function global_previewEquivalenceFromShares_ASSERTION_GLOBAL_PREVIEW_EQUIVALENCE_FROM_SHARES(uint256 shares) public {
    316
    ✓ 106.4K
            uint256 previewMintAssets = superVault.previewMint(shares);
    317
    ✓ 71.4K
            uint256 previewDepositShares = superVault.previewDeposit(
    318
                previewMintAssets
    319
            );
    320
    ✓ 70.7K
            uint256 price = superVaultStrategy.getStoredPPS();
    321
    
                                                    
                                                
    322
    ✓ 70.7K
            if (price > 0) {
    323
    ✓ 69.5K
                eq(
    324
    ✓ 69.5K
                    shares,
    325
    ✓ 69.5K
                    previewDepositShares,
    326
    ✓ 69.5K
                    ASSERTION_GLOBAL_PREVIEW_EQUIVALENCE_FROM_SHARES
    327
                );
    328
            }
    329
        }
    330
    
                                                    
                                                
    331
        /// @dev Property: previewMint and previewDeposit equivalence (from assets)
    332
        function global_previewEquivalenceFromAssets_ASSERTION_GLOBAL_PREVIEW_EQUIVALENCE_FROM_ASSETS(uint256 assets) public {
    333
    ✓ 119.3K
            uint256 previewDepositShares = superVault.previewDeposit(assets);
    334
    ✓ 116.2K
            uint256 previewMintAssets_under = superVault.previewMint(
    335
                previewDepositShares
    336
            );
    337
    ✓ 116.2K
            uint256 previewMintAssets_over = superVault.previewMint(
    338
    ✓ 116.2K
                previewDepositShares + 1
    339
            );
    340
    ✓ 113.5K
            uint256 price = superVaultStrategy.getStoredPPS();
    341
    
                                                    
                                                
    342
    ✓ 113.5K
            if (price > 0) {
    343
    ✓ 112.9K
                gte(
    344
    ✓ 112.9K
                    assets,
    345
    ✓ 112.9K
                    previewMintAssets_under,
    346
    ✓ 112.9K
                    ASSERTION_GLOBAL_PREVIEW_EQUIVALENCE_FROM_ASSETS
    347
                );
    348
    
                                                    
                                                
    349
    ✓ 112.9K
                lte(
    350
    ✓ 112.9K
                    assets,
    351
    ✓ 112.9K
                    previewMintAssets_over,
    352
    ✓ 112.9K
                    ASSERTION_GLOBAL_PREVIEW_EQUIVALENCE_FROM_ASSETS
    353
                );
    354
            }
    355
        }
    356
    
                                                    
                                                
    357
        /// @dev Property: previewMint is >= convertToAssets
    358
        function global_comparePreviewMintAndConvertToAssets_ASSERTION_GLOBAL_PREVIEW_MINT_GTE_CONVERT_TO_ASSETS(
    359
            uint256 shares
    360
        ) public {
    361
    ✓ 161.3K
            uint256 previewMintAssets = superVault.previewMint(shares);
    362
    ✓ 130.4K
            uint256 convertToAssets = superVault.convertToAssets(shares);
    363
    ✓ 130.4K
            gte(
    364
    ✓ 130.4K
                previewMintAssets,
    365
    ✓ 130.4K
                convertToAssets,
    366
    ✓ 130.4K
                ASSERTION_GLOBAL_PREVIEW_MINT_GTE_CONVERT_TO_ASSETS
    367
            );
    368
        }
    369
    
                                                    
                                                
    370
        /// @dev Property: convertToShares is >= previewDepositShares (equivalent without fees)
    371
        function global_comparePreviewDepositAndConvertToShares_ASSERTION_GLOBAL_CONVERT_TO_SHARES_GTE_PREVIEW_DEPOSIT(
    372
            uint256 assets
    373
        ) public {
    374
    ✓ 224.2K
            uint256 previewDepositShares = superVault.previewDeposit(assets);
    375
    ✓ 220.8K
            uint256 convertToShares = superVault.convertToShares(assets);
    376
    ✓ 219.8K
            gte(
    377
    ✓ 219.8K
                convertToShares,
    378
    ✓ 219.8K
                previewDepositShares,
    379
    ✓ 219.8K
                ASSERTION_GLOBAL_CONVERT_TO_SHARES_GTE_PREVIEW_DEPOSIT
    380
            );
    381
        }
    382
    
                                                    
                                                
    383
        // NOTE: Withdrawal properties are more nuanced, as we should ensure that no gain can be had and that losses are imputed to the controller receiving the assets
    384
    
                                                    
                                                
    385
        /// @dev Property: After all redemptions are processed, the sum of all claimable is <= balance available
    386
        function invariant_sumOfClaimable() public returns (bool) {
    387
            address[] memory actors = _getActors();
    388
    
                                                    
                                                
    389
            uint256 sumPending;
    390
            uint256 sumClaimable;
    391
            for (uint256 i; i < actors.length; i++) {
    392
                sumPending += superVault.pendingRedeemRequest(0, actors[i]);
    393
                sumClaimable += superVault.maxWithdraw(actors[i]);
    394
            }
    395
    
                                                    
                                                
    396
            uint256 strategyBalance = MockERC20(superVault.asset()).balanceOf(
    397
                address(superVaultStrategy)
    398
            );
    399
    
                                                    
                                                
    400
            // precondition: all pending has been fulfilled
    401
            if (sumPending == 0) {
    402
                lte(
    403
                    sumClaimable,
    404
                    strategyBalance,
    405
                    "sum of all claimable is > balance available"
    406
                );
    407
            }
    408
            return true;
    409
        }
    410
    
                                                    
                                                
    411
        /// @dev Property: If the sum of assets in SuperVaultStrategy and yield strategies is 0, maxWithdraw should be 0
    412
        function invariant_sumOfAssetsMaxWithdrawable() public returns (bool) {
    413
            uint256 summedTotalAssets = _sumStrategyAssets();
    414
    
                                                    
                                                
    415
            if (summedTotalAssets == 0) {
    416
                uint256 maxWithdraw = superVault.maxWithdraw(_getActor());
    417
                eq(
    418
                    maxWithdraw,
    419
                    0,
    420
                    "if sum of assets in SuperVaultStrategy and yield strategies is 0, maxWithdraw should be 0"
    421
                );
    422
            }
    423
            return true;
    424
        }
    425
    
                                                    
                                                
    426
        /// @dev Property: averageWithdrawPrice should never decrease when new redemptions are fulfilled at a higher PPS
    427
        function invariant_avgPPSMonotonicity() public returns (bool) {
    428
            if (
    429
                _currentOp == OpType.FULFILL &&
    430
                _before.oraclePPS > _before.state[_getActor()].averageRequestPPS && // fulfilled at a higher price
    431
                _after.state[_getActor()].pendingRedeemRequest != 0 // redemptions have all been fulfilled/cancelled; avg gets reset to 0 in this case
    432
            ) {
    433
                gte(
    434
                    _after.state[_getActor()].averageRequestPPS,
    435
                    _before.state[_getActor()].averageRequestPPS,
    436
                    "averageWithdrawPrice should not decrease when fulfilled at a higher PPS"
    437
                );
    438
            }
    439
            return true;
    440
        }
    441
    
                                                    
                                                
    442
        /// @dev Property: state.accumulatorShares >= superVaultState[controllers[i]].pendingRedeemRequest for each user
    443
        function invariant_accumulatorSharesGtPendingRequests() public returns (bool) {
    444
            address[] memory actors = _getActors();
    445
    
                                                    
                                                
    446
            for (uint256 i; i < actors.length; i++) {
    447
                ISuperVaultStrategy.SuperVaultState
    448
                    memory state = superVaultStrategy.getSuperVaultState(actors[i]);
    449
                gte(
    450
                    state.accumulatorShares,
    451
                    state.pendingRedeemRequest,
    452
                    "state.accumulatorShares < state.pendingRedeemRequest"
    453
                );
    454
            }
    455
            return true;
    456
        }
    457
    
                                                    
                                                
    458
        /// @dev Property: accumulatorShares is always accurately increased
    459
        function invariant_accumulatorSharesIncrease() public returns (bool) {
    460
            if (_currentOp == OpType.ADD) {
    461
                uint256 accumulatorSharesBefore = _before
    462
                    .state[_getActor()]
    463
                    .accumulatorShares;
    464
                uint256 accumulatorSharesAfter = _after
    465
                    .state[_getActor()]
    466
                    .accumulatorShares;
    467
                uint256 actorSharesBefore = _before.superVaultShares[_getActor()];
    468
                uint256 actorSharesAfter = _after.superVaultShares[_getActor()];
    469
                eq(
    470
                    accumulatorSharesAfter - accumulatorSharesBefore,
    471
                    actorSharesAfter - actorSharesBefore,
    472
                    "accumulatorShares is always accurately updated"
    473
                );
    474
            }
    475
            return true;
    476
        }
    477
    
                                                    
                                                
    478
        /// @dev Property: accumulatorCostBasis is always accurately increased
    479
        function invariant_accumulatorCostBasisIncrease() public returns (bool) {
    480
            if (_currentOp == OpType.ADD) {
    481
                uint256 accumulatorCostBasisBefore = _before
    482
                    .state[_getActor()]
    483
                    .accumulatorCostBasis;
    484
                uint256 accumulatorCostBasisAfter = _after
    485
                    .state[_getActor()]
    486
                    .accumulatorCostBasis;
    487
                eq(
    488
                    accumulatorCostBasisAfter - accumulatorCostBasisBefore,
    489
                    _after.strategyAssetBalance - _before.strategyAssetBalance,
    490
                    "accumulatorShares is always accurately updated"
    491
                );
    492
            }
    493
            return true;
    494
        }
    495
    
                                                    
                                                
    496
        /// @dev Property: redemptions only burn the requested amount of shares (within tolerance range)
    497
        function invariant_fulfillOnlyBurnsRequestedAmount() public returns (bool) {
    498
            uint256 TOLERANCE_CONSTANT = 10 wei; // taken from SuperVaultStrategy
    499
    
                                                    
                                                
    500
            if (_currentOp == OpType.FULFILL) {
    501
                uint256 pendingRedeemDelta = _before.summedPendingRedeem -
    502
                    _after.summedPendingRedeem;
    503
                uint256 totalSupplyDelta = _before.summedTotalShares -
    504
                    _after.summedTotalShares;
    505
    
                                                    
                                                
    506
                // Check that burned amount is within tolerance of requested amount
    507
                if (totalSupplyDelta < pendingRedeemDelta) {
    508
                    // Burned less than requested - check within tolerance
    509
                    gte(
    510
                        totalSupplyDelta,
    511
                        pendingRedeemDelta - TOLERANCE_CONSTANT,
    512
                        "burned less than requested beyond tolerance"
    513
                    );
    514
                } else {
    515
                    // Burned more than requested - check within tolerance
    516
                    lte(
    517
                        totalSupplyDelta,
    518
                        pendingRedeemDelta + TOLERANCE_CONSTANT,
    519
                        "burned more than requested beyond tolerance"
    520
                    );
    521
                }
    522
            }
    523
            return true;
    524
        }
    525
    
                                                    
                                                
    526
        /// @dev Property: accumulatorShares decreases by the exact amounts requested when fulfilling redemptions
    527
        function invariant_accumulatorSharesDecreaseOnFulfill_exact() public returns (bool) {
    528
            if (_currentOp == OpType.FULFILL) {
    529
                uint256 accumulatorSharesDelta = _before.summedAccumulatorShares -
    530
                    _after.summedAccumulatorShares;
    531
                uint256 totalSharesDelta = _before.summedTotalShares -
    532
                    _after.summedTotalShares;
    533
                eq(
    534
                    accumulatorSharesDelta,
    535
                    totalSharesDelta,
    536
                    "accumulatorShares decreases by the exact amounts requested when fulfilling redemptions"
    537
                );
    538
            }
    539
            return true;
    540
        }
    541
    
                                                    
                                                
    542
        function invariant_accumulatorSharesDecreaseOnFulfill_with_tolerance()
    543
            public returns (bool)
    544
        {
    545
            if (_currentOp == OpType.FULFILL) {
    546
                uint256 accumulatorSharesDelta = _before.summedAccumulatorShares -
    547
                    _after.summedAccumulatorShares;
    548
                uint256 totalSharesDelta = _before.summedTotalShares -
    549
                    _after.summedTotalShares;
    550
                if (accumulatorSharesDelta >= totalSharesDelta) {
    551
                    lte(
    552
                        accumulatorSharesDelta - totalSharesDelta,
    553
                        TOLERANCE,
    554
                        "accumulatorShares decreases by more than TOLERANCE when fulfilling redemptions"
    555
                    );
    556
                } else {
    557
                    lte(
    558
                        totalSharesDelta - accumulatorSharesDelta,
    559
                        TOLERANCE,
    560
                        "accumulatorShares decreases by less than TOLERANCE when fulfilling redemptions"
    561
                    );
    562
                }
    563
            }
    564
            return true;
    565
        }
    566
    
                                                    
                                                
    567
        /// Optimization Setters
    568
    
                                                    
                                                
    569
        function setpreviewAssetsGreater(uint256 shares) public {
    570
    ✓ 139.2K
            uint256 previewMintAssets = superVault.previewMint(shares);
    571
    ✓ 100.9K
            uint256 previewDepositAssets = superVault.previewDeposit(
    572
                previewMintAssets
    573
            );
    574
    
                                                    
                                                
    575
    ✓ 100.3K
            if (previewMintAssets > previewDepositAssets) {
    576
    ✓ 91.4K
                previewMintAssetsGreater = int256(previewMintAssets);
    577
            } else {
    578
    ✓ 8.9K
                previewDepositAssetsGreater = int256(previewDepositAssets);
    579
            }
    580
        }
    581
    
                                                    
                                                
    582
        function setPreviewSharesGreater(uint256 assets) public {
    583
    ✓ 119.7K
            uint256 previewDepositShares = superVault.previewDeposit(assets);
    584
    ✓ 116.9K
            uint256 previewMintShares = superVault.previewMint(
    585
                previewDepositShares
    586
            );
    587
    
                                                    
                                                
    588
    ✓ 116.9K
            if (previewDepositShares > previewMintShares) {
    589
    ✓ 2.2K
                previewDepositSharesGreater =
    590
    ✓ 2.2K
                    int256(previewDepositShares) -
    591
    ✓ 2.2K
                    int256(previewMintShares);
    592
            } else {
    593
    ✓ 110.9K
                previewMintSharesGreater =
    594
    ✓ 114.7K
                    int256(previewMintShares) -
    595
    ✓ 114.7K
                    int256(previewDepositShares);
    596
            }
    597
        }
    598
    
                                                    
                                                
    599
        function setFulfilledDifference() public {
    600
    ✓ 105.9K
            if (_currentOp == OpType.FULFILL) {
    601
    ✓ 8.6K
                uint256 pendingRedeemDelta = _before.summedPendingRedeem -
    602
    ✓ 8.6K
                    _after.summedPendingRedeem;
    603
    ✓ 8.6K
                uint256 totalSupplyDelta = _before.summedTotalShares -
    604
    ✓ 8.6K
                    _after.summedTotalShares;
    605
    
                                                    
                                                
    606
                // Check that burned amount is within tolerance of requested amount
    607
    ✓ 8.6K
                if (totalSupplyDelta < pendingRedeemDelta) {
    608
                    // Burned less than requested
    609
    ✓ 7.2K
                    int256 burnedLessThanRequested = int256(pendingRedeemDelta) -
    610
    ✓ 7.2K
                        int256(totalSupplyDelta);
    611
                } else {
    612
                    // Burned more than requested
    613
    ✓ 1.4K
                    int256 burnedMoreThanRequested = int256(totalSupplyDelta) -
    614
    ✓ 1.4K
                        int256(pendingRedeemDelta);
    615
                }
    616
            }
    617
        }
    618
    
                                                    
                                                
    619
        /// Optimization Tests
    620
    
                                                    
                                                
    621
        /// @dev Optimize the difference between the amount of assets in the system and claimable assets
    622
        function optimize_maxDustAccumulation() public view returns (int256) {
    623
            address[] memory actors = _getActors();
    624
    
                                                    
                                                
    625
            uint256 summedClaimableRedemptionsAsAssets;
    626
            for (uint256 i; i < actors.length; i++) {
    627
                uint256 claimableRedemptions = superVault.claimableRedeemRequest(
    628
                    0,
    629
                    actors[i]
    630
                );
    631
                summedClaimableRedemptionsAsAssets += superVault.convertToAssets(
    632
                    claimableRedemptions
    633
                );
    634
            }
    635
    
                                                    
                                                
    636
            uint256 totalAssets = _sumStrategyAssets();
    637
    
                                                    
                                                
    638
            return int256(totalAssets) - int256(summedClaimableRedemptionsAsAssets);
    639
        }
    640
    
                                                    
                                                
    641
        /// @dev Optimize the difference between the amount of claimable assets and assets in the system
    642
        function optimize_moreClaimableThanHeldDifference()
    643
            public
    644
            view
    645
            returns (int256)
    646
        {
    647
            address[] memory actors = _getActors();
    648
    
                                                    
                                                
    649
            uint256 summedClaimableRedemptionsAsAssets;
    650
            for (uint256 i; i < actors.length; i++) {
    651
                summedClaimableRedemptionsAsAssets += superVault.maxWithdraw(
    652
                    actors[i]
    653
                );
    654
            }
    655
    
                                                    
                                                
    656
            uint256 totalAssets = _sumStrategyAssets();
    657
    
                                                    
                                                
    658
            if (summedClaimableRedemptionsAsAssets > totalAssets) {
    659
                return
    660
                    int256(summedClaimableRedemptionsAsAssets) -
    661
                    int256(totalAssets);
    662
            }
    663
        }
    664
    
                                                    
                                                
    665
        function optimize_burnMoreThanRequestedInRedemption()
    666
            public
    667
            view
    668
            returns (int256)
    669
        {
    670
            return burnedMoreThanRequested;
    671
        }
    672
    
                                                    
                                                
    673
        function optimize_burnLessThanRequestedInRedemption()
    674
            public
    675
            view
    676
            returns (int256)
    677
        {
    678
            return burnedLessThanRequested;
    679
        }
    680
    
                                                    
                                                
    681
        // function optimize_previewMintSharesGreater() public view returns (int256) {
    682
        //     return previewMintSharesGreater;
    683
        // }
    684
    
                                                    
                                                
    685
        // function optimize_previewDepositSharesGreater()
    686
        //     public
    687
        //     view
    688
        //     returns (int256)
    689
        // {
    690
        //     return previewDepositSharesGreater;
    691
        // }
    692
    
                                                    
                                                
    693
        // function optimize_previewMintAssetsGreater() public view returns (int256) {
    694
        //     return previewMintAssetsGreater;
    695
        // }
    696
    
                                                    
                                                
    697
        // function optimize_previewDepositAssetsGreater()
    698
        //     public
    699
        //     view
    700
        //     returns (int256)
    701
        // {
    702
        //     return previewDepositAssetsGreater;
    703
        // }
    704
    
                                                    
                                                
    705
        function optimize_assetBackingDifference() public view returns (int256) {
    706
            uint256 summedTotalAssets = _sumStrategyAssets();
    707
            uint256 totalSupply = superVault.totalSupply();
    708
    
                                                    
                                                
    709
            if (summedTotalAssets == 0 && totalSupply > 0) {
    710
                return int256(totalSupply);
    711
            }
    712
        }
    713
    
                                                    
                                                
    714
        // Canaries
    715
    
                                                    
                                                
    716
        /// @dev Canary assertion helper. A failing input is expected to be discovered during fuzzing.
    717
        function assert_canary_ASSERTION_CANARY(uint256 entropy) public {
    718
    ✓ 171.4K
            t(entropy > 0, ASSERTION_CANARY);
    719
        }
    720
    
                                                    
                                                
    721
        /// @dev Canary global invariant expected to fail immediately.
    722
        function invariant_canary() public returns (bool) {
    723
            t(false, INVARIANT_CANARY_GLOBAL_INVARIANT_FAILURE);
    724
            return true;
    725
        }
    726
    
                                                    
                                                
    727
        // ERC7540 Properties from erc7540-reusable-properties
    728
    
                                                    
                                                
    729
        /// @dev Property 7540-1: convertToAssets(totalSupply) == totalAssets unless price is 0.0
    730
        function invariant_erc7540_1() public returns (bool) {
    731
            actor = _getActor();
    732
            t(
    733
                erc7540_1(address(superVault)),
    734
                "ERC7540-1: convertToAssets(totalSupply) == totalAssets failed"
    735
            );
    736
            return true;
    737
        }
    738
    
                                                    
                                                
    739
        /// @dev Property 7540-2: convertToShares(totalAssets) == totalSupply unless price is 0.0
    740
        function invariant_erc7540_2() public returns (bool) {
    741
            actor = _getActor();
    742
            t(
    743
                erc7540_2(address(superVault)),
    744
                "ERC7540-2: convertToShares(totalAssets) == totalSupply failed"
    745
            );
    746
            return true;
    747
        }
    748
    
                                                    
                                                
    749
        /// @dev Property 7540-3: max* never reverts
    750
        function invariant_erc7540_3() public returns (bool) {
    751
            actor = _getActor();
    752
            t(
    753
                erc7540_3(address(superVault)),
    754
                "ERC7540-3: max* functions should never revert"
    755
            );
    756
            return true;
    757
        }
    758
    
                                                    
                                                
    759
        /// @dev Property 7540-4: claiming more than max always reverts
    760
        function global_erc7540_4_deposit_ASSERTION_ERC7540_4_DEPOSIT(uint256 amt) public stateless {
    761
    ✓ 152.9K
            actor = _getActor();
    762
    ✓ 14.8K
            t(
    763
    ✓ 152.9K
                erc7540_4_deposit(address(superVault), amt),
    764
    ✓ 14.8K
                ASSERTION_ERC7540_4_DEPOSIT
    765
            );
    766
        }
    767
    
                                                    
                                                
    768
        function global_erc7540_4_mint_ASSERTION_ERC7540_4_MINT(uint256 amt) public stateless {
    769
    ✓ 115.5K
            actor = _getActor();
    770
    ✓ 6.5K
            t(
    771
    ✓ 115.5K
                erc7540_4_mint(address(superVault), amt),
    772
    ✓ 6.5K
                ASSERTION_ERC7540_4_MINT
    773
            );
    774
        }
    775
    
                                                    
                                                
    776
        function global_erc7540_4_withdraw_ASSERTION_ERC7540_4_WITHDRAW(uint256 amt) public stateless {
    777
    ✓ 136.8K
            actor = _getActor();
    778
    ✓ 136.7K
            t(
    779
    ✓ 136.8K
                erc7540_4_withdraw(address(superVault), amt),
    780
    ✓ 136.7K
                ASSERTION_ERC7540_4_WITHDRAW
    781
            );
    782
        }
    783
    
                                                    
                                                
    784
        function global_erc7540_4_redeem_ASSERTION_ERC7540_4_REDEEM(uint256 amt) public stateless {
    785
    ✓ 105.5K
            actor = _getActor();
    786
    ✓ 105.4K
            t(
    787
    ✓ 105.5K
                erc7540_4_redeem(address(superVault), amt),
    788
    ✓ 105.4K
                ASSERTION_ERC7540_4_REDEEM
    789
            );
    790
        }
    791
    
                                                    
                                                
    792
        /// @dev Property 7540-5: requestRedeem reverts if the share balance is less than amount
    793
        function global_erc7540_5_ASSERTION_ERC7540_5(uint256 shares) public stateless {
    794
    ✓ 112.6K
            actor = _getActor();
    795
    ✓ 112.1K
            t(
    796
    ✓ 112.6K
                erc7540_5(address(superVault), address(superVault), shares),
    797
    ✓ 112.1K
                ASSERTION_ERC7540_5
    798
            );
    799
        }
    800
    
                                                    
                                                
    801
        /// @dev Property 7540-6: preview* always reverts
    802
        // NOTE: previewMint and previewDeposit don't revert because deposits are sync
    803
        // function crytic_erc7540_6() public {
    804
        //     actor = _getActor();
    805
        //     t(
    806
        //         erc7540_6(address(superVault)),
    807
        //         "ERC7540-6: preview* functions should always revert"
    808
        //     );
    809
        // }
    810
    
                                                    
                                                
    811
        /// @dev Property 7540-7: if max[method] > 0, then [method] (max) should not revert
    812
        // NOTE: maxDeposit always returns type(uint256).max
    813
        // function crytic_erc7540_7_deposit(uint256 amt) public {
    814
        //     actor = _getActor();
    815
        //     t(
    816
        //         erc7540_7_deposit(address(superVault), amt),
    817
        //         "ERC7540-7: deposit should not revert when amount <= max"
    818
        //     );
    819
        // }
    820
    
                                                    
                                                
    821
        // NOTE: maxMint always returns type(uint256).max
    822
        // function crytic_erc7540_7_mint(uint256 amt) public stateless {
    823
        //     actor = _getActor();
    824
        //     t(
    825
        //         erc7540_7_mint(address(superVault), amt),
    826
        //         "ERC7540-7: mint should not revert when amount <= max"
    827
        //     );
    828
        // }
    829
    
                                                    
                                                
    830
        function global_erc7540_7_withdraw_ASSERTION_ERC7540_7_WITHDRAW(uint256 amt) public stateless {
    831
    ✓ 165.2K
            actor = _getActor();
    832
    ✓ 165.2K
            t(
    833
    ✓ 165.2K
                erc7540_7_withdraw(address(superVault), amt),
    834
    ✓ 165.2K
                ASSERTION_ERC7540_7_WITHDRAW
    835
            );
    836
        }
    837
    
                                                    
                                                
    838
        // NOTE: this implements the check from ERC7540Properties directly because SuperVault logic implementation allows amt to be nonzero but round down to 0 when assets passed into redeem are calculated
    839
        function global_erc7540_7_redeem_ASSERTION_ERC7540_7_REDEEM(uint256 amt) public stateless {
    840
    ✓ 221.7K
            actor = _getActor();
    841
    
                                                    
                                                
    842
    ✓ 221.7K
            uint256 maxRedeem = superVault.maxRedeem(actor);
    843
    ✓ 221.7K
            amt = between(amt, 0, maxRedeem);
    844
    
                                                    
                                                
    845
    ✓ 221.7K
            if (amt == 0) {
    846
    ✓ 156.6K
                return; // Skip
    847
            }
    848
    
                                                    
                                                
    849
    ✓ 65.1K
            uint256 averageWithdrawPrice = superVaultStrategy
    850
    ✓ 65.1K
                .getAverageWithdrawPrice(actor);
    851
    
                                                    
                                                
    852
            // calculates assets in the same way as redeem to confirm that a nonzero amount is being requested
    853
    ✓ 65.1K
            uint256 assets = amt.mulDiv(
    854
    ✓ 65.1K
                averageWithdrawPrice,
    855
    ✓ 65.1K
                superVault.PRECISION(),
    856
    ✓ 65.1K
                Math.Rounding.Floor
    857
            );
    858
    
                                                    
                                                
    859
    ✓ 65.1K
            try superVault.redeem(amt, actor, actor) {} catch {
    860
    ✓ 31.6K
                if (amt > 0 && assets > 0) {
    861
    ✓ 31.6K
                    t(
    862
    ✓ 31.6K
                        false,
    863
    ✓ 31.6K
                        ASSERTION_ERC7540_7_REDEEM
    864
                    );
    865
                }
    866
            }
    867
        }
    868
    }
    869
    
                                                    
                                                
    99.3% test/recon/Setup.sol
    Lines covered: 150 / 151 (99.3%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    // External dependencies
    5
    import {BaseSetup} from "@chimera/BaseSetup.sol";
    6
    import {vm} from "@chimera/Hevm.sol";
    7
    import {ActorManager} from "@recon/ActorManager.sol";
    8
    import {AssetManager} from "@recon/AssetManager.sol";
    9
    import {Utils} from "@recon/Utils.sol";
    10
    // ERC4626 Hooks
    11
    import {Deposit4626VaultHook} from "lib/v2-core/src/hooks/vaults/4626/Deposit4626VaultHook.sol";
    12
    import {ApproveAndDeposit4626VaultHook} from "lib/v2-core/src/hooks/vaults/4626/ApproveAndDeposit4626VaultHook.sol";
    13
    import {Redeem4626VaultHook} from "lib/v2-core/src/hooks/vaults/4626/Redeem4626VaultHook.sol";
    14
    
                                                    
                                                
    15
    // ERC5115 Hooks
    16
    import {Deposit5115VaultHook} from "lib/v2-core/src/hooks/vaults/5115/Deposit5115VaultHook.sol";
    17
    import {ApproveAndDeposit5115VaultHook} from "lib/v2-core/src/hooks/vaults/5115/ApproveAndDeposit5115VaultHook.sol";
    18
    import {Redeem5115VaultHook} from "lib/v2-core/src/hooks/vaults/5115/Redeem5115VaultHook.sol";
    19
    
                                                    
                                                
    20
    // ERC7540 Hooks
    21
    import {Deposit7540VaultHook} from "lib/v2-core/src/hooks/vaults/7540/Deposit7540VaultHook.sol";
    22
    import {Redeem7540VaultHook} from "lib/v2-core/src/hooks/vaults/7540/Redeem7540VaultHook.sol";
    23
    import {RequestDeposit7540VaultHook} from "lib/v2-core/src/hooks/vaults/7540/RequestDeposit7540VaultHook.sol";
    24
    import {RequestRedeem7540VaultHook} from "lib/v2-core/src/hooks/vaults/7540/RequestRedeem7540VaultHook.sol";
    25
    import {ApproveAndRequestDeposit7540VaultHook} from "lib/v2-core/src/hooks/vaults/7540/ApproveAndRequestDeposit7540VaultHook.sol";
    26
    import {CancelDepositRequest7540Hook} from "lib/v2-core/src/hooks/vaults/7540/CancelDepositRequest7540Hook.sol";
    27
    import {CancelRedeemRequest7540Hook} from "lib/v2-core/src/hooks/vaults/7540/CancelRedeemRequest7540Hook.sol";
    28
    import {ClaimCancelDepositRequest7540Hook} from "lib/v2-core/src/hooks/vaults/7540/ClaimCancelDepositRequest7540Hook.sol";
    29
    import {ClaimCancelRedeemRequest7540Hook} from "lib/v2-core/src/hooks/vaults/7540/ClaimCancelRedeemRequest7540Hook.sol";
    30
    import {Withdraw7540VaultHook} from "lib/v2-core/src/hooks/vaults/7540/Withdraw7540VaultHook.sol";
    31
    
                                                    
                                                
    32
    // Super Vault Hooks
    33
    import {CancelRedeemHook} from "lib/v2-core/src/hooks/vaults/super-vault/CancelRedeemHook.sol";
    34
    import {Withdraw7540VaultHook as SuperVaultWithdraw7540VaultHook} from "lib/v2-core/src/hooks/vaults/super-vault/Withdraw7540VaultHook.sol";
    35
    
                                                    
                                                
    36
    // Source dependencies
    37
    import "src/SuperVault/SuperVault.sol";
    38
    import "src/SuperVault/SuperVaultAggregator.sol";
    39
    import "src/SuperVault/SuperVaultEscrow.sol";
    40
    import "src/SuperVault/SuperVaultStrategy.sol";
    41
    import "src/SuperGovernor.sol";
    42
    import {ISuperVaultAggregator} from "src/interfaces/SuperVault/ISuperVaultAggregator.sol";
    43
    import {ISuperVaultStrategy} from "src/interfaces/SuperVault/ISuperVaultStrategy.sol";
    44
    import {MockYieldSourceOracle} from "test/mocks/MockYieldSourceOracle.sol";
    45
    
                                                    
                                                
    46
    // Test suite dependencies
    47
    import {YieldManager, YieldSourceType} from "test/recon/managers/YieldManager.sol";
    48
    import {MerkleTestHelper} from "test/recon/helpers/MerkleTestHelper.sol";
    49
    import {UnsafeSuperVaultAggregator} from "test/recon/helpers/UnsafeSuperVaultAggregator.sol";
    50
    import {MockERC4626YieldSourceOracle} from "test/recon/mocks/MockERC4626YieldSourceOracle.sol";
    51
    import {MockERC5115YieldSourceOracle} from "test/recon/mocks/MockERC5115YieldSourceOracle.sol";
    52
    import {MockERC7540YieldSourceOracle} from "test/recon/mocks/MockERC7540YieldSourceOracle.sol";
    53
    import {MockECDSAPPSOracle} from "test/recon/mocks/MockECDSAPPSOracle.sol";
    54
    
                                                    
                                                
    55
    abstract contract Setup is
    56
        BaseSetup,
    57
        ActorManager,
    58
        AssetManager,
    59
        YieldManager,
    60
        Utils
    61
    {
    62
        // Configuration constants
    63
    ✓ 1
        uint8 internal constant DECIMALS = 18;
    64
        address asset;
    65
    ✓ 1
        address feeRecipient = address(0xbeef);
    66
    
                                                    
                                                
    67
        // Core contracts
    68
        SuperGovernor superGovernor;
    69
        SuperVault superVault;
    70
        UnsafeSuperVaultAggregator superVaultAggregator;
    71
        SuperVaultEscrow superVaultEscrow;
    72
        SuperVaultStrategy superVaultStrategy;
    73
    
                                                    
                                                
    74
        // Implementation contracts for aggregator
    75
        SuperVault vaultImpl;
    76
        SuperVaultStrategy strategyImpl;
    77
        SuperVaultEscrow escrowImpl;
    78
    
                                                    
                                                
    79
        // Helpers
    80
        MerkleTestHelper merkleHelper;
    81
    
                                                    
                                                
    82
        // ERC4626 Hooks
    83
        ApproveAndDeposit4626VaultHook approveAndDeposit4626Hook;
    84
        Deposit4626VaultHook deposit4626Hook;
    85
        Redeem4626VaultHook redeem4626Hook;
    86
    
                                                    
                                                
    87
        // ERC5115 Hooks
    88
        ApproveAndDeposit5115VaultHook approveAndDeposit5115Hook;
    89
        Deposit5115VaultHook deposit5115Hook;
    90
        Redeem5115VaultHook redeem5115Hook;
    91
    
                                                    
                                                
    92
        // ERC7540 Hooks
    93
        Deposit7540VaultHook deposit7540Hook;
    94
        Redeem7540VaultHook redeem7540Hook;
    95
        RequestDeposit7540VaultHook requestDeposit7540Hook;
    96
        RequestRedeem7540VaultHook requestRedeem7540Hook;
    97
        ApproveAndRequestDeposit7540VaultHook approveAndRequestDeposit7540Hook;
    98
        CancelDepositRequest7540Hook cancelDepositRequest7540Hook;
    99
        CancelRedeemRequest7540Hook cancelRedeemRequest7540Hook;
    100
        ClaimCancelDepositRequest7540Hook claimCancelDepositRequest7540Hook;
    101
        ClaimCancelRedeemRequest7540Hook claimCancelRedeemRequest7540Hook;
    102
        Withdraw7540VaultHook withdraw7540Hook;
    103
    
                                                    
                                                
    104
        // Super Vault Hooks
    105
        CancelRedeemHook cancelRedeemHook;
    106
        SuperVaultWithdraw7540VaultHook superVaultWithdraw7540Hook;
    107
    
                                                    
                                                
    108
        // Mocks
    109
        MockERC4626YieldSourceOracle erc4626YieldSourceOracle;
    110
        MockERC5115YieldSourceOracle erc5115YieldSourceOracle;
    111
        MockERC7540YieldSourceOracle erc7540YieldSourceOracle;
    112
        MockECDSAPPSOracle ECDSAPPSOracle;
    113
    
                                                    
                                                
    114
        // Yield sources for different standards
    115
        address erc4626YieldSource;
    116
        address erc5115YieldSource;
    117
        address erc7540YieldSource;
    118
    
                                                    
                                                
    119
        // Ghosts
    120
        bool hasUpdatedPPS;
    121
        int256 burnedMoreThanRequested;
    122
        int256 burnedLessThanRequested;
    123
        int256 previewMintSharesGreater; // setPreviewSharesGreater
    124
        int256 previewDepositSharesGreater; // setPreviewSharesGreater
    125
        int256 previewMintAssetsGreater; // setpreviewAssetsGreater
    126
        int256 previewDepositAssetsGreater; // setpreviewAssetsGreater
    127
    
                                                    
                                                
    128
        // Canaries
    129
        bool executeHooksClampedSuccess;
    130
        bool executeHooksSuccess;
    131
        bool fulfillRedeemRequestsSuccess;
    132
        bool hasDeployedNewVault;
    133
    
                                                    
                                                
    134
        /// === MODIFIERS === ///
    135
        /// Prank admin and actor
    136
    
                                                    
                                                
    137
        modifier asAdmin() {
    138
    ✓ 240.1K
            vm.prank(address(this));
    139
            _;
    140
        }
    141
    
                                                    
                                                
    142
        modifier asActor() {
    143
    ✓ 741.6K
            vm.prank(address(_getActor()));
    144
            _;
    145
        }
    146
    
                                                    
                                                
    147
        /// Makes a handler have no side effects
    148
        /// The fuzzer will call this anyway, and because it reverts it will be removed from shrinking
    149
        /// Replace the "withGhosts" with "stateless" to make the code clean
    150
        ///
    151
        /// Under Foundry with `assertions_revert = false` a failing t()/vm.assert* only records a
    152
        /// journaled write to the global fail slot, which the trailing revert would erase — making
    153
        /// every stateless assertion property invisible to the Foundry leg. Re-raise a failure
    154
        /// recorded during this call as Panic(0x01), which Foundry classifies as an assertion
    155
        /// failure; under echidna/medusa/recon a failing t() already reverted with Panic before
    156
        /// reaching this point, so the extra check never fires there.
    157
        modifier stateless() {
    158
    ✓ 221.7K
            bool preFailed = _foundryFailSlotSet();
    159
            _;
    160
    ✓ 1.1M
            if (!preFailed && _foundryFailSlotSet()) assert(false);
    161
    ✓ 1.1M
    ⟲ 1.1M
            revert("stateless");
    162
        }
    163
    
                                                    
                                                
    164
        // bytes32("failed") — Foundry's GLOBAL_FAIL_SLOT on the cheatcode address, written
    165
        // (journaled sstore of 1) by non-reverting vm.assert* failures.
    166
        bytes32 internal constant _FOUNDRY_FAIL_SLOT =
    167
            0x6661696c65640000000000000000000000000000000000000000000000000000;
    168
    
                                                    
                                                
    169
        /// Probes the Foundry fail slot with a raw staticcall so fuzzers without the `load`
    170
        /// cheatcode simply report "not set" instead of reverting.
    171
    ✓ 3.4M
        function _foundryFailSlotSet() internal view returns (bool) {
    172
    ✓ 3.4M
            (bool ok, bytes memory ret) = address(vm).staticcall(
    173
    ✓ 3.4M
                abi.encodeWithSignature("load(address,bytes32)", address(vm), _FOUNDRY_FAIL_SLOT)
    174
            );
    175
    ✓ 3.4M
            return ok && ret.length >= 32 && abi.decode(ret, (bytes32)) != bytes32(0);
    176
        }
    177
    
                                                    
                                                
    178
        /// === Setup === ///
    179
        /// This contains all calls to be performed in the tester constructor, both for Echidna and Foundry
    180
        function setup() internal virtual override {
    181
            // 1. Add additional actors
    182
    ✓ 1
            _addActor(address(0x100)); // Actor 1
    183
    ✓ 1
            _addActor(address(0x200)); // Actor 2
    184
    
                                                    
                                                
    185
            // 2. Create assets using AssetManager
    186
    ✓ 1
            _newAsset(DECIMALS); // Deploy token with 18 decimals
    187
    ✓ 1
            _newAsset(DECIMALS); // UP token
    188
    
                                                    
                                                
    189
    ✓ 1
            _switchAsset(0);
    190
    
                                                    
                                                
    191
            // 3. Deploy all three types of yield sources using YieldManager
    192
            // Deploy ERC4626 yield source (default)
    193
    ✓ 1
            erc4626YieldSource = _newYieldSource(
    194
    ✓ 1
                _getAsset(),
    195
    ✓ 1
                YieldSourceType.ERC4626
    196
            );
    197
    
                                                    
                                                
    198
            // Deploy ERC5115 yield source
    199
    ✓ 1
            erc5115YieldSource = _newYieldSource(
    200
    ✓ 1
                _getAsset(),
    201
    ✓ 1
                YieldSourceType.ERC5115
    202
            );
    203
    
                                                    
                                                
    204
            // Deploy ERC7540 yield source
    205
    ✓ 1
            erc7540YieldSource = _newYieldSource(
    206
    ✓ 1
                _getAsset(),
    207
    ✓ 1
                YieldSourceType.ERC7540
    208
            );
    209
    
                                                    
                                                
    210
            // Set ERC4626 as the default active yield source
    211
    ✓ 1
            _switchYieldSource(0); // Switch to first yield source in the array (ERC4626)
    212
    
                                                    
                                                
    213
            // 4. Deploy SuperGovernor first (required by other contracts)
    214
    ✓ 1
            superGovernor = new SuperGovernor(
    215
    ✓ 1
                address(this), // superGovernor role
    216
    ✓ 1
                address(this), // governor role
    217
    ✓ 1
                address(this), // bankManager role
    218
    ✓ 1
                address(this), // gasManager role
    219
    ✓ 1
                feeRecipient, // treasury
    220
    ✓ 1
                address(this) // prover
    221
            );
    222
    
                                                    
                                                
    223
            // 5. Deploy implementation contracts for the aggregator
    224
    ✓ 1
            vaultImpl = new SuperVault(address(superGovernor));
    225
    ✓ 1
            strategyImpl = new SuperVaultStrategy(address(superGovernor));
    226
    ✓ 1
            escrowImpl = new SuperVaultEscrow();
    227
    
                                                    
                                                
    228
            // 6. Deploy SuperVaultAggregator with implementation contracts
    229
    ✓ 1
            superVaultAggregator = new UnsafeSuperVaultAggregator(
    230
    ✓ 1
                address(superGovernor),
    231
    ✓ 1
                address(vaultImpl),
    232
    ✓ 1
                address(strategyImpl),
    233
                address(escrowImpl)
    234
            );
    235
    
                                                    
                                                
    236
            // 7. Register the SuperVaultAggregator and UpToken address with SuperGovernor
    237
    ✓ 1
            superGovernor.setAddress(
    238
    ✓ 1
                superGovernor.SUPER_VAULT_AGGREGATOR(),
    239
    ✓ 1
                address(superVaultAggregator)
    240
            );
    241
    
                                                    
                                                
    242
    ✓ 1
            address[] memory assets = _getAssets();
    243
    ✓ 1
            superGovernor.setAddress(superGovernor.UP(), assets[1]); // the second deployed token in the AssetManager is the UPToken
    244
    ✓ 1
            superGovernor.setAddress(superGovernor.SUPER_BANK(), address(this));
    245
    
                                                    
                                                
    246
            // 8. Deploy Mocks and Oracles
    247
    
                                                    
                                                
    248
            // Deploy specific oracles for each yield source type
    249
    ✓ 1
            erc4626YieldSourceOracle = new MockERC4626YieldSourceOracle();
    250
    ✓ 1
            erc5115YieldSourceOracle = new MockERC5115YieldSourceOracle();
    251
    ✓ 1
            erc7540YieldSourceOracle = new MockERC7540YieldSourceOracle();
    252
    ✓ 1
            ECDSAPPSOracle = new MockECDSAPPSOracle();
    253
    
                                                    
                                                
    254
            // ECDSAPPSOracle setup
    255
    ✓ 1
            superGovernor.setActivePPSOracle(address(ECDSAPPSOracle));
    256
    ✓ 1
            ECDSAPPSOracle.setSUPER_GOVERNORReturn(address(superVaultAggregator));
    257
    
                                                    
                                                
    258
            // Set valid assets for all oracles
    259
    ✓ 1
            asset = _getAsset();
    260
    ✓ 1
            erc4626YieldSourceOracle.setValidAsset(asset, true);
    261
    ✓ 1
            erc5115YieldSourceOracle.setValidAsset(asset, true);
    262
    ✓ 1
            erc7540YieldSourceOracle.setValidAsset(asset, true);
    263
    
                                                    
                                                
    264
            // 9. Create a vault trio using the aggregator
    265
    ✓ 1
            ISuperVaultAggregator.VaultCreationParams
    266
                memory params = ISuperVaultAggregator.VaultCreationParams({
    267
    ✓ 1
                    asset: _getAsset(), // Use the token created by AssetManager
    268
                    name: "SuperVault",
    269
                    symbol: "SV",
    270
    ✓ 1
                    mainManager: address(this), // CONFIGURABLE: This parameter can be modified via target functions
    271
    ✓ 1
                    secondaryManagers: new address[](0), // CONFIGURABLE: This parameter can be modified via target functions
    272
    ✓ 1
                    minUpdateInterval: 5, // CONFIGURABLE: This parameter can be modified via target functions
    273
    ✓ 1
                    maxStaleness: 300, // CONFIGURABLE: This parameter can be modified via target functions
    274
                    feeConfig: ISuperVaultStrategy.FeeConfig({
    275
    ✓ 1
                        performanceFeeBps: 1000, // 10% performance fee
    276
    ✓ 1
                        managementFeeBps: 100, // 1% management fee
    277
    ✓ 1
                        recipient: feeRecipient
    278
                    })
    279
                });
    280
    
                                                    
                                                
    281
            (
    282
                address vaultAddr,
    283
                address strategyAddr,
    284
                address escrowAddr
    285
    ✓ 1
            ) = superVaultAggregator.createVault(params);
    286
    
                                                    
                                                
    287
            // 10. Store the deployed contracts
    288
    ✓ 1
            superVault = SuperVault(vaultAddr);
    289
    ✓ 1
            superVaultStrategy = SuperVaultStrategy(payable(strategyAddr));
    290
    ✓ 1
            superVaultEscrow = SuperVaultEscrow(escrowAddr);
    291
    
                                                    
                                                
    292
            /// 11. Deploy all hook contracts and helper
    293
    ✓ 1
            merkleHelper = new MerkleTestHelper();
    294
    
                                                    
                                                
    295
            // Deploy ERC4626 Hooks
    296
    ✓ 1
            approveAndDeposit4626Hook = new ApproveAndDeposit4626VaultHook();
    297
    ✓ 1
            deposit4626Hook = new Deposit4626VaultHook();
    298
    ✓ 1
            redeem4626Hook = new Redeem4626VaultHook();
    299
    
                                                    
                                                
    300
            // Deploy ERC5115 Hooks
    301
    ✓ 1
            approveAndDeposit5115Hook = new ApproveAndDeposit5115VaultHook();
    302
    ✓ 1
            deposit5115Hook = new Deposit5115VaultHook();
    303
    ✓ 1
            redeem5115Hook = new Redeem5115VaultHook();
    304
    
                                                    
                                                
    305
            // Deploy ERC7540 Hooks
    306
    ✓ 1
            deposit7540Hook = new Deposit7540VaultHook();
    307
    ✓ 1
            redeem7540Hook = new Redeem7540VaultHook();
    308
    ✓ 1
            requestDeposit7540Hook = new RequestDeposit7540VaultHook();
    309
    ✓ 1
            requestRedeem7540Hook = new RequestRedeem7540VaultHook();
    310
    ✓ 1
            approveAndRequestDeposit7540Hook = new ApproveAndRequestDeposit7540VaultHook();
    311
    ✓ 1
            cancelDepositRequest7540Hook = new CancelDepositRequest7540Hook();
    312
    ✓ 1
            cancelRedeemRequest7540Hook = new CancelRedeemRequest7540Hook();
    313
    ✓ 1
            claimCancelDepositRequest7540Hook = new ClaimCancelDepositRequest7540Hook();
    314
    ✓ 1
            claimCancelRedeemRequest7540Hook = new ClaimCancelRedeemRequest7540Hook();
    315
    ✓ 1
            withdraw7540Hook = new Withdraw7540VaultHook();
    316
    
                                                    
                                                
    317
            // Deploy Super Vault Hooks
    318
    ✓ 1
            cancelRedeemHook = new CancelRedeemHook();
    319
    ✓ 1
            superVaultWithdraw7540Hook = new SuperVaultWithdraw7540VaultHook();
    320
    
                                                    
                                                
    321
            // Register all hooks with SuperGovernor
    322
            // ERC4626 Hooks (deposit hooks are regular hooks, redeem hooks are fulfill request hooks)
    323
    ✓ 1
            superGovernor.registerHook(address(approveAndDeposit4626Hook), false);
    324
    ✓ 1
            superGovernor.registerHook(address(deposit4626Hook), false);
    325
    ✓ 1
            superGovernor.registerHook(address(redeem4626Hook), true); // fulfill request hook
    326
    
                                                    
                                                
    327
            // ERC5115 Hooks
    328
    ✓ 1
            superGovernor.registerHook(address(approveAndDeposit5115Hook), false);
    329
    ✓ 1
            superGovernor.registerHook(address(deposit5115Hook), false);
    330
    ✓ 1
            superGovernor.registerHook(address(redeem5115Hook), true); // fulfill request hook
    331
    
                                                    
                                                
    332
            // ERC7540 Hooks (deposit/request hooks are regular hooks, redeem/withdraw hooks are fulfill request hooks)
    333
    ✓ 1
            superGovernor.registerHook(address(deposit7540Hook), false);
    334
    ✓ 1
            superGovernor.registerHook(address(redeem7540Hook), true); // fulfill request hook
    335
    ✓ 1
            superGovernor.registerHook(address(requestDeposit7540Hook), false);
    336
    ✓ 1
            superGovernor.registerHook(address(requestRedeem7540Hook), false);
    337
    ✓ 1
            superGovernor.registerHook(
    338
    ✓ 1
                address(approveAndRequestDeposit7540Hook),
    339
                false
    340
            );
    341
    ✓ 1
            superGovernor.registerHook(
    342
    ✓ 1
                address(cancelDepositRequest7540Hook),
    343
                false
    344
            );
    345
    ✓ 1
            superGovernor.registerHook(address(cancelRedeemRequest7540Hook), false);
    346
    ✓ 1
            superGovernor.registerHook(
    347
    ✓ 1
                address(claimCancelDepositRequest7540Hook),
    348
                false
    349
            );
    350
    ✓ 1
            superGovernor.registerHook(
    351
    ✓ 1
                address(claimCancelRedeemRequest7540Hook),
    352
                false
    353
            );
    354
    ✓ 1
            superGovernor.registerHook(address(withdraw7540Hook), true); // fulfill request hook
    355
    
                                                    
                                                
    356
            // Super Vault Hooks
    357
    ✓ 1
            superGovernor.registerHook(address(cancelRedeemHook), false);
    358
    ✓ 1
            superGovernor.registerHook(address(superVaultWithdraw7540Hook), true); // fulfill request hook
    359
    
                                                    
                                                
    360
            // 12. Set up approval array for contracts that need token access
    361
    ✓ 1
            address[] memory approvalArray = new address[](6);
    362
    ✓ 1
            approvalArray[0] = address(superVault);
    363
    ✓ 1
            approvalArray[1] = address(superVaultStrategy);
    364
    ✓ 1
            approvalArray[2] = address(superVaultAggregator);
    365
    ✓ 1
            approvalArray[3] = erc4626YieldSource;
    366
    ✓ 1
            approvalArray[4] = erc5115YieldSource;
    367
    ✓ 1
            approvalArray[5] = erc7540YieldSource;
    368
    
                                                    
                                                
    369
            // 13. Finalize asset deployment (mints to actors and sets approvals)
    370
    ✓ 1
            _finalizeAssetDeployment(_getActors(), approvalArray, type(uint88).max);
    371
        }
    372
    
                                                    
                                                
    373
        /// Get hook addresses for different yield source types
    374
    
                                                    
                                                
    375
        function _getApproveAndDepositHookForType(
    376
            YieldSourceType sourceType
    377
    ✓ 50.1K
        ) internal view returns (address) {
    378
    ✓ 50.1K
            if (sourceType == YieldSourceType.ERC4626) {
    379
    ✓ 43.5K
                return address(approveAndDeposit4626Hook);
    380
    ✓ 6.6K
            } else if (sourceType == YieldSourceType.ERC5115) {
    381
    ✓ 2.4K
                return address(approveAndDeposit5115Hook);
    382
    ✓ 4.2K
            } else if (sourceType == YieldSourceType.ERC7540) {
    383
    ✓ 4.2K
                return address(approveAndRequestDeposit7540Hook);
    384
            }
    385
            return address(0);
    386
        }
    387
    
                                                    
                                                
    388
        function _getRedeemHookForType(
    389
            YieldSourceType sourceType
    390
    ✓ 746.5K
        ) internal view returns (address) {
    391
    ✓ 746.5K
            if (sourceType == YieldSourceType.ERC4626) {
    392
    ✓ 617.7K
                return address(redeem4626Hook);
    393
    ✓ 128.8K
            } else if (sourceType == YieldSourceType.ERC5115) {
    394
    ✓ 76.2K
                return address(redeem5115Hook);
    395
    ✓ 52.6K
            } else if (sourceType == YieldSourceType.ERC7540) {
    396
    ✓ 52.6K
                return address(redeem7540Hook);
    397
            }
    398
            return address(0);
    399
        }
    400
    
                                                    
                                                
    401
        /// Get oracle addresses for different yield source types
    402
    
                                                    
                                                
    403
        function _getYieldSourceOracleForType(
    404
            YieldSourceType sourceType
    405
    ✓ 448.7K
        ) internal view returns (address) {
    406
    ✓ 448.7K
            if (sourceType == YieldSourceType.ERC4626) {
    407
    ✓ 322.9K
                return address(erc4626YieldSourceOracle);
    408
    ✓ 125.7K
            } else if (sourceType == YieldSourceType.ERC5115) {
    409
    ✓ 88.3K
                return address(erc5115YieldSourceOracle);
    410
    ✓ 37.4K
            } else if (sourceType == YieldSourceType.ERC7540) {
    411
    ✓ 37.4K
                return address(erc7540YieldSourceOracle);
    412
            }
    413
        }
    414
    
                                                    
                                                
    415
        /// @dev Helper function to determine yield source type from address
    416
        function _getYieldSourceTypeFromAddress(
    417
            address yieldSource
    418
    ✓ 1.0M
        ) internal view returns (YieldSourceType) {
    419
            // Get all available yield sources from YieldManager
    420
    ✓ 1.0M
            address[] memory yieldSources = _getYieldSources();
    421
    
                                                    
                                                
    422
            // Check which index matches the current yield source
    423
    ✓ 1.5M
            for (uint256 i = 0; i < yieldSources.length; i++) {
    424
    ✓ 1.4M
                if (yieldSources[i] == yieldSource) {
    425
                    // Return the corresponding type based on creation order:
    426
                    // Index 0: ERC4626, Index 1: ERC5115, Index 2: ERC7540
    427
    ✓ 1.0M
                    if (i == 0) return YieldSourceType.ERC4626;
    428
    ✓ 219.0K
                    if (i == 1) return YieldSourceType.ERC5115;
    429
    ✓ 129.9K
                    if (i == 2) return YieldSourceType.ERC7540;
    430
                }
    431
            }
    432
    
                                                    
                                                
    433
            // Default to ERC4626 if not found
    434
    ✓ 57.7K
            return YieldSourceType.ERC4626;
    435
        }
    436
    
                                                    
                                                
    437
        function _getERC4626YieldSourceOracle() internal view returns (address) {
    438
            return address(erc4626YieldSourceOracle);
    439
        }
    440
    
                                                    
                                                
    441
        function _getERC5115YieldSourceOracle() internal view returns (address) {
    442
            return address(erc5115YieldSourceOracle);
    443
        }
    444
    
                                                    
                                                
    445
        function _getERC7540YieldSourceOracle() internal view returns (address) {
    446
            return address(erc7540YieldSourceOracle);
    447
        }
    448
    
                                                    
                                                
    449
        // Helpers
    450
    ✓ 545.7K
        function _getRandomActor(uint256 entropy) public view returns (address) {
    451
    ✓ 545.7K
            address[] memory actors = _getActors();
    452
    
                                                    
                                                
    453
    ✓ 545.7K
            return actors[entropy % actors.length];
    454
        }
    455
    }
    456
    
                                                    
                                                
    0.0% test/recon/TargetFunctions.sol
    Lines covered: 0 / 0 (0.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    // Chimera deps
    5
    import {vm} from "@chimera/Hevm.sol";
    6
    
                                                    
                                                
    7
    // Helpers
    8
    import {Panic} from "@recon/Panic.sol";
    9
    
                                                    
                                                
    10
    // Targets
    11
    // NOTE: Always import and apply them in alphabetical order, so much easier to debug!
    12
    import {AdminTargets} from "./targets/AdminTargets.sol";
    13
    import {DoomsdayTargets} from "./targets/DoomsdayTargets.sol";
    14
    import {ManagersTargets} from "./targets/ManagersTargets.sol";
    15
    import {OracleTargets} from "./targets/OracleTargets.sol";
    16
    import {SuperVaultTargets} from "./targets/SuperVaultTargets.sol";
    17
    import {SuperVaultAggregatorTargets} from "./targets/SuperVaultAggregatorTargets.sol";
    18
    import {SuperVaultEscrowTargets} from "./targets/SuperVaultEscrowTargets.sol";
    19
    import {SuperVaultStrategyTargets} from "./targets/SuperVaultStrategyTargets.sol";
    20
    import {SuperGovernorTargets} from "./targets/SuperGovernorTargets.sol";
    21
    import {YieldSourceTargets} from "./targets/YieldSourceTargets.sol";
    22
    
                                                    
                                                
    23
    abstract contract TargetFunctions is
    24
        AdminTargets,
    25
        DoomsdayTargets,
    26
        ManagersTargets,
    27
        OracleTargets,
    28
        SuperVaultTargets,
    29
        SuperVaultAggregatorTargets,
    30
        SuperVaultEscrowTargets,
    31
        SuperVaultStrategyTargets,
    32
        SuperGovernorTargets,
    33
        YieldSourceTargets
    34
    {
    35
        /// CUSTOM TARGET FUNCTIONS - Add your own target functions here ///
    36
        /// AUTO GENERATED TARGET FUNCTIONS - WARNING: DO NOT DELETE OR MODIFY THIS LINE ///
    37
    }
    38
    
                                                    
                                                
    72.4% test/recon/helpers/MerkleTestHelper.sol
    Lines covered: 21 / 29 (72.4%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
    5
    
                                                    
                                                
    6
    /// @title MerkleTestHelper
    7
    /// @notice Helper contract for generating test Merkle roots and proofs for hook validation
    8
    ✓ 1
    contract MerkleTestHelper {
    9
        /// @notice Generate a test Merkle root for ERC4626 deposit and redeem hooks
    10
        /// @param depositHook Address of the Deposit4626VaultHook or ApproveAndDeposit4626VaultHook contract
    11
        /// @param redeemHook Address of the Redeem4626VaultHook contract  
    12
        /// @param mockVault Address of the mock ERC4626 vault
    13
        /// @param mockToken Address of the mock token (unused but kept for compatibility)
    14
        /// @return root The Merkle root for the tree
    15
        /// @return proofs Array of proofs for each leaf [depositProof, redeemProof]
    16
        function generateTestHooksRoot(
    17
            address depositHook,
    18
            address redeemHook,
    19
            address mockVault,
    20
            address mockToken
    21
    ✓ 100.3K
        ) public pure returns (bytes32 root, bytes32[][] memory proofs) {
    22
            // Create leaves for the two hooks following the exact format from SuperVaultAggregator._createLeaf()
    23
    ✓ 100.3K
            bytes32[] memory leaves = new bytes32[](2);
    24
            
    25
            // Leaf 1: Deposit hook with yield source only (what inspect() returns)
    26
            // The inspect() function only returns abi.encodePacked(yieldSource)
    27
    ✓ 100.3K
            bytes memory depositArgs = abi.encodePacked(mockVault); // Only yield source address
    28
    ✓ 100.3K
            leaves[0] = keccak256(bytes.concat(keccak256(abi.encode(depositHook, depositArgs))));
    29
            
    30
            // Leaf 2: Redeem hook with yield source only (what inspect() returns)
    31
            // The inspect() function only returns abi.encodePacked(yieldSource)
    32
    ✓ 100.3K
            bytes memory redeemArgs = abi.encodePacked(mockVault); // Only yield source address
    33
    ✓ 100.3K
            leaves[1] = keccak256(bytes.concat(keccak256(abi.encode(redeemHook, redeemArgs))));
    34
            
    35
            // Sort leaves to match standard Merkle tree ordering
    36
    ✓ 100.3K
            if (leaves[0] > leaves[1]) {
    37
    ✓ 30.4K
                (leaves[0], leaves[1]) = (leaves[1], leaves[0]);
    38
            }
    39
            
    40
            // For a 2-leaf tree, root is hash of the sorted leaves
    41
    ✓ 100.3K
            root = keccak256(abi.encodePacked(leaves[0], leaves[1]));
    42
            
    43
            // Store original leaf hashes for proof assignment
    44
    ✓ 100.3K
            bytes32 depositLeaf = keccak256(bytes.concat(keccak256(abi.encode(depositHook, depositArgs))));
    45
    ✓ 100.3K
            bytes32 redeemLeaf = keccak256(bytes.concat(keccak256(abi.encode(redeemHook, redeemArgs))));
    46
            
    47
            // Generate proofs for each leaf
    48
    ✓ 100.3K
            proofs = new bytes32[][](2);
    49
    ✓ 100.3K
            proofs[0] = new bytes32[](1); // Proof for deposit hook
    50
    ✓ 100.3K
            proofs[1] = new bytes32[](1); // Proof for redeem hook
    51
            
    52
            // In a 2-leaf tree, each leaf's proof is just its sibling
    53
            // Find which position each leaf ended up in after sorting
    54
    ✓ 100.3K
            if (leaves[0] == depositLeaf) {
    55
                // depositHook is first leaf after sorting
    56
    ✓ 69.9K
                proofs[0][0] = leaves[1]; // Sibling of deposit leaf
    57
    ✓ 69.9K
                proofs[1][0] = leaves[0]; // Sibling of redeem leaf  
    58
            } else {
    59
                // redeemHook is first leaf after sorting
    60
    ✓ 30.4K
                proofs[0][0] = leaves[1]; // Sibling of deposit leaf
    61
    ✓ 30.4K
                proofs[1][0] = leaves[0]; // Sibling of redeem leaf
    62
            }
    63
            
    64
    ✓ 100.3K
            return (root, proofs);
    65
        }
    66
        
    67
        /// @notice Generate encoded hook arguments for Deposit4626VaultHook
    68
        /// @param yieldSource Address of the yield source vault
    69
        /// @param amount Amount to deposit
    70
        /// @param usePrevHookAmount Whether to use previous hook amount
    71
        /// @return Encoded hook arguments
    72
        function encodeDepositHookArgs(
    73
            address yieldSource,
    74
            uint256 amount,
    75
            bool usePrevHookAmount
    76
        ) public pure returns (bytes memory) {
    77
            return abi.encodePacked(
    78
                bytes32(0), // yieldSourceOracleId placeholder
    79
                yieldSource,
    80
                amount,
    81
                usePrevHookAmount
    82
            );
    83
        }
    84
        
    85
        /// @notice Generate encoded hook arguments for Redeem4626VaultHook
    86
        /// @param yieldSource Address of the yield source vault
    87
        /// @param owner Address of the owner
    88
        /// @param shares Number of shares to redeem
    89
        /// @param usePrevHookAmount Whether to use previous hook amount
    90
        /// @return Encoded hook arguments
    91
        function encodeRedeemHookArgs(
    92
            address yieldSource,
    93
            address owner,
    94
            uint256 shares,
    95
            bool usePrevHookAmount
    96
        ) public pure returns (bytes memory) {
    97
            return abi.encodePacked(
    98
                bytes32(0), // yieldSourceOracleId placeholder
    99
                yieldSource,
    100
                owner,
    101
                shares,
    102
                usePrevHookAmount
    103
            );
    104
        }
    105
        
    106
        /// @notice Generate encoded hook arguments for ApproveAndDeposit4626VaultHook
    107
        /// @param yieldSource Address of the yield source vault
    108
        /// @param token Address of the token to approve and deposit
    109
        /// @param amount Amount to deposit
    110
        /// @param usePrevHookAmount Whether to use previous hook amount
    111
        /// @return Encoded hook arguments
    112
        function encodeApproveAndDepositHookArgs(
    113
            address yieldSource,
    114
            address token,
    115
            uint256 amount,
    116
            bool usePrevHookAmount
    117
        ) public pure returns (bytes memory) {
    118
            return abi.encodePacked(
    119
                bytes32(0), // yieldSourceOracleId placeholder
    120
                yieldSource,
    121
                token,
    122
                amount,
    123
                usePrevHookAmount
    124
            );
    125
        }
    126
        
    127
        /// @notice Create a leaf hash for a specific hook and arguments
    128
        /// @param hookAddress Address of the hook contract
    129
        /// @param hookArgs Encoded hook arguments
    130
        /// @return leaf The leaf hash
    131
        function createLeaf(address hookAddress, bytes memory hookArgs) public pure returns (bytes32 leaf) {
    132
            return keccak256(bytes.concat(keccak256(abi.encode(hookAddress, hookArgs))));
    133
        }
    134
        
    135
        /// @notice Verify a Merkle proof against a root
    136
        /// @param proof Array of proof elements
    137
        /// @param root Merkle root
    138
        /// @param leaf Leaf to verify
    139
        /// @return True if proof is valid
    140
        function verifyProof(bytes32[] memory proof, bytes32 root, bytes32 leaf) public pure returns (bool) {
    141
            return MerkleProof.verify(proof, root, leaf);
    142
        }
    143
    }
    100.0% test/recon/helpers/UnsafeSuperVaultAggregator.sol
    Lines covered: 5 / 5 (100.0%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity 0.8.30;
    3
    
                                                    
                                                
    4
    import {SuperVaultAggregator} from "src/SuperVault/SuperVaultAggregator.sol";
    5
    
                                                    
                                                
    6
    /// @dev inherits from aggregator to override hook validation
    7
    contract UnsafeSuperVaultAggregator is SuperVaultAggregator {
    8
        constructor(
    9
            address superGovernor_,
    10
            address vaultImpl_,
    11
            address strategyImpl_,
    12
            address escrowImpl_
    13
        )
    14
            SuperVaultAggregator(
    15
    ✓ 1
                superGovernor_,
    16
    ✓ 1
                vaultImpl_,
    17
    ✓ 1
                strategyImpl_,
    18
    ✓ 1
                escrowImpl_
    19
            )
    20
        {}
    21
    
                                                    
                                                
    22
        function validateHook(
    23
            address strategy,
    24
            ValidateHookArgs calldata args
    25
        ) external view override returns (bool isValid) {
    26
    ✓ 729.9K
            return true;
    27
        }
    28
    }
    29
    
                                                    
                                                
    88.5% test/recon/managers/YieldManager.sol
    Lines covered: 23 / 26 (88.5%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseSetup} from "@chimera/BaseSetup.sol";
    5
    import {vm} from "@chimera/Hevm.sol";
    6
    
                                                    
                                                
    7
    import {EnumerableSet} from "@recon/EnumerableSet.sol";
    8
    import {MockERC20} from "@recon/MockERC20.sol";
    9
    
                                                    
                                                
    10
    import {MockERC4626Tester} from "../mocks/MockERC4626Tester.sol";
    11
    import {MockERC5115Tester} from "../mocks/MockERC5115Tester.sol";
    12
    import {MockERC7540Tester} from "../mocks/MockERC7540Tester.sol";
    13
    
                                                    
                                                
    14
    /// @dev Source of truth for the yield sources being used in the test
    15
    /// @notice No yield sources should be used in the test suite without being added from here first
    16
    /// @notice Enum to specify the type of yield source to deploy
    17
    enum YieldSourceType {
    18
        ERC4626,
    19
        ERC5115,
    20
        ERC7540
    21
    }
    22
    
                                                    
                                                
    23
    abstract contract YieldManager {
    24
        using EnumerableSet for EnumerableSet.AddressSet;
    25
    
                                                    
                                                
    26
        /// @notice The current target for this set of variables
    27
        address private __yieldSource;
    28
    
                                                    
                                                
    29
        /// @notice The current yield source type
    30
        YieldSourceType private __currentYieldSourceType;
    31
    
                                                    
                                                
    32
        /// @notice The list of all yield sources being used
    33
        EnumerableSet.AddressSet private _yieldSources;
    34
    
                                                    
                                                
    35
        // If the current target is address(0) then it has not been setup yet and should revert
    36
        error YieldSourceNotSetup();
    37
        // Do not allow duplicates
    38
        error YieldSourceExists();
    39
        // Enable only added yield sources
    40
        error YieldSourceNotAdded();
    41
        // Invalid yield source type
    42
        error InvalidYieldSourceType();
    43
    
                                                    
                                                
    44
        /// @notice Returns the current active yield source
    45
    ✓ 6.5M
        function _getYieldSource() internal view returns (address) {
    46
    ✓ 6.5M
            if (__yieldSource == address(0)) {
    47
                revert YieldSourceNotSetup();
    48
            }
    49
    
                                                    
                                                
    50
    ✓ 6.5M
            return __yieldSource;
    51
        }
    52
    
                                                    
                                                
    53
        /// @notice Returns all yield sources being used
    54
    ✓ 7.8M
        function _getYieldSources() internal view returns (address[] memory) {
    55
    ✓ 7.8M
            return _yieldSources.values();
    56
        }
    57
    
                                                    
                                                
    58
        /// @notice Returns the current yield source type
    59
        function _getCurrentYieldSourceType() internal view returns (YieldSourceType) {
    60
    ✓ 381.3K
            return __currentYieldSourceType;
    61
        }
    62
    
                                                    
                                                
    63
        /// @notice Creates a new yield source and adds it to the list of yield sources
    64
        /// @param asset The asset to create the yield source for
    65
        /// @param yieldSourceType The type of yield source to deploy
    66
        /// @return The address of the new yield source
    67
    ✓ 115.2K
        function _newYieldSource(address asset, YieldSourceType yieldSourceType) internal returns (address) {
    68
            address yieldSource_;
    69
            
    70
    ✓ 115.2K
            if (yieldSourceType == YieldSourceType.ERC4626) {
    71
    ✓ 115.2K
                yieldSource_ = address(new MockERC4626Tester(asset));
    72
    ✓ 2
            } else if (yieldSourceType == YieldSourceType.ERC5115) {
    73
    ✓ 1
                yieldSource_ = address(new MockERC5115Tester(asset));
    74
    ✓ 1
            } else if (yieldSourceType == YieldSourceType.ERC7540) {
    75
    ✓ 1
                yieldSource_ = address(new MockERC7540Tester(asset));
    76
            } else {
    77
                revert InvalidYieldSourceType();
    78
            }
    79
            
    80
    ✓ 115.2K
            _addYieldSource(yieldSource_);
    81
    ✓ 115.2K
            __yieldSource = yieldSource_; // sets the yield source as the current yield source
    82
    ✓ 115.2K
            __currentYieldSourceType = yieldSourceType;
    83
    ✓ 115.2K
            return yieldSource_;
    84
        }
    85
    
                                                    
                                                
    86
        /// @notice Creates a new yield source with ERC4626 by default (backward compatibility)
    87
        /// @param asset The asset to create the yield source for
    88
        /// @return The address of the new yield source
    89
        function _newYieldSource(address asset) internal returns (address) {
    90
            return _newYieldSource(asset, YieldSourceType.ERC4626);
    91
        }
    92
    
                                                    
                                                
    93
        /// @notice Legacy function name for backward compatibility
    94
        /// @param asset The asset to create the yield source for
    95
        /// @return The address of the new yield source
    96
    ✓ 115.2K
        function _newVault(address asset) internal returns (address) {
    97
    ✓ 115.2K
            return _newYieldSource(asset, YieldSourceType.ERC4626);
    98
        }
    99
    
                                                    
                                                
    100
        /// @notice Adds a yield source to the list of yield sources
    101
        /// @param target The address of the yield source to add
    102
        function _addYieldSource(address target) internal {
    103
    ✓ 115.2K
            if (_yieldSources.contains(target)) {
    104
                revert YieldSourceExists();
    105
            }
    106
    
                                                    
                                                
    107
    ✓ 115.2K
            _yieldSources.add(target);
    108
        }
    109
    
                                                    
                                                
    110
        /// @notice Legacy function name for backward compatibility
    111
        /// @param target The address of the yield source to add
    112
        function _addVault(address target) internal {
    113
            _addYieldSource(target);
    114
        }
    115
    
                                                    
                                                
    116
        /// @notice Removes a yield source from the list of yield sources
    117
        /// @param target The address of the yield source to remove
    118
        function _removeYieldSource(address target) internal {
    119
            if (!_yieldSources.contains(target)) {
    120
                revert YieldSourceNotAdded();
    121
            }
    122
    
                                                    
                                                
    123
            _yieldSources.remove(target);
    124
        }
    125
    
                                                    
                                                
    126
        /// @notice Legacy function name for backward compatibility
    127
        /// @param target The address of the yield source to remove
    128
        function _removeVault(address target) internal {
    129
            _removeYieldSource(target);
    130
        }
    131
    
                                                    
                                                
    132
        /// @notice Switches the current yield source based on the entropy
    133
        /// @param entropy The entropy to choose a random yield source in the array for switching
    134
        function _switchYieldSource(uint256 entropy) internal {
    135
    ✓ 648.6K
            address target = _yieldSources.at(entropy % _yieldSources.length());
    136
    ✓ 648.6K
            __yieldSource = target;
    137
        }
    138
    
                                                    
                                                
    139
        /// @notice Legacy function name for backward compatibility
    140
        /// @param entropy The entropy to choose a random yield source in the array for switching
    141
        function _switchVault(uint256 entropy) internal {
    142
            _switchYieldSource(entropy);
    143
        }
    144
    
                                                    
                                                
    145
        /// @notice Switches to a different yield source type by deploying a new yield source
    146
        /// @param asset The asset to create the new yield source for
    147
        /// @param newYieldSourceType The new yield source type to switch to
    148
        /// @return The address of the new yield source
    149
        function _switchYieldSourceType(address asset, YieldSourceType newYieldSourceType) internal returns (address) {
    150
            return _newYieldSource(asset, newYieldSourceType);
    151
        }
    152
    }
    153
    
                                                    
                                                
    34.4% test/recon/mocks/MockECDSAPPSOracle.sol
    Lines covered: 11 / 32 (34.4%)
    1
    // SPDX-License-Identifier: UNLICENSED
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {ISuperVaultAggregator} from "src/interfaces/SuperVault/ISuperVaultAggregator.sol";
    5
    import {IECDSAPPSOracle} from "src/interfaces/oracles/IECDSAPPSOracle.sol";
    6
    
                                                    
                                                
    7
    ✓ 1
    contract MockECDSAPPSOracle {
    8
        //<>=============================================================<>
    9
        //||                                                             ||
    10
        //||                    NON-VIEW FUNCTIONS                       ||
    11
        //||                                                             ||
    12
        //<>=============================================================<>
    13
        // Mock implementation of updatePPS
    14
        function updatePPS(IECDSAPPSOracle.UpdatePPSArgs memory args) public {
    15
    ✓ 529.1K
            ISuperVaultAggregator.ForwardPPSArgs
    16
                memory forwardArgs = ISuperVaultAggregator.ForwardPPSArgs({
    17
    ✓ 529.1K
                    strategies: args.strategies,
    18
    ✓ 529.1K
                    ppss: args.ppss,
    19
    ✓ 529.1K
                    ppsStdevs: args.ppsStdevs,
    20
    ✓ 529.1K
                    validatorSets: args.validatorSets,
    21
    ✓ 529.1K
                    totalValidators: args.totalValidators,
    22
    ✓ 529.1K
                    timestamps: args.timestamps,
    23
    ✓ 529.1K
                    updateAuthority: msg.sender
    24
                });
    25
    
                                                    
                                                
    26
    ✓ 529.1K
            ISuperVaultAggregator(_SUPER_GOVERNORReturn_0).forwardPPS(forwardArgs);
    27
        }
    28
    
                                                    
                                                
    29
        //<>=============================================================<>
    30
        //||                                                             ||
    31
        //||                    SETTER FUNCTIONS                         ||
    32
        //||                                                             ||
    33
        //<>=============================================================<>
    34
        // Function to set return values for SUPER_GOVERNOR
    35
        function setSUPER_GOVERNORReturn(address _value0) public {
    36
    ✓ 1
            _SUPER_GOVERNORReturn_0 = _value0;
    37
        }
    38
    
                                                    
                                                
    39
        // Function to set return values for UPDATE_PPS_TYPEHASH
    40
        function setUPDATE_PPS_TYPEHASHReturn(bytes32 _value0) public {
    41
            _UPDATE_PPS_TYPEHASHReturn_0 = _value0;
    42
        }
    43
    
                                                    
                                                
    44
        // Function to set return values for domainSeparator
    45
        function setDomainSeparatorReturn(bytes32 _value0) public {
    46
            _domainSeparatorReturn_0 = _value0;
    47
        }
    48
    
                                                    
                                                
    49
        /*******************************************************************
    50
         *   ⚠️ WARNING ⚠️ WARNING ⚠️ WARNING ⚠️ WARNING ⚠️ WARNING ⚠️  *
    51
         *-----------------------------------------------------------------*
    52
         *      Generally you only need to modify the sections above.      *
    53
         *          The code below handles system operations.              *
    54
         *******************************************************************/
    55
    
                                                    
                                                
    56
        //<>=============================================================<>
    57
        //||                                                             ||
    58
        //||        ⚠️  STRUCT DEFINITIONS - DO NOT MODIFY  ⚠️          ||
    59
        //||                                                             ||
    60
        //<>=============================================================<>
    61
        // Struct definition removed - using IECDSAPPSOracle.UpdatePPSArgs from interface
    62
    
                                                    
                                                
    63
        //<>=============================================================<>
    64
        //||                                                             ||
    65
        //||        ⚠️  EVENTS DEFINITIONS - DO NOT MODIFY  ⚠️          ||
    66
        //||                                                             ||
    67
        //<>=============================================================<>
    68
        event EIP712DomainChanged();
    69
        event PPSValidated(
    70
            address strategy,
    71
            uint256 pps,
    72
            uint256 ppsStdev,
    73
            uint256 validatorSet,
    74
            uint256 totalValidators,
    75
            uint256 timestamp,
    76
            address sender
    77
        );
    78
    
                                                    
                                                
    79
        //<>=============================================================<>
    80
        //||                                                             ||
    81
        //||         ⚠️  INTERNAL STORAGE - DO NOT MODIFY  ⚠️           ||
    82
        //||                                                             ||
    83
        //<>=============================================================<>
    84
        address private _SUPER_GOVERNORReturn_0;
    85
        bytes32 private _UPDATE_PPS_TYPEHASHReturn_0;
    86
        bytes32 private _domainSeparatorReturn_0;
    87
        bytes1 private _eip712DomainReturn_0;
    88
        string private _eip712DomainReturn_1;
    89
        string private _eip712DomainReturn_2;
    90
        uint256 private _eip712DomainReturn_3;
    91
        address private _eip712DomainReturn_4;
    92
        bytes32 private _eip712DomainReturn_5;
    93
        uint256[] private _eip712DomainReturn_6;
    94
        uint256 private _nonceReturn_0;
    95
    
                                                    
                                                
    96
        //<>=============================================================<>
    97
        //||                                                             ||
    98
        //||          ⚠️  VIEW FUNCTIONS - DO NOT MODIFY  ⚠️            ||
    99
        //||                                                             ||
    100
        //<>=============================================================<>
    101
        // Mock implementation of SUPER_GOVERNOR
    102
        function SUPER_GOVERNOR() public view returns (address) {
    103
            return _SUPER_GOVERNORReturn_0;
    104
        }
    105
    
                                                    
                                                
    106
        // Mock implementation of UPDATE_PPS_TYPEHASH
    107
        function UPDATE_PPS_TYPEHASH() public view returns (bytes32) {
    108
            return _UPDATE_PPS_TYPEHASHReturn_0;
    109
        }
    110
    
                                                    
                                                
    111
        // Mock implementation of domainSeparator
    112
        function domainSeparator() public view returns (bytes32) {
    113
            return _domainSeparatorReturn_0;
    114
        }
    115
    
                                                    
                                                
    116
        // Mock implementation of eip712Domain
    117
        function eip712Domain()
    118
            public
    119
            view
    120
            returns (
    121
                bytes1,
    122
                string memory,
    123
                string memory,
    124
                uint256,
    125
                address,
    126
                bytes32,
    127
                uint256[] memory
    128
            )
    129
        {
    130
            return (
    131
                _eip712DomainReturn_0,
    132
                _eip712DomainReturn_1,
    133
                _eip712DomainReturn_2,
    134
                _eip712DomainReturn_3,
    135
                _eip712DomainReturn_4,
    136
                _eip712DomainReturn_5,
    137
                _eip712DomainReturn_6
    138
            );
    139
        }
    140
    
                                                    
                                                
    141
        // Mock implementation of nonce
    142
        function nonce() public view returns (uint256) {
    143
            return _nonceReturn_0;
    144
        }
    145
    }
    146
    
                                                    
                                                
    91.3% test/recon/mocks/MockERC4626Tester.sol
    Lines covered: 84 / 92 (91.3%)
    1
    // SPDX-License-Identifier: MIT
    2
    // Heavily inspired by https://github.com/liquity/V2-gov/blob/9632de9a988522775336d9b60cdf2542efc600db/test/mocks/MaliciousInitiative.sol
    3
    pragma solidity ^0.8.0;
    4
    
                                                    
                                                
    5
    import {MockERC20} from "@recon/MockERC20.sol";
    6
    
                                                    
                                                
    7
    abstract contract ERC4626 is MockERC20 {
    8
    ✓ 399.2K
        MockERC20 public immutable asset;
    9
    
                                                    
                                                
    10
        event Deposit(
    11
            address indexed caller,
    12
            address indexed owner,
    13
            uint256 assets,
    14
            uint256 shares
    15
        );
    16
        event Withdraw(
    17
            address indexed caller,
    18
            address indexed receiver,
    19
            address indexed owner,
    20
            uint256 assets,
    21
            uint256 shares
    22
        );
    23
    
                                                    
                                                
    24
    ✓ 115.2K
        constructor(MockERC20 _asset) MockERC20("MockERC4626Tester", "MCT", 18) {
    25
    ✓ 115.2K
            asset = _asset;
    26
        }
    27
    
                                                    
                                                
    28
        function deposit(
    29
            uint256 assets,
    30
            address receiver
    31
    ✓ 43.5K
        ) public virtual returns (uint256) {
    32
    ✓ 43.5K
            uint256 shares = previewDeposit(assets);
    33
    ✓ 43.2K
            _deposit(msg.sender, receiver, assets, shares);
    34
            return shares;
    35
        }
    36
    
                                                    
                                                
    37
        function mint(
    38
            uint256 shares,
    39
            address receiver
    40
    ✓ 336.4K
        ) public virtual returns (uint256) {
    41
    ✓ 336.4K
            uint256 assets = previewMint(shares);
    42
    ✓ 329.3K
            _deposit(msg.sender, receiver, assets, shares);
    43
            return assets;
    44
        }
    45
    
                                                    
                                                
    46
        function withdraw(
    47
            uint256 assets,
    48
            address receiver,
    49
            address owner
    50
        ) public virtual returns (uint256) {
    51
            uint256 shares = previewWithdraw(assets);
    52
            _withdraw(msg.sender, receiver, owner, assets, shares);
    53
            return shares;
    54
        }
    55
    
                                                    
                                                
    56
        function redeem(
    57
            uint256 shares,
    58
            address receiver,
    59
            address owner
    60
        ) public virtual returns (uint256) {
    61
            uint256 assets = previewRedeem(shares);
    62
            _withdraw(msg.sender, receiver, owner, assets, shares);
    63
            return assets;
    64
        }
    65
    
                                                    
                                                
    66
    ✓ 873.9K
        function totalAssets() public view virtual returns (uint256) {
    67
    ✓ 873.9K
            return asset.balanceOf(address(this));
    68
        }
    69
    
                                                    
                                                
    70
        function convertToShares(
    71
            uint256 assets
    72
    ✓ 578.2K
        ) public view virtual returns (uint256) {
    73
    ✓ 578.2K
            uint256 supply = totalSupply;
    74
    ✓ 578.2K
            return supply == 0 ? assets : (assets * supply) / totalAssets();
    75
        }
    76
    
                                                    
                                                
    77
        function convertToAssets(
    78
            uint256 shares
    79
    ✓ 830.8K
        ) public view virtual returns (uint256) {
    80
    ✓ 830.8K
            uint256 supply = totalSupply;
    81
    ✓ 830.8K
            return supply == 0 ? shares : (shares * totalAssets()) / supply;
    82
        }
    83
    
                                                    
                                                
    84
        function previewDeposit(
    85
            uint256 assets
    86
    ✓ 493.2K
        ) public view virtual returns (uint256) {
    87
    ✓ 493.2K
            return convertToShares(assets);
    88
        }
    89
    
                                                    
                                                
    90
        function previewMint(uint256 shares) public view virtual returns (uint256) {
    91
            uint256 supply = totalSupply;
    92
            return supply == 0 ? shares : (shares * totalAssets()) / supply;
    93
        }
    94
    
                                                    
                                                
    95
        function previewWithdraw(
    96
            uint256 assets
    97
        ) public view virtual returns (uint256) {
    98
            uint256 supply = totalSupply;
    99
            return supply == 0 ? assets : (assets * supply) / totalAssets();
    100
        }
    101
    
                                                    
                                                
    102
        function previewRedeem(
    103
            uint256 shares
    104
    ✓ 481.9K
        ) public view virtual returns (uint256) {
    105
    ✓ 481.9K
            return convertToAssets(shares);
    106
        }
    107
    
                                                    
                                                
    108
        function maxDeposit(address) public view virtual returns (uint256) {
    109
    ✓ 8.4K
            return type(uint256).max;
    110
        }
    111
    
                                                    
                                                
    112
        function maxMint(address) public view virtual returns (uint256) {
    113
            return type(uint256).max;
    114
        }
    115
    
                                                    
                                                
    116
    ✓ 12.5K
        function maxWithdraw(address owner) public view virtual returns (uint256) {
    117
    ✓ 12.5K
            return convertToAssets(balanceOf[owner]);
    118
        }
    119
    
                                                    
                                                
    120
    ✓ 5.3K
        function maxRedeem(address owner) public view virtual returns (uint256) {
    121
    ✓ 5.3K
            return balanceOf[owner];
    122
        }
    123
    
                                                    
                                                
    124
        function _deposit(
    125
            address caller,
    126
            address receiver,
    127
            uint256 assets,
    128
            uint256 shares
    129
        ) internal virtual {
    130
    ✓ 372.4K
            asset.transferFrom(caller, address(this), assets);
    131
    ✓ 290.4K
            _mint(receiver, shares);
    132
    ✓ 290.4K
            emit Deposit(caller, receiver, assets, shares);
    133
        }
    134
    
                                                    
                                                
    135
        function _withdraw(
    136
            address caller,
    137
            address receiver,
    138
            address owner,
    139
            uint256 assets,
    140
            uint256 shares
    141
        ) internal virtual {
    142
    ✓ 525.2K
            if (caller != owner) {
    143
    ✓ 125.5K
                allowance[owner][caller] -= shares;
    144
            }
    145
    ✓ 407.5K
            _burn(owner, shares);
    146
    ✓ 388.6K
            asset.transfer(receiver, assets);
    147
    ✓ 388.6K
            emit Withdraw(caller, receiver, owner, assets, shares);
    148
        }
    149
    }
    150
    
                                                    
                                                
    151
    enum FunctionType {
    152
        NONE,
    153
        DEPOSIT,
    154
        MINT,
    155
        WITHDRAW,
    156
        REDEEM
    157
    }
    158
    
                                                    
                                                
    159
    enum RevertType {
    160
        NONE,
    161
        THROW,
    162
        OOG,
    163
        RETURN_BOMB,
    164
        REVERT_BOMB
    165
    }
    166
    
                                                    
                                                
    167
    /// @dev This will use the simplest possible implementation initially to allow getting coverage and will be expanded on as necessary for testing potentially more interesting behaviors
    168
    /// @dev Note that blindspots not testable with this current implementation are covered in the ERC4626-integrations.md file
    169
    contract MockERC4626Tester is ERC4626 {
    170
        mapping(FunctionType => RevertType) public revertBehaviours;
    171
    
                                                    
                                                
    172
        uint8 public decimalsOffset;
    173
        /// @dev Track total losses
    174
        uint256 public totalLosses;
    175
        uint256 public totalGains;
    176
        uint256 public lossOnWithdraw;
    177
    ✓ 115.2K
        uint256 public MAX_BPS = 10_000;
    178
    
                                                    
                                                
    179
    ✓ 115.2K
        constructor(address _asset) ERC4626(MockERC20(_asset)) {}
    180
    
                                                    
                                                
    181
        /// Standard ERC4626 functions ///
    182
    
                                                    
                                                
    183
        /// @dev Deposit assets, reverts as specified
    184
        function deposit(
    185
            uint256 assets,
    186
            address receiver
    187
    ✓ 45.5K
        ) public override returns (uint256) {
    188
    ✓ 45.5K
            _performRevertBehaviour(revertBehaviours[FunctionType.DEPOSIT]);
    189
    ✓ 43.5K
            return super.deposit(assets, receiver);
    190
        }
    191
    
                                                    
                                                
    192
        /// @dev Mint shares, reverts as specified
    193
        function mint(
    194
            uint256 shares,
    195
            address receiver
    196
    ✓ 336.4K
        ) public override returns (uint256) {
    197
    ✓ 336.4K
            _performRevertBehaviour(revertBehaviours[FunctionType.MINT]);
    198
    ✓ 336.4K
            return super.mint(shares, receiver);
    199
        }
    200
    
                                                    
                                                
    201
        /// @dev Withdraw assets, reverts as specified
    202
        function withdraw(
    203
            uint256 assets,
    204
            address receiver,
    205
            address owner
    206
    ✓ 78.0K
        ) public override returns (uint256) {
    207
    ✓ 78.0K
            _performRevertBehaviour(revertBehaviours[FunctionType.WITHDRAW]);
    208
    
                                                    
                                                
    209
    ✓ 78.0K
            uint256 shares = previewWithdraw(assets);
    210
    ✓ 70.6K
            uint256 lossyAssets = assets - ((assets * lossOnWithdraw) / MAX_BPS);
    211
    ✓ 64.8K
            _withdraw(msg.sender, receiver, owner, lossyAssets, shares);
    212
    
                                                    
                                                
    213
    ✓ 4.7K
            return shares;
    214
        }
    215
    
                                                    
                                                
    216
        /// @dev Redeem shares, reverts as specified
    217
        function redeem(
    218
            uint256 shares,
    219
            address receiver,
    220
            address owner
    221
    ✓ 481.9K
        ) public override returns (uint256) {
    222
    ✓ 481.9K
            _performRevertBehaviour(revertBehaviours[FunctionType.REDEEM]);
    223
    
                                                    
                                                
    224
    ✓ 481.9K
            uint256 assets = previewRedeem(shares);
    225
    ✓ 470.2K
            uint256 lossyAssets = assets - ((assets * lossOnWithdraw) / MAX_BPS);
    226
    ✓ 460.4K
            _withdraw(msg.sender, receiver, owner, lossyAssets, shares);
    227
    
                                                    
                                                
    228
    ✓ 383.9K
            return lossyAssets;
    229
        }
    230
    
                                                    
                                                
    231
        /// @dev Preview deposit, reverts as specified
    232
        function previewDeposit(
    233
            uint256 assets
    234
    ✓ 493.2K
        ) public view override returns (uint256) {
    235
    ✓ 493.2K
            _performRevertBehaviour(revertBehaviours[FunctionType.DEPOSIT]);
    236
    ✓ 493.2K
            return super.previewDeposit(assets);
    237
        }
    238
    
                                                    
                                                
    239
        /// @dev Preview mint, reverts as specified
    240
        function previewMint(
    241
            uint256 shares
    242
    ✓ 336.4K
        ) public view override returns (uint256) {
    243
    ✓ 336.4K
            _performRevertBehaviour(revertBehaviours[FunctionType.MINT]);
    244
    ✓ 336.4K
            return super.previewMint(shares);
    245
        }
    246
    
                                                    
                                                
    247
        /// @dev Preview withdraw, reverts as specified
    248
        function previewWithdraw(
    249
            uint256 assets
    250
    ✓ 78.0K
        ) public view override returns (uint256) {
    251
    ✓ 2.3M
            _performRevertBehaviour(revertBehaviours[FunctionType.WITHDRAW]);
    252
    ✓ 78.0K
            return super.previewWithdraw(assets);
    253
        }
    254
    
                                                    
                                                
    255
        /// @dev Preview redeem, reverts as specified
    256
        function previewRedeem(
    257
            uint256 shares
    258
    ✓ 481.9K
        ) public view override returns (uint256) {
    259
    ✓ 481.9K
            _performRevertBehaviour(revertBehaviours[FunctionType.REDEEM]);
    260
    ✓ 481.9K
            return super.previewRedeem(shares);
    261
        }
    262
    
                                                    
                                                
    263
        /// @dev Revert in different ways to test the revert behaviour
    264
        function _performRevertBehaviour(RevertType action) internal pure {
    265
    ✓ 2.3M
            if (action == RevertType.THROW) {
    266
                revert("A normal Revert");
    267
            }
    268
    
                                                    
                                                
    269
            // 3 gas per iteration, consider changing to storage changes if traces are cluttered
    270
    ✓ 2.3M
            if (action == RevertType.OOG) {
    271
                uint256 i;
    272
                while (true) {
    273
                    ++i;
    274
                }
    275
            }
    276
    
                                                    
                                                
    277
    ✓ 2.3M
            if (action == RevertType.RETURN_BOMB) {
    278
    ✓ 1.4K
                uint256 _bytes = 2_000_000;
    279
                assembly {
    280
    ✓ 1.4K
                    return(0, _bytes)
    281
                }
    282
            }
    283
    
                                                    
                                                
    284
    ✓ 2.3M
            if (action == RevertType.REVERT_BOMB) {
    285
    ✓ 572
                uint256 _bytes = 2_000_000;
    286
                assembly {
    287
    ⟲ 572
                    revert(0, _bytes)
    288
                }
    289
            }
    290
    
                                                    
                                                
    291
            return; // NONE
    292
        }
    293
    
                                                    
                                                
    294
        /// Custom functions for testing vault behavior ///
    295
    
                                                    
                                                
    296
        /// @dev Specify the revert behavior on each function
    297
        function setRevertBehavior(FunctionType ft, RevertType rt) public {
    298
    ✓ 2.1K
            revertBehaviours[ft] = rt;
    299
        }
    300
    
                                                    
                                                
    301
        /// @dev Simulate a loss on the vault's assets
    302
        function simulateLoss(uint256 lossAmount) external {
    303
    ✓ 85.3K
            MockERC20(asset).transfer(address(0xbeef), lossAmount);
    304
    ✓ 4.6K
            totalLosses += lossAmount;
    305
        }
    306
    
                                                    
                                                
    307
        /// @dev Simulate a gain on the vault's assets (similar to Yearn's profit taking)
    308
        function simulateGain(uint256 gainAmount) external {
    309
    ✓ 96.1K
            MockERC20(asset).transferFrom(msg.sender, address(this), gainAmount);
    310
    ✓ 15.1K
            totalGains += gainAmount;
    311
        }
    312
    
                                                    
                                                
    313
        /// @dev Set the loss on withdraw as percentage of the assets being withdrawn
    314
        function setLossOnWithdraw(uint256 _lossOnWithdraw) public {
    315
    ✓ 83.8K
            _lossOnWithdraw %= MAX_BPS + 1; // clamp to ensure we set a max of 100%
    316
    ✓ 83.8K
            lossOnWithdraw = _lossOnWithdraw;
    317
        }
    318
    
                                                    
                                                
    319
        /// @dev Set the decimal offset. Only possible with no supply.
    320
        function setDecimalsOffset(uint8 targetDecimalsOffset) external {
    321
    ✓ 16.7K
            if (totalSupply != 0) {
    322
    ✓ 401
                revert("Supply is not zero");
    323
            }
    324
    ✓ 16.3K
            decimalsOffset = targetDecimalsOffset;
    325
        }
    326
    }
    327
    
                                                    
                                                
    8.5% test/recon/mocks/MockERC4626YieldSourceOracle.sol
    Lines covered: 4 / 47 (8.5%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.30;
    3
    
                                                    
                                                
    4
    import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
    5
    import {IYieldSourceOracle} from "@superform-v2-core/src/interfaces/accounting/IYieldSourceOracle.sol";
    6
    
                                                    
                                                
    7
    /// @title MockERC4626YieldSourceOracle
    8
    /// @notice Mock oracle for ERC4626 vaults in testing
    9
    ✓ 1
    contract MockERC4626YieldSourceOracle is IYieldSourceOracle {
    10
        mapping(address => bool) public validAssetMap;
    11
        
    12
        function setValidAsset(address asset, bool isValid) external {
    13
    ✓ 223.0K
            validAssetMap[asset] = isValid;
    14
        }
    15
        
    16
        function decimals(address yieldSourceAddress) external view returns (uint8) {
    17
            return IERC4626(yieldSourceAddress).decimals();
    18
        }
    19
        
    20
        function getShareOutput(
    21
            address yieldSourceAddress,
    22
            address,
    23
            uint256 assetsIn
    24
    ✓ 454.4K
        ) external view returns (uint256) {
    25
    ✓ 454.4K
            return IERC4626(yieldSourceAddress).previewDeposit(assetsIn);
    26
        }
    27
        
    28
        function getAssetOutput(
    29
            address yieldSourceAddress,
    30
            address,
    31
            uint256 sharesIn
    32
        ) public view returns (uint256) {
    33
            return IERC4626(yieldSourceAddress).previewRedeem(sharesIn);
    34
        }
    35
        
    36
        function getPricePerShare(address yieldSourceAddress) external view returns (uint256) {
    37
            IERC4626 yieldSource = IERC4626(yieldSourceAddress);
    38
            uint256 _decimals = yieldSource.decimals();
    39
            return yieldSource.convertToAssets(10 ** _decimals);
    40
        }
    41
        
    42
        function getBalanceOfOwner(
    43
            address yieldSourceAddress,
    44
            address ownerOfShares
    45
        ) external view returns (uint256) {
    46
            return IERC4626(yieldSourceAddress).balanceOf(ownerOfShares);
    47
        }
    48
        
    49
        function getTVLByOwnerOfShares(
    50
            address yieldSourceAddress,
    51
            address ownerOfShares
    52
        ) external view returns (uint256) {
    53
            uint256 shares = IERC4626(yieldSourceAddress).balanceOf(ownerOfShares);
    54
            return IERC4626(yieldSourceAddress).convertToAssets(shares);
    55
        }
    56
        
    57
        function getTVL(address yieldSourceAddress) external view returns (uint256) {
    58
            return IERC4626(yieldSourceAddress).totalAssets();
    59
        }
    60
        
    61
        function getPricePerShareMultiple(
    62
            address[] memory yieldSourceAddresses
    63
        ) external view returns (uint256[] memory) {
    64
            uint256[] memory prices = new uint256[](yieldSourceAddresses.length);
    65
            for (uint256 i = 0; i < yieldSourceAddresses.length; i++) {
    66
                IERC4626 yieldSource = IERC4626(yieldSourceAddresses[i]);
    67
                uint256 _decimals = yieldSource.decimals();
    68
                prices[i] = yieldSource.convertToAssets(10 ** _decimals);
    69
            }
    70
            return prices;
    71
        }
    72
        
    73
        function getTVLByOwnerOfSharesMultiple(
    74
            address[] memory yieldSourceAddresses,
    75
            address[][] memory ownersOfShares
    76
        ) external view returns (uint256[][] memory) {
    77
            uint256[][] memory result = new uint256[][](yieldSourceAddresses.length);
    78
            for (uint256 i = 0; i < yieldSourceAddresses.length; i++) {
    79
                result[i] = new uint256[](ownersOfShares[i].length);
    80
                for (uint256 j = 0; j < ownersOfShares[i].length; j++) {
    81
                    uint256 shares = IERC4626(yieldSourceAddresses[i]).balanceOf(ownersOfShares[i][j]);
    82
                    result[i][j] = IERC4626(yieldSourceAddresses[i]).convertToAssets(shares);
    83
                }
    84
            }
    85
            return result;
    86
        }
    87
        
    88
        function getTVLMultiple(
    89
            address[] memory yieldSourceAddresses
    90
        ) external view returns (uint256[] memory) {
    91
            uint256[] memory tvls = new uint256[](yieldSourceAddresses.length);
    92
            for (uint256 i = 0; i < yieldSourceAddresses.length; i++) {
    93
                tvls[i] = IERC4626(yieldSourceAddresses[i]).totalAssets();
    94
            }
    95
            return tvls;
    96
        }
    97
        
    98
        function isValidUnderlyingAsset(address, address asset) external view returns (bool) {
    99
            return validAssetMap[asset];
    100
        }
    101
        
    102
        function isValidUnderlyingAssets(
    103
            address[] memory,
    104
            address[] memory assets
    105
        ) external view returns (bool[] memory) {
    106
            bool[] memory validities = new bool[](assets.length);
    107
            for (uint256 i = 0; i < assets.length; i++) {
    108
                validities[i] = validAssetMap[assets[i]];
    109
            }
    110
            return validities;
    111
        }
    112
        
    113
        function getAssetOutputWithFees(
    114
            bytes32,
    115
            address yieldSourceAddress,
    116
            address,
    117
            address,
    118
            uint256 sharesIn
    119
        ) external view returns (uint256) {
    120
            return IERC4626(yieldSourceAddress).previewRedeem(sharesIn);
    121
        }
    122
    }
    22.1% test/recon/mocks/MockERC5115Tester.sol
    Lines covered: 19 / 86 (22.1%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {MockERC20} from "@recon/MockERC20.sol";
    5
    
                                                    
                                                
    6
    abstract contract ERC5115 is MockERC20 {
    7
        MockERC20 public immutable yieldToken;
    8
        address[] public tokensIn;
    9
        address[] public tokensOut;
    10
    
                                                    
                                                
    11
        event Deposit(
    12
            address indexed caller,
    13
            address indexed receiver,
    14
            address indexed tokenIn,
    15
            uint256 amountDeposited,
    16
            uint256 amountSyOut
    17
        );
    18
    
                                                    
                                                
    19
        event Redeem(
    20
            address indexed caller,
    21
            address indexed receiver,
    22
            address indexed tokenOut,
    23
            uint256 amountSyToRedeem,
    24
            uint256 amountTokenOut
    25
        );
    26
    
                                                    
                                                
    27
        constructor(
    28
            MockERC20 _yieldToken
    29
    ✓ 1
        ) MockERC20("MockERC5115Tester", "SY5115", 18) {
    30
    ✓ 1
            yieldToken = _yieldToken;
    31
    ✓ 1
            tokensIn.push(address(_yieldToken));
    32
    ✓ 1
            tokensOut.push(address(_yieldToken));
    33
        }
    34
    
                                                    
                                                
    35
        function deposit(
    36
            address receiver,
    37
            address tokenIn,
    38
            uint256 amountTokenToDeposit,
    39
            uint256 minSharesOut,
    40
            bool depositFromInternalBalance
    41
        ) public virtual returns (uint256 amountSharesOut) {
    42
            amountSharesOut = previewDeposit(tokenIn, amountTokenToDeposit);
    43
    
                                                    
                                                
    44
            MockERC20(tokenIn).transferFrom(
    45
                msg.sender,
    46
                address(this),
    47
                amountTokenToDeposit
    48
            );
    49
            _mint(receiver, amountSharesOut);
    50
    
                                                    
                                                
    51
            emit Deposit(
    52
                msg.sender,
    53
                receiver,
    54
                tokenIn,
    55
                amountTokenToDeposit,
    56
                amountSharesOut
    57
            );
    58
        }
    59
    
                                                    
                                                
    60
        function exchangeRate() public view virtual returns (uint256) {
    61
            uint256 supply = totalSupply;
    62
            if (supply == 0) return 1e18;
    63
            return (yieldToken.balanceOf(address(this)) * 1e18) / supply;
    64
        }
    65
    
                                                    
                                                
    66
        function getTokensIn() public view virtual returns (address[] memory) {
    67
            return tokensIn;
    68
        }
    69
    
                                                    
                                                
    70
        function getTokensOut() public view virtual returns (address[] memory) {
    71
            return tokensOut;
    72
        }
    73
    
                                                    
                                                
    74
        function previewDeposit(
    75
            address tokenIn,
    76
            uint256 amountTokenToDeposit
    77
    ✓ 4.0K
        ) public view virtual returns (uint256 amountSharesOut) {
    78
    ✓ 4.0K
            require(tokenIn == address(yieldToken), "Invalid token");
    79
    ✓ 4.0K
            uint256 supply = totalSupply;
    80
    ✓ 4.0K
            if (supply == 0) {
    81
    ✓ 4.0K
                return amountTokenToDeposit;
    82
            }
    83
            return
    84
                (amountTokenToDeposit * supply) /
    85
                yieldToken.balanceOf(address(this));
    86
        }
    87
    
                                                    
                                                
    88
        function previewRedeem(
    89
            address tokenOut,
    90
            uint256 amountSharesToRedeem
    91
        ) public view virtual returns (uint256 amountTokenOut) {
    92
            require(tokenOut == address(yieldToken), "Invalid token");
    93
            uint256 supply = totalSupply;
    94
            if (supply == 0) {
    95
                return amountSharesToRedeem;
    96
            }
    97
            return
    98
                (amountSharesToRedeem * yieldToken.balanceOf(address(this))) /
    99
                supply;
    100
        }
    101
    }
    102
    
                                                    
                                                
    103
    enum RevertType {
    104
        NONE,
    105
        THROW,
    106
        OOG,
    107
        RETURN_BOMB,
    108
        REVERT_BOMB
    109
    }
    110
    
                                                    
                                                
    111
    contract MockERC5115Tester is ERC5115 {
    112
        RevertType public revertBehaviour;
    113
        uint256 public totalLosses;
    114
        uint256 public totalGains;
    115
        uint256 public lossOnWithdraw;
    116
    ✓ 1
        uint256 public MAX_BPS = 10_000;
    117
    
                                                    
                                                
    118
    ✓ 1
        constructor(address _yieldToken) ERC5115(MockERC20(_yieldToken)) {}
    119
    
                                                    
                                                
    120
        function deposit(
    121
            address receiver,
    122
            address tokenIn,
    123
            uint256 amountTokenToDeposit,
    124
            uint256 minSharesOut,
    125
            bool depositFromInternalBalance
    126
        ) public override returns (uint256 amountSharesOut) {
    127
            _performRevertBehaviour(revertBehaviour);
    128
            return
    129
                super.deposit(
    130
                    receiver,
    131
                    tokenIn,
    132
                    amountTokenToDeposit,
    133
                    minSharesOut,
    134
                    depositFromInternalBalance
    135
                );
    136
        }
    137
    
                                                    
                                                
    138
        function redeem(
    139
            address receiver,
    140
            uint256 amountSharesToRedeem,
    141
            address tokenOut,
    142
            uint256 minTokenOut,
    143
            bool burnFromInternalBalance
    144
        ) public virtual returns (uint256 amountTokenOut) {
    145
            amountTokenOut = previewRedeem(tokenOut, amountSharesToRedeem);
    146
    
                                                    
                                                
    147
            _burn(msg.sender, amountSharesToRedeem);
    148
            uint256 lossyAmountTokenOut = amountTokenOut -
    149
                ((amountTokenOut * lossOnWithdraw) / MAX_BPS);
    150
            MockERC20(tokenOut).transfer(receiver, lossyAmountTokenOut);
    151
    
                                                    
                                                
    152
            emit Redeem(
    153
                msg.sender,
    154
                receiver,
    155
                tokenOut,
    156
                amountSharesToRedeem,
    157
                amountTokenOut
    158
            );
    159
        }
    160
    
                                                    
                                                
    161
        function _performRevertBehaviour(RevertType action) internal pure {
    162
            if (action == RevertType.THROW) {
    163
                revert("A normal Revert");
    164
            }
    165
    
                                                    
                                                
    166
            if (action == RevertType.OOG) {
    167
                uint256 i;
    168
                while (true) {
    169
                    ++i;
    170
                }
    171
            }
    172
    
                                                    
                                                
    173
            if (action == RevertType.RETURN_BOMB) {
    174
                uint256 _bytes = 2_000_000;
    175
                assembly {
    176
                    return(0, _bytes)
    177
                }
    178
            }
    179
    
                                                    
                                                
    180
            if (action == RevertType.REVERT_BOMB) {
    181
                uint256 _bytes = 2_000_000;
    182
                assembly {
    183
                    revert(0, _bytes)
    184
                }
    185
            }
    186
    
                                                    
                                                
    187
            return; // NONE
    188
        }
    189
    
                                                    
                                                
    190
        function setRevertBehavior(RevertType rt) public {
    191
            revertBehaviour = rt;
    192
        }
    193
    
                                                    
                                                
    194
        function simulateLoss(uint256 lossAmount) external {
    195
    ✓ 27.9K
            MockERC20(yieldToken).transfer(address(0xbeef), lossAmount);
    196
    ✓ 416
            totalLosses += lossAmount;
    197
        }
    198
    
                                                    
                                                
    199
        function simulateGain(uint256 gainAmount) external {
    200
    ✓ 19.4K
            MockERC20(yieldToken).transferFrom(
    201
    ✓ 19.4K
                msg.sender,
    202
    ✓ 19.4K
                address(this),
    203
                gainAmount
    204
            );
    205
    ✓ 3.8K
            totalGains += gainAmount;
    206
        }
    207
    
                                                    
                                                
    208
        /// @dev Set the loss on withdraw as percentage of the assets being withdrawn
    209
        function setLossOnWithdraw(uint256 _lossOnWithdraw) public {
    210
    ✓ 28.8K
            _lossOnWithdraw %= MAX_BPS + 1; // clamp to ensure we set a max of 100%
    211
    ✓ 28.8K
            lossOnWithdraw = _lossOnWithdraw;
    212
        }
    213
    
                                                    
                                                
    214
        function increaseYield(uint256 increasePercentageFP4) public {
    215
            require(increasePercentageFP4 <= 10000, "Invalid percentage");
    216
            uint256 amount = (yieldToken.balanceOf(address(this)) *
    217
                increasePercentageFP4) / 10000;
    218
            MockERC20(yieldToken).transferFrom(msg.sender, address(this), amount);
    219
        }
    220
    
                                                    
                                                
    221
        function decreaseYield(uint256 decreasePercentageFP4) public {
    222
            require(decreasePercentageFP4 <= 10000, "Invalid percentage");
    223
            uint256 amount = (yieldToken.balanceOf(address(this)) *
    224
                decreasePercentageFP4) / 10000;
    225
            MockERC20(yieldToken).transfer(address(0xbeef), amount);
    226
            totalLosses += amount;
    227
        }
    228
    }
    229
    
                                                    
                                                
    8.3% test/recon/mocks/MockERC5115YieldSourceOracle.sol
    Lines covered: 4 / 48 (8.3%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.30;
    3
    
                                                    
                                                
    4
    import {IYieldSourceOracle} from "@superform-v2-core/src/interfaces/accounting/IYieldSourceOracle.sol";
    5
    import {MockERC5115Tester} from "./MockERC5115Tester.sol";
    6
    
                                                    
                                                
    7
    /// @title MockERC5115YieldSourceOracle
    8
    /// @notice Mock oracle for ERC5115 (Standardized Yield) vaults in testing
    9
    ✓ 1
    contract MockERC5115YieldSourceOracle is IYieldSourceOracle {
    10
        mapping(address => bool) public validAssetMap;
    11
        
    12
        function setValidAsset(address asset, bool isValid) external {
    13
    ✓ 172.3K
            validAssetMap[asset] = isValid;
    14
        }
    15
        
    16
        function decimals(address) external pure returns (uint8) {
    17
            // ERC5115 always uses 18 decimals
    18
            return 18;
    19
        }
    20
        
    21
        function getShareOutput(
    22
            address yieldSourceAddress,
    23
            address assetIn,
    24
            uint256 assetsIn
    25
    ✓ 11.8K
        ) external view returns (uint256) {
    26
    ✓ 11.8K
            return MockERC5115Tester(yieldSourceAddress).previewDeposit(assetIn, assetsIn);
    27
        }
    28
        
    29
        function getAssetOutput(
    30
            address yieldSourceAddress,
    31
            address assetOut,
    32
            uint256 sharesIn
    33
        ) public view returns (uint256) {
    34
            return MockERC5115Tester(yieldSourceAddress).previewRedeem(assetOut, sharesIn);
    35
        }
    36
        
    37
        function getPricePerShare(address yieldSourceAddress) external view returns (uint256) {
    38
            return MockERC5115Tester(yieldSourceAddress).exchangeRate();
    39
        }
    40
        
    41
        function getBalanceOfOwner(
    42
            address yieldSourceAddress,
    43
            address ownerOfShares
    44
        ) external view returns (uint256) {
    45
            return MockERC5115Tester(yieldSourceAddress).balanceOf(ownerOfShares);
    46
        }
    47
        
    48
        function getTVLByOwnerOfShares(
    49
            address yieldSourceAddress,
    50
            address ownerOfShares
    51
        ) external view returns (uint256) {
    52
            uint256 shares = MockERC5115Tester(yieldSourceAddress).balanceOf(ownerOfShares);
    53
            uint256 exchangeRate = MockERC5115Tester(yieldSourceAddress).exchangeRate();
    54
            return (shares * exchangeRate) / 1e18;
    55
        }
    56
        
    57
        function getTVL(address yieldSourceAddress) external view returns (uint256) {
    58
            uint256 totalShares = MockERC5115Tester(yieldSourceAddress).totalSupply();
    59
            uint256 exchangeRate = MockERC5115Tester(yieldSourceAddress).exchangeRate();
    60
            return (totalShares * exchangeRate) / 1e18;
    61
        }
    62
        
    63
        function getPricePerShareMultiple(
    64
            address[] memory yieldSourceAddresses
    65
        ) external view returns (uint256[] memory) {
    66
            uint256[] memory prices = new uint256[](yieldSourceAddresses.length);
    67
            for (uint256 i = 0; i < yieldSourceAddresses.length; i++) {
    68
                prices[i] = MockERC5115Tester(yieldSourceAddresses[i]).exchangeRate();
    69
            }
    70
            return prices;
    71
        }
    72
        
    73
        function getTVLByOwnerOfSharesMultiple(
    74
            address[] memory yieldSourceAddresses,
    75
            address[][] memory ownersOfShares
    76
        ) external view returns (uint256[][] memory) {
    77
            uint256[][] memory result = new uint256[][](yieldSourceAddresses.length);
    78
            for (uint256 i = 0; i < yieldSourceAddresses.length; i++) {
    79
                result[i] = new uint256[](ownersOfShares[i].length);
    80
                uint256 exchangeRate = MockERC5115Tester(yieldSourceAddresses[i]).exchangeRate();
    81
                for (uint256 j = 0; j < ownersOfShares[i].length; j++) {
    82
                    uint256 shares = MockERC5115Tester(yieldSourceAddresses[i]).balanceOf(ownersOfShares[i][j]);
    83
                    result[i][j] = (shares * exchangeRate) / 1e18;
    84
                }
    85
            }
    86
            return result;
    87
        }
    88
        
    89
        function getTVLMultiple(
    90
            address[] memory yieldSourceAddresses
    91
        ) external view returns (uint256[] memory) {
    92
            uint256[] memory tvls = new uint256[](yieldSourceAddresses.length);
    93
            for (uint256 i = 0; i < yieldSourceAddresses.length; i++) {
    94
                uint256 totalShares = MockERC5115Tester(yieldSourceAddresses[i]).totalSupply();
    95
                uint256 exchangeRate = MockERC5115Tester(yieldSourceAddresses[i]).exchangeRate();
    96
                tvls[i] = (totalShares * exchangeRate) / 1e18;
    97
            }
    98
            return tvls;
    99
        }
    100
        
    101
        function isValidUnderlyingAsset(address, address asset) external view returns (bool) {
    102
            return validAssetMap[asset];
    103
        }
    104
        
    105
        function isValidUnderlyingAssets(
    106
            address[] memory,
    107
            address[] memory assets
    108
        ) external view returns (bool[] memory) {
    109
            bool[] memory validities = new bool[](assets.length);
    110
            for (uint256 i = 0; i < assets.length; i++) {
    111
                validities[i] = validAssetMap[assets[i]];
    112
            }
    113
            return validities;
    114
        }
    115
        
    116
        function getAssetOutputWithFees(
    117
            bytes32,
    118
            address yieldSourceAddress,
    119
            address assetOut,
    120
            address,
    121
            uint256 sharesIn
    122
        ) external view returns (uint256) {
    123
            return MockERC5115Tester(yieldSourceAddress).previewRedeem(assetOut, sharesIn);
    124
        }
    125
    }
    69.3% test/recon/mocks/MockERC7540Tester.sol
    Lines covered: 97 / 140 (69.3%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {MockERC20} from "@recon/MockERC20.sol";
    5
    import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    6
    
                                                    
                                                
    7
    abstract contract ERC7575 is MockERC20 {
    8
    ✓ 13.0K
        MockERC20 public immutable asset;
    9
    
                                                    
                                                
    10
        event Deposit(
    11
            address indexed sender,
    12
            address indexed owner,
    13
            uint256 assets,
    14
            uint256 shares
    15
        );
    16
        event Withdraw(
    17
            address indexed sender,
    18
            address indexed receiver,
    19
            address indexed owner,
    20
            uint256 assets,
    21
            uint256 shares
    22
        );
    23
    
                                                    
                                                
    24
    ✓ 1
        constructor(MockERC20 _asset) MockERC20("MockERC7540Tester", "M7540", 18) {
    25
    ✓ 1
            asset = _asset;
    26
        }
    27
    
                                                    
                                                
    28
        function share() external view returns (address shareTokenAddress) {
    29
    ✓ 9.0K
            return address(this);
    30
        }
    31
    
                                                    
                                                
    32
    ✓ 17.8K
        function totalAssets() public view virtual returns (uint256) {
    33
    ✓ 17.8K
            return asset.balanceOf(address(this));
    34
        }
    35
    
                                                    
                                                
    36
        function convertToShares(
    37
            uint256 assets
    38
        ) public view virtual returns (uint256) {
    39
            uint256 supply = totalSupply;
    40
            return supply == 0 ? assets : (assets * supply) / totalAssets();
    41
        }
    42
    
                                                    
                                                
    43
        function convertToAssets(
    44
            uint256 shares
    45
    ✓ 62.0K
        ) public view virtual returns (uint256) {
    46
    ✓ 62.0K
            uint256 supply = totalSupply;
    47
    ✓ 83.4K
            return supply == 0 ? shares : (shares * totalAssets()) / supply;
    48
        }
    49
    
                                                    
                                                
    50
        function maxDeposit(address) public pure virtual returns (uint256) {
    51
    ✓ 5.4K
            return type(uint256).max;
    52
        }
    53
    
                                                    
                                                
    54
        function maxMint(address) public pure virtual returns (uint256) {
    55
            return type(uint256).max;
    56
        }
    57
    
                                                    
                                                
    58
    ✓ 2.8K
        function maxWithdraw(address owner) public view virtual returns (uint256) {
    59
    ✓ 2.8K
            return convertToAssets(balanceOf[owner]);
    60
        }
    61
    
                                                    
                                                
    62
    ✓ 2.6K
        function maxRedeem(address owner) public view virtual returns (uint256) {
    63
    ✓ 2.6K
            return balanceOf[owner];
    64
        }
    65
    
                                                    
                                                
    66
        function previewDeposit(
    67
            uint256 assets
    68
    ✓ 11.6K
        ) public view virtual returns (uint256) {
    69
    ✓ 11.6K
            return convertToShares(assets);
    70
        }
    71
    
                                                    
                                                
    72
        function previewMint(uint256 shares) public view virtual returns (uint256) {
    73
            uint256 supply = totalSupply;
    74
            return supply == 0 ? shares : (shares * totalAssets()) / supply;
    75
        }
    76
    
                                                    
                                                
    77
        function previewWithdraw(
    78
            uint256 assets
    79
    ✓ 36.9K
        ) public view virtual returns (uint256) {
    80
    ✓ 36.9K
            uint256 supply = totalSupply;
    81
    ✓ 36.9K
            return supply == 0 ? assets : (assets * supply) / totalAssets();
    82
        }
    83
    
                                                    
                                                
    84
        function previewRedeem(
    85
            uint256 shares
    86
    ✓ 25.4K
        ) public view virtual returns (uint256) {
    87
    ✓ 25.4K
            return convertToAssets(shares);
    88
        }
    89
    
                                                    
                                                
    90
        function deposit(
    91
            uint256 assets,
    92
            address receiver
    93
    ✓ 4.3K
        ) public virtual returns (uint256 shares) {
    94
    ✓ 4.3K
            shares = previewDeposit(assets);
    95
    ✓ 4.2K
            asset.transferFrom(msg.sender, address(this), assets);
    96
    ✓ 1.1K
            _mint(receiver, shares);
    97
    ✓ 1.1K
            emit Deposit(msg.sender, receiver, assets, shares);
    98
        }
    99
    
                                                    
                                                
    100
        function mint(
    101
            uint256 shares,
    102
            address receiver
    103
    ✓ 29.3K
        ) public virtual returns (uint256 assets) {
    104
    ✓ 29.3K
            assets = previewMint(shares);
    105
    ✓ 28.7K
            asset.transferFrom(msg.sender, address(this), assets);
    106
    ✓ 8.6K
            _mint(receiver, shares);
    107
    ✓ 7.9K
            emit Deposit(msg.sender, receiver, assets, shares);
    108
        }
    109
    }
    110
    
                                                    
                                                
    111
    contract MockERC7540Tester is ERC7575, IERC165 {
    112
        event DepositRequest(
    113
            address indexed controller,
    114
            address indexed owner,
    115
            uint256 indexed requestId,
    116
            address sender,
    117
            uint256 assets
    118
        );
    119
        event RedeemRequest(
    120
            address indexed controller,
    121
            address indexed owner,
    122
            uint256 indexed requestId,
    123
            address sender,
    124
            uint256 shares
    125
        );
    126
        event OperatorSet(
    127
            address indexed controller,
    128
            address indexed operator,
    129
            bool approved
    130
        );
    131
    
                                                    
                                                
    132
        struct DepositRequestStruct {
    133
            uint256 assets;
    134
            address controller;
    135
            address owner;
    136
            bool fulfilled;
    137
            bool canceled;
    138
        }
    139
    
                                                    
                                                
    140
        struct RedeemRequestStruct {
    141
            uint256 shares;
    142
            address controller;
    143
            address owner;
    144
            bool fulfilled;
    145
            bool canceled;
    146
        }
    147
    
                                                    
                                                
    148
    ✓ 1
        uint256 private _nextRequestId = 1;
    149
    
                                                    
                                                
    150
        mapping(uint256 => DepositRequestStruct) public depositRequests;
    151
        mapping(uint256 => RedeemRequestStruct) public redeemRequests;
    152
        mapping(address => mapping(address => bool)) public operators;
    153
        mapping(uint256 => bool) public pendingCancelDeposit;
    154
        mapping(uint256 => bool) public pendingCancelRedeem;
    155
    
                                                    
                                                
    156
    ✓ 1
        uint256 public yieldMultiplier = 10000; // 100% in basis points
    157
    ✓ 29.1K
        uint256 private constant MAX_BPS = 10000;
    158
        uint256 public totalLosses;
    159
        uint256 public totalGains;
    160
        uint256 public lossOnWithdraw;
    161
    
                                                    
                                                
    162
    ✓ 1
        constructor(address _asset) ERC7575(MockERC20(_asset)) {}
    163
    
                                                    
                                                
    164
        // Operator Management
    165
        function setOperator(
    166
            address operator,
    167
            bool approved
    168
    ✓ 17.4K
        ) external returns (bool) {
    169
    ✓ 17.4K
            operators[msg.sender][operator] = approved;
    170
    ✓ 17.4K
            emit OperatorSet(msg.sender, operator, approved);
    171
            return true;
    172
        }
    173
    
                                                    
                                                
    174
        function isOperator(
    175
            address controller,
    176
            address operator
    177
        ) external view returns (bool) {
    178
            return operators[controller][operator];
    179
        }
    180
    
                                                    
                                                
    181
        // Async Deposit Flow
    182
        function requestDeposit(
    183
            uint256 assets,
    184
            address controller,
    185
            address owner
    186
    ✓ 19.6K
        ) external returns (uint256 requestId) {
    187
    ✓ 19.6K
            requestId = _nextRequestId++;
    188
    ✓ 19.6K
            depositRequests[requestId] = DepositRequestStruct({
    189
    ✓ 19.6K
                assets: assets,
    190
                controller: controller,
    191
                owner: owner,
    192
                fulfilled: false,
    193
                canceled: false
    194
            });
    195
    
                                                    
                                                
    196
    ✓ 19.6K
            asset.transferFrom(msg.sender, address(this), assets);
    197
    ✓ 3.6K
            emit DepositRequest(controller, owner, requestId, msg.sender, assets);
    198
        }
    199
    
                                                    
                                                
    200
        function pendingDepositRequest(
    201
            uint256 requestId,
    202
            address controller
    203
        ) external view returns (uint256) {
    204
            DepositRequestStruct storage request = depositRequests[requestId];
    205
            if (
    206
                request.controller != controller ||
    207
                request.fulfilled ||
    208
                request.canceled
    209
            ) {
    210
    ✓ 7.0K
                return 0;
    211
            }
    212
            return request.assets;
    213
        }
    214
    
                                                    
                                                
    215
        function claimableDepositRequest(
    216
            uint256 requestId,
    217
            address controller
    218
        ) external view returns (uint256) {
    219
            DepositRequestStruct storage request = depositRequests[requestId];
    220
            if (
    221
                request.controller != controller ||
    222
                request.fulfilled ||
    223
                request.canceled
    224
            ) {
    225
                return 0;
    226
            }
    227
            return request.assets;
    228
        }
    229
    
                                                    
                                                
    230
        function pendingCancelDepositRequest(
    231
            uint256 requestId,
    232
            address controller
    233
        ) external view returns (bool) {
    234
            DepositRequestStruct storage request = depositRequests[requestId];
    235
            return
    236
                request.controller == controller && pendingCancelDeposit[requestId];
    237
        }
    238
    
                                                    
                                                
    239
        // Override deposit to handle async requests
    240
        function deposit(
    241
            uint256 assets,
    242
            address receiver,
    243
            address controller
    244
    ✓ 17.7K
        ) public returns (uint256 shares) {
    245
            require(
    246
    ✓ 17.7K
                msg.sender == controller || operators[controller][msg.sender],
    247
                "Not authorized"
    248
            );
    249
    
                                                    
                                                
    250
            // Find and fulfill a matching deposit request
    251
    ✓ 506
            for (uint256 i = 1; i < _nextRequestId; i++) {
    252
    ✓ 86
                DepositRequestStruct storage request = depositRequests[i];
    253
                if (
    254
    ✓ 86
                    request.controller == controller &&
    255
                    !request.fulfilled &&
    256
                    !request.canceled &&
    257
                    request.assets >= assets
    258
                ) {
    259
                    shares = previewDeposit(assets);
    260
                    request.fulfilled = true;
    261
                    _mint(receiver, shares);
    262
    
                                                    
                                                
    263
                    // Refund excess assets if any
    264
                    if (request.assets > assets) {
    265
                        asset.transfer(controller, request.assets - assets);
    266
                    }
    267
    
                                                    
                                                
    268
                    emit Deposit(msg.sender, receiver, assets, shares);
    269
                    return shares;
    270
                }
    271
            }
    272
    
                                                    
                                                
    273
            // Fallback to synchronous deposit if no matching request
    274
    ✓ 420
            return super.deposit(assets, receiver);
    275
        }
    276
    
                                                    
                                                
    277
        function withdraw(
    278
            uint256 assets,
    279
            address receiver,
    280
            address owner
    281
    ✓ 23.1K
        ) public returns (uint256 shares) {
    282
    ✓ 23.1K
            shares = previewWithdraw(assets);
    283
    ✓ 21.0K
            if (msg.sender != owner) {
    284
    ✓ 20.8K
                allowance[owner][msg.sender] -= shares;
    285
            }
    286
    ✓ 1.7K
            _burn(owner, shares);
    287
    ✓ 1.5K
            uint256 lossyAssets = assets - ((assets * lossOnWithdraw) / MAX_BPS);
    288
    
                                                    
                                                
    289
    ✓ 1.5K
            asset.transfer(receiver, lossyAssets);
    290
    ✓ 1.5K
            emit Withdraw(msg.sender, receiver, owner, lossyAssets, shares);
    291
        }
    292
    
                                                    
                                                
    293
        function redeem(
    294
            uint256 shares,
    295
            address receiver,
    296
            address owner
    297
    ✓ 25.4K
        ) public returns (uint256 assets) {
    298
    ✓ 25.4K
            assets = previewRedeem(shares);
    299
    ✓ 24.6K
            if (msg.sender != owner) {
    300
    ✓ 20.6K
                allowance[owner][msg.sender] -= shares;
    301
            }
    302
    ✓ 4.4K
            _burn(owner, shares);
    303
    ✓ 3.6K
            uint256 lossyAssets = assets - ((assets * lossOnWithdraw) / MAX_BPS);
    304
    
                                                    
                                                
    305
    ✓ 3.6K
            asset.transfer(receiver, lossyAssets);
    306
    ✓ 3.6K
            emit Withdraw(msg.sender, receiver, owner, lossyAssets, shares);
    307
        }
    308
    
                                                    
                                                
    309
        // Async Redeem Flow
    310
        function requestRedeem(
    311
            uint256 shares,
    312
            address controller,
    313
            address owner
    314
    ✓ 19.8K
        ) external returns (uint256 requestId) {
    315
    ✓ 19.8K
            requestId = _nextRequestId++;
    316
    ✓ 19.8K
            redeemRequests[requestId] = RedeemRequestStruct({
    317
                shares: shares,
    318
                controller: controller,
    319
                owner: owner,
    320
                fulfilled: false,
    321
                canceled: false
    322
            });
    323
    
                                                    
                                                
    324
    ✓ 19.8K
            emit RedeemRequest(controller, owner, requestId, msg.sender, shares);
    325
        }
    326
    
                                                    
                                                
    327
        function pendingRedeemRequest(
    328
            uint256 requestId,
    329
            address controller
    330
        ) external view returns (uint256) {
    331
            RedeemRequestStruct storage request = redeemRequests[requestId];
    332
            if (
    333
                request.controller != controller ||
    334
                request.fulfilled ||
    335
                request.canceled
    336
            ) {
    337
                return 0;
    338
            }
    339
            return request.shares;
    340
        }
    341
    
                                                    
                                                
    342
        function claimableRedeemRequest(
    343
            uint256 requestId,
    344
            address controller
    345
    ✓ 7.0K
        ) external view returns (uint256) {
    346
    ✓ 7.0K
            RedeemRequestStruct storage request = redeemRequests[requestId];
    347
            if (
    348
    ✓ 7.0K
                request.controller != controller ||
    349
                request.fulfilled ||
    350
                request.canceled
    351
            ) {
    352
                return 0;
    353
            }
    354
            return request.shares;
    355
        }
    356
    
                                                    
                                                
    357
        function pendingCancelRedeemRequest(
    358
            uint256 requestId,
    359
            address controller
    360
        ) external view returns (bool) {
    361
            RedeemRequestStruct storage request = redeemRequests[requestId];
    362
            return
    363
                request.controller == controller && pendingCancelRedeem[requestId];
    364
        }
    365
    
                                                    
                                                
    366
        // Cancel Operations
    367
        function cancelDepositRequest(
    368
            uint256 requestId,
    369
            address controller
    370
        ) external {
    371
            require(
    372
    ✓ 21.5K
                msg.sender == controller || operators[controller][msg.sender],
    373
                "Not authorized"
    374
            );
    375
    ✓ 1.6K
            DepositRequestStruct storage request = depositRequests[requestId];
    376
            require(
    377
    ✓ 1.6K
                request.controller == controller && !request.fulfilled,
    378
                "Invalid request"
    379
            );
    380
    
                                                    
                                                
    381
            pendingCancelDeposit[requestId] = true;
    382
        }
    383
    
                                                    
                                                
    384
        function claimCancelDepositRequest(
    385
            uint256 requestId,
    386
            address receiver,
    387
            address controller
    388
    ✓ 17.1K
        ) external returns (uint256 assets) {
    389
    ✓ 17.1K
            DepositRequestStruct storage request = depositRequests[requestId];
    390
    
                                                    
                                                
    391
    ✓ 17.1K
            assets = request.assets;
    392
    ✓ 17.1K
            request.canceled = true;
    393
    ✓ 17.1K
            pendingCancelDeposit[requestId] = false;
    394
    
                                                    
                                                
    395
    ✓ 17.1K
            asset.transfer(receiver, assets);
    396
        }
    397
    
                                                    
                                                
    398
        function cancelRedeemRequest(
    399
            uint256 requestId,
    400
            address controller
    401
        ) external {
    402
    ✓ 25.3K
            RedeemRequestStruct storage request = redeemRequests[requestId];
    403
    
                                                    
                                                
    404
    ✓ 25.3K
            pendingCancelRedeem[requestId] = true;
    405
        }
    406
    
                                                    
                                                
    407
        function claimCancelRedeemRequest(
    408
            uint256 requestId,
    409
            address receiver,
    410
            address controller
    411
    ✓ 14.6K
        ) external returns (uint256 shares) {
    412
    ✓ 14.6K
            RedeemRequestStruct storage request = redeemRequests[requestId];
    413
    
                                                    
                                                
    414
    ✓ 14.6K
            shares = request.shares;
    415
    ✓ 14.6K
            request.canceled = true;
    416
    ✓ 14.6K
            pendingCancelRedeem[requestId] = false;
    417
    
                                                    
                                                
    418
    ✓ 14.6K
            _mint(receiver, shares);
    419
        }
    420
    
                                                    
                                                
    421
        // Placeholder functions for Centrifuge compatibility
    422
        function poolId() external pure returns (uint64) {
    423
            return 1;
    424
        }
    425
    
                                                    
                                                
    426
        function trancheId() external pure returns (bytes16) {
    427
            return bytes16(uint128(1));
    428
        }
    429
    
                                                    
                                                
    430
        // Yield simulation functions (similar to MockERC4626Tester)
    431
        // Primary way that 7540 vaults receive losses is on rounding in redemptions so we just simulate a loss that reduces total asset balance
    432
        function increaseYield(uint256 increasePercentageFP4) external {
    433
            uint256 amount = (totalAssets() * increasePercentageFP4) / MAX_BPS;
    434
            MockERC20(asset).transferFrom(msg.sender, address(this), amount);
    435
        }
    436
    
                                                    
                                                
    437
        function decreaseYield(uint256 decreasePercentageFP4) external {
    438
            uint256 amount = (totalAssets() * decreasePercentageFP4) / MAX_BPS;
    439
            MockERC20(asset).transfer(address(0xbeef), amount);
    440
        }
    441
    
                                                    
                                                
    442
        function simulateGain(uint256 gainAmount) external {
    443
    ✓ 16.7K
            MockERC20(asset).transferFrom(msg.sender, address(this), gainAmount);
    444
    ✓ 4.3K
            totalGains += gainAmount;
    445
        }
    446
    
                                                    
                                                
    447
        function simulateLoss(uint256 lossAmount) external {
    448
    ✓ 18.3K
            MockERC20(asset).transfer(address(0xbeef), lossAmount);
    449
    ✓ 656
            totalLosses += lossAmount;
    450
        }
    451
    
                                                    
                                                
    452
        /// @dev Set the loss on withdraw as percentage of the assets being withdrawn
    453
        function setLossOnWithdraw(uint256 _lossOnWithdraw) public {
    454
    ✓ 29.1K
            _lossOnWithdraw %= MAX_BPS + 1; // clamp to ensure we set a max of 100%
    455
    ✓ 29.1K
            lossOnWithdraw = _lossOnWithdraw;
    456
        }
    457
    
                                                    
                                                
    458
        /// @notice ERC165 interface detection
    459
        function supportsInterface(
    460
            bytes4 interfaceId
    461
        ) external pure override returns (bool) {
    462
            return interfaceId == type(IERC165).interfaceId;
    463
        }
    464
    }
    465
    
                                                    
                                                
    8.0% test/recon/mocks/MockERC7540YieldSourceOracle.sol
    Lines covered: 4 / 50 (8.0%)
    1
    // SPDX-License-Identifier: MIT
    2
    pragma solidity ^0.8.30;
    3
    
                                                    
                                                
    4
    import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
    5
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    6
    import {IYieldSourceOracle} from "@superform-v2-core/src/interfaces/accounting/IYieldSourceOracle.sol";
    7
    import {MockERC7540Tester} from "./MockERC7540Tester.sol";
    8
    
                                                    
                                                
    9
    /// @title MockERC7540YieldSourceOracle
    10
    /// @notice Mock oracle for ERC7540 (Asynchronous Tokenized Vaults) in testing
    11
    ✓ 1
    contract MockERC7540YieldSourceOracle is IYieldSourceOracle {
    12
        mapping(address => bool) public validAssetMap;
    13
        
    14
        function setValidAsset(address asset, bool isValid) external {
    15
    ✓ 103.0K
            validAssetMap[asset] = isValid;
    16
        }
    17
        
    18
        function decimals(address yieldSourceAddress) external view returns (uint8) {
    19
            address share = MockERC7540Tester(yieldSourceAddress).share();
    20
            return IERC20Metadata(share).decimals();
    21
        }
    22
        
    23
        function getShareOutput(
    24
            address yieldSourceAddress,
    25
            address,
    26
            uint256 assetsIn
    27
    ✓ 8.4K
        ) external view returns (uint256) {
    28
    ✓ 8.4K
            return MockERC7540Tester(yieldSourceAddress).convertToShares(assetsIn);
    29
        }
    30
        
    31
        function getAssetOutput(
    32
            address yieldSourceAddress,
    33
            address,
    34
            uint256 sharesIn
    35
        ) public view returns (uint256) {
    36
            return MockERC7540Tester(yieldSourceAddress).convertToAssets(sharesIn);
    37
        }
    38
        
    39
        function getPricePerShare(address yieldSourceAddress) external view returns (uint256) {
    40
            address share = MockERC7540Tester(yieldSourceAddress).share();
    41
            uint256 _decimals = IERC20Metadata(share).decimals();
    42
            return MockERC7540Tester(yieldSourceAddress).convertToAssets(10 ** _decimals);
    43
        }
    44
        
    45
        function getBalanceOfOwner(
    46
            address yieldSourceAddress,
    47
            address ownerOfShares
    48
        ) external view returns (uint256) {
    49
            return IERC20(MockERC7540Tester(yieldSourceAddress).share()).balanceOf(ownerOfShares);
    50
        }
    51
        
    52
        function getTVLByOwnerOfShares(
    53
            address yieldSourceAddress,
    54
            address ownerOfShares
    55
        ) external view returns (uint256) {
    56
            address share = MockERC7540Tester(yieldSourceAddress).share();
    57
            uint256 shares = IERC20(share).balanceOf(ownerOfShares);
    58
            return MockERC7540Tester(yieldSourceAddress).convertToAssets(shares);
    59
        }
    60
        
    61
        function getTVL(address yieldSourceAddress) external view returns (uint256) {
    62
            return MockERC7540Tester(yieldSourceAddress).totalAssets();
    63
        }
    64
        
    65
        function getPricePerShareMultiple(
    66
            address[] memory yieldSourceAddresses
    67
        ) external view returns (uint256[] memory) {
    68
            uint256[] memory prices = new uint256[](yieldSourceAddresses.length);
    69
            for (uint256 i = 0; i < yieldSourceAddresses.length; i++) {
    70
                address share = MockERC7540Tester(yieldSourceAddresses[i]).share();
    71
                uint256 _decimals = IERC20Metadata(share).decimals();
    72
                prices[i] = MockERC7540Tester(yieldSourceAddresses[i]).convertToAssets(10 ** _decimals);
    73
            }
    74
            return prices;
    75
        }
    76
        
    77
        function getTVLByOwnerOfSharesMultiple(
    78
            address[] memory yieldSourceAddresses,
    79
            address[][] memory ownersOfShares
    80
        ) external view returns (uint256[][] memory) {
    81
            uint256[][] memory result = new uint256[][](yieldSourceAddresses.length);
    82
            for (uint256 i = 0; i < yieldSourceAddresses.length; i++) {
    83
                result[i] = new uint256[](ownersOfShares[i].length);
    84
                address share = MockERC7540Tester(yieldSourceAddresses[i]).share();
    85
                for (uint256 j = 0; j < ownersOfShares[i].length; j++) {
    86
                    uint256 shares = IERC20(share).balanceOf(ownersOfShares[i][j]);
    87
                    result[i][j] = MockERC7540Tester(yieldSourceAddresses[i]).convertToAssets(shares);
    88
                }
    89
            }
    90
            return result;
    91
        }
    92
        
    93
        function getTVLMultiple(
    94
            address[] memory yieldSourceAddresses
    95
        ) external view returns (uint256[] memory) {
    96
            uint256[] memory tvls = new uint256[](yieldSourceAddresses.length);
    97
            for (uint256 i = 0; i < yieldSourceAddresses.length; i++) {
    98
                tvls[i] = MockERC7540Tester(yieldSourceAddresses[i]).totalAssets();
    99
            }
    100
            return tvls;
    101
        }
    102
        
    103
        function isValidUnderlyingAsset(address, address asset) external view returns (bool) {
    104
            return validAssetMap[asset];
    105
        }
    106
        
    107
        function isValidUnderlyingAssets(
    108
            address[] memory,
    109
            address[] memory assets
    110
        ) external view returns (bool[] memory) {
    111
            bool[] memory validities = new bool[](assets.length);
    112
            for (uint256 i = 0; i < assets.length; i++) {
    113
                validities[i] = validAssetMap[assets[i]];
    114
            }
    115
            return validities;
    116
        }
    117
        
    118
        function getAssetOutputWithFees(
    119
            bytes32,
    120
            address yieldSourceAddress,
    121
            address,
    122
            address,
    123
            uint256 sharesIn
    124
        ) external view returns (uint256) {
    125
            return MockERC7540Tester(yieldSourceAddress).convertToAssets(sharesIn);
    126
        }
    127
    }
    97.2% test/recon/targets/AdminTargets.sol
    Lines covered: 173 / 178 (97.2%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    // External dependencies
    5
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    6
    import {vm} from "@chimera/Hevm.sol";
    7
    import {Panic} from "@recon/Panic.sol";
    8
    import {MockERC20} from "@recon/MockERC20.sol";
    9
    
                                                    
                                                
    10
    // System dependencies
    11
    import {ISuperVaultStrategy} from "src/interfaces/SuperVault/ISuperVaultStrategy.sol";
    12
    import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
    13
    
                                                    
                                                
    14
    // Test dependencies
    15
    import {YieldSourceType} from "test/recon/managers/YieldManager.sol";
    16
    import {BeforeAfter, OpType} from "../BeforeAfter.sol";
    17
    import {Properties} from "../Properties.sol";
    18
    
                                                    
                                                
    19
    abstract contract AdminTargets is BaseTargetFunctions, Properties {
    20
        enum HookType {
    21
            // ERC4626 Hooks
    22
            ApproveAndDeposit4626,
    23
            Deposit4626,
    24
            Redeem4626,
    25
            // ERC5115 Hooks
    26
            ApproveAndDeposit5115,
    27
            Deposit5115,
    28
            Redeem5115,
    29
            // ERC7540 Hooks
    30
            Deposit7540,
    31
            Redeem7540,
    32
            RequestDeposit7540,
    33
            RequestRedeem7540,
    34
            ApproveAndRequestDeposit7540,
    35
            CancelDepositRequest7540,
    36
            CancelRedeemRequest7540,
    37
            ClaimCancelDepositRequest7540,
    38
            ClaimCancelRedeemRequest7540,
    39
            Withdraw7540,
    40
            // Super Vault Hooks
    41
            CancelRedeem,
    42
            SuperVaultWithdraw7540
    43
        }
    44
    
                                                    
                                                
    45
        /// CUSTOM TARGET FUNCTIONS - Add your own target functions here ///
    46
        function superVaultStrategy_executeHooks_clamped(
    47
            uint256[] memory hookTypeInts,
    48
            uint256[] memory amountsToInvest,
    49
            bool[] memory usePrevHookAmounts
    50
        ) public payable {
    51
            // Limit the number of hooks to 10 maximum
    52
    ✓ 94.6K
            uint256 numHooks = hookTypeInts.length;
    53
    ✓ 94.6K
            if (numHooks > 10) {
    54
    ✓ 82.6K
                numHooks = 10;
    55
            }
    56
    
                                                    
                                                
    57
            // Ensure all arrays have the same length
    58
    ✓ 94.6K
            if (amountsToInvest.length < numHooks) {
    59
    ✓ 10.3K
                numHooks = amountsToInvest.length;
    60
            }
    61
    ✓ 94.6K
            if (usePrevHookAmounts.length < numHooks) {
    62
    ✓ 9.9K
                numHooks = usePrevHookAmounts.length;
    63
            }
    64
    
                                                    
                                                
    65
            // Return early if no hooks to execute
    66
    ✓ 94.6K
            if (numHooks == 0) {
    67
    ✓ 4.0K
                return;
    68
            }
    69
    
                                                    
                                                
    70
            // Create ExecuteArgs for the hooks
    71
    ✓ 90.6K
            ISuperVaultStrategy.ExecuteArgs memory executeArgs = ISuperVaultStrategy
    72
                .ExecuteArgs({
    73
    ✓ 90.6K
                    hooks: new address[](numHooks),
    74
    ✓ 90.6K
                    hookCalldata: new bytes[](numHooks),
    75
    ✓ 90.6K
                    expectedAssetsOrSharesOut: new uint256[](numHooks),
    76
    ✓ 90.6K
                    globalProofs: new bytes32[][](numHooks),
    77
    ✓ 90.6K
                    strategyProofs: new bytes32[][](numHooks)
    78
                });
    79
    
                                                    
                                                
    80
            // Process each hook
    81
    ✓ 90.6K
            uint256 totalAmountToDeposit;
    82
    ✓ 865.7K
            for (uint256 i = 0; i < numHooks; i++) {
    83
                // Convert integer to enum (will wrap around if > max enum value)
    84
    ✓ 775.1K
                HookType hookType = HookType(hookTypeInts[i] % 17); // 17 is the total number of hooks
    85
    
                                                    
                                                
    86
                // Clamp to the strategy's asset balance (not SuperVault's balance)
    87
    ✓ 775.1K
                uint256 clampedAmount = amountsToInvest[i] %
    88
    ✓ 775.1K
                    (MockERC20(superVault.asset()).balanceOf(
    89
    ✓ 775.1K
                        address(superVaultStrategy)
    90
    ✓ 775.1K
                    ) + 1);
    91
    
                                                    
                                                
    92
                // Get the hook address and calldata
    93
                (
    94
    ✓ 775.1K
                    address hookAddress,
    95
    ✓ 775.1K
                    bytes memory hookCalldata
    96
    ✓ 775.1K
                ) = _getHookAddressAndCalldata(
    97
    ✓ 775.1K
                        hookType,
    98
    ✓ 775.1K
                        clampedAmount,
    99
    ✓ 775.1K
                        usePrevHookAmounts[i]
    100
                    );
    101
    
                                                    
                                                
    102
    ✓ 775.1K
                executeArgs.hooks[i] = hookAddress;
    103
    ✓ 775.1K
                executeArgs.hookCalldata[i] = hookCalldata;
    104
    ✓ 775.1K
                executeArgs.expectedAssetsOrSharesOut[i] = clampedAmount;
    105
    ✓ 775.1K
                executeArgs.globalProofs[i] = new bytes32[](1);
    106
    ✓ 775.1K
                executeArgs.strategyProofs[i] = new bytes32[](1);
    107
    
                                                    
                                                
    108
    ✓ 775.1K
                totalAmountToDeposit += clampedAmount;
    109
            }
    110
    
                                                    
                                                
    111
            // Check that amount to be invested is less than the claimable assets so that it doesn't reinvest and prevent users from claiming
    112
    ✓ 90.6K
            if (_claimableMoreThanInvested(totalAmountToDeposit)) return;
    113
    
                                                    
                                                
    114
            // Execute all hooks
    115
    ✓ 78.7K
            this.superVaultStrategy_executeHooks{value: msg.value}(executeArgs);
    116
        }
    117
    
                                                    
                                                
    118
        function _getHookAddressAndCalldata(
    119
            HookType hookType,
    120
            uint256 amountToInvest,
    121
            bool usePrevHookAmount
    122
    ✓ 775.1K
        ) internal view returns (address hookAddress, bytes memory hookCalldata) {
    123
    ✓ 775.1K
            if (hookType == HookType.ApproveAndDeposit4626) {
    124
    ✓ 76.1K
                hookAddress = address(approveAndDeposit4626Hook);
    125
                hookCalldata = abi.encodePacked(
    126
                    bytes32(0), // yieldSourceOracleId placeholder
    127
    ✓ 76.1K
                    _getYieldSource(), // Address of the yield source
    128
    ✓ 76.1K
                    superVault.asset(), // Address of the token to approve and deposit
    129
    ✓ 76.1K
                    amountToInvest, // Amount to deposit
    130
    ✓ 76.1K
                    usePrevHookAmount
    131
                );
    132
    ✓ 699.0K
            } else if (hookType == HookType.Deposit4626) {
    133
    ✓ 58.6K
                hookAddress = address(deposit4626Hook);
    134
                hookCalldata = abi.encodePacked(
    135
                    bytes32(0), // yieldSourceOracleId placeholder
    136
    ✓ 58.6K
                    _getYieldSource(), // Address of the yield source
    137
    ✓ 58.6K
                    amountToInvest, // Amount to deposit
    138
    ✓ 58.6K
                    usePrevHookAmount
    139
                );
    140
    ✓ 640.4K
            } else if (hookType == HookType.Redeem4626) {
    141
    ✓ 44.0K
                hookAddress = address(redeem4626Hook);
    142
                hookCalldata = abi.encodePacked(
    143
                    bytes32(0), // yieldSourceOracleId placeholder
    144
    ✓ 44.0K
                    _getYieldSource(), // Address of the yield source
    145
                    amountToInvest, // Amount to redeem
    146
    ✓ 161.8K
                    address(superVaultStrategy), // Receiver
    147
                    address(superVaultStrategy) // Owner
    148
                );
    149
    ✓ 596.4K
            } else if (hookType == HookType.ApproveAndDeposit5115) {
    150
    ✓ 49.4K
                hookAddress = address(approveAndDeposit5115Hook);
    151
                hookCalldata = abi.encodePacked(
    152
                    bytes32(0), // yieldSourceOracleId placeholder
    153
    ✓ 49.4K
                    _getYieldSource(), // Address of the yield source
    154
    ✓ 49.4K
                    superVault.asset(), // Address of the token to approve and deposit
    155
    ✓ 49.4K
                    bytes32(0), // tokenId (for ERC5115)
    156
                    amountToInvest, // Amount to deposit
    157
                    usePrevHookAmount
    158
                );
    159
    ✓ 547.0K
            } else if (hookType == HookType.Deposit5115) {
    160
    ✓ 47.8K
                hookAddress = address(deposit5115Hook);
    161
                hookCalldata = abi.encodePacked(
    162
                    bytes32(0), // yieldSourceOracleId placeholder
    163
    ✓ 47.8K
                    _getYieldSource(), // Address of the yield source
    164
                    bytes32(0), // tokenId (for ERC5115)
    165
                    amountToInvest, // Amount to deposit
    166
                    usePrevHookAmount
    167
                );
    168
    ✓ 499.2K
            } else if (hookType == HookType.Redeem5115) {
    169
    ✓ 40.2K
                hookAddress = address(redeem5115Hook);
    170
                hookCalldata = abi.encodePacked(
    171
                    bytes32(0), // yieldSourceOracleId placeholder
    172
    ✓ 40.2K
                    _getYieldSource(), // Address of the yield source
    173
    ✓ 40.2K
                    bytes32(0), // tokenId (for ERC5115)
    174
                    amountToInvest, // Amount to redeem
    175
    ✓ 40.2K
                    address(superVaultStrategy), // Receiver
    176
                    address(superVaultStrategy) // Owner
    177
                );
    178
    ✓ 459.0K
            } else if (hookType == HookType.Deposit7540) {
    179
    ✓ 45.5K
                hookAddress = address(deposit7540Hook);
    180
                hookCalldata = abi.encodePacked(
    181
                    bytes32(0), // yieldSourceOracleId placeholder
    182
    ✓ 45.5K
                    _getYieldSource(), // Address of the yield source
    183
                    amountToInvest, // Amount to deposit
    184
                    address(superVaultStrategy), // Receiver
    185
                    address(superVaultStrategy) // Controller
    186
                );
    187
    ✓ 413.5K
            } else if (hookType == HookType.Redeem7540) {
    188
    ✓ 37.6K
                hookAddress = address(redeem7540Hook);
    189
                hookCalldata = abi.encodePacked(
    190
                    bytes32(0), // yieldSourceOracleId placeholder
    191
    ✓ 37.6K
                    _getYieldSource(), // Address of the yield source
    192
    ✓ 85.7K
                    amountToInvest, // Amount to redeem
    193
    ✓ 85.7K
                    address(superVaultStrategy), // Receiver
    194
                    address(superVaultStrategy), // Owner
    195
                    address(superVaultStrategy) // Controller
    196
                );
    197
    ✓ 375.9K
            } else if (hookType == HookType.RequestDeposit7540) {
    198
    ✓ 36.1K
                hookAddress = address(requestDeposit7540Hook);
    199
                hookCalldata = abi.encodePacked(
    200
                    bytes32(0), // yieldSourceOracleId placeholder
    201
    ✓ 36.1K
                    _getYieldSource(), // Address of the yield source
    202
                    amountToInvest, // Amount to request deposit
    203
                    address(superVaultStrategy), // Owner
    204
                    address(superVaultStrategy) // Controller
    205
                );
    206
    ✓ 339.9K
            } else if (hookType == HookType.RequestRedeem7540) {
    207
    ✓ 36.2K
                hookAddress = address(requestRedeem7540Hook);
    208
                hookCalldata = abi.encodePacked(
    209
                    bytes32(0), // yieldSourceOracleId placeholder
    210
    ✓ 36.2K
                    _getYieldSource(), // Address of the yield source
    211
                    amountToInvest, // Amount to request redeem
    212
                    address(superVaultStrategy), // Owner
    213
                    address(superVaultStrategy) // Controller
    214
                );
    215
    ✓ 303.7K
            } else if (hookType == HookType.ApproveAndRequestDeposit7540) {
    216
    ✓ 34.0K
                hookAddress = address(approveAndRequestDeposit7540Hook);
    217
                hookCalldata = abi.encodePacked(
    218
                    bytes32(0), // yieldSourceOracleId placeholder
    219
    ✓ 34.0K
                    _getYieldSource(), // Address of the yield source
    220
    ✓ 34.0K
                    superVault.asset(), // Address of the token to approve
    221
                    amountToInvest, // Amount to request deposit
    222
    ✓ 34.0K
                    address(superVaultStrategy), // Owner
    223
                    address(superVaultStrategy) // Controller
    224
                );
    225
    ✓ 269.6K
            } else if (hookType == HookType.CancelDepositRequest7540) {
    226
    ✓ 40.5K
                hookAddress = address(cancelDepositRequest7540Hook);
    227
                hookCalldata = abi.encodePacked(
    228
                    bytes32(0), // yieldSourceOracleId placeholder
    229
    ✓ 40.5K
                    _getYieldSource(), // Address of the yield source
    230
    ✓ 135.8K
                    address(superVaultStrategy) // Controller
    231
                );
    232
    ✓ 229.2K
            } else if (hookType == HookType.CancelRedeemRequest7540) {
    233
    ✓ 47.6K
                hookAddress = address(cancelRedeemRequest7540Hook);
    234
                hookCalldata = abi.encodePacked(
    235
                    bytes32(0), // yieldSourceOracleId placeholder
    236
    ✓ 47.6K
                    _getYieldSource(), // Address of the yield source
    237
                    address(superVaultStrategy) // Controller
    238
                );
    239
    ✓ 181.6K
            } else if (hookType == HookType.ClaimCancelDepositRequest7540) {
    240
    ✓ 40.3K
                hookAddress = address(claimCancelDepositRequest7540Hook);
    241
                hookCalldata = abi.encodePacked(
    242
                    bytes32(0), // yieldSourceOracleId placeholder
    243
    ✓ 40.3K
                    _getYieldSource(), // Address of the yield source
    244
    ✓ 85.7K
                    address(superVaultStrategy), // Receiver
    245
                    address(superVaultStrategy) // Controller
    246
                );
    247
    ✓ 141.2K
            } else if (hookType == HookType.ClaimCancelRedeemRequest7540) {
    248
    ✓ 45.4K
                hookAddress = address(claimCancelRedeemRequest7540Hook);
    249
                hookCalldata = abi.encodePacked(
    250
                    bytes32(0), // yieldSourceOracleId placeholder
    251
    ✓ 45.4K
                    _getYieldSource(), // Address of the yield source
    252
                    address(superVaultStrategy), // Receiver
    253
                    address(superVaultStrategy) // Controller
    254
                );
    255
    ✓ 95.9K
            } else if (hookType == HookType.Withdraw7540) {
    256
    ✓ 48.1K
                hookAddress = address(withdraw7540Hook);
    257
                hookCalldata = abi.encodePacked(
    258
                    bytes32(0), // yieldSourceOracleId placeholder
    259
    ✓ 48.1K
                    _getYieldSource(), // Address of the yield source
    260
                    amountToInvest, // Amount to withdraw
    261
                    address(superVaultStrategy), // Receiver
    262
                    address(superVaultStrategy), // Owner
    263
                    address(superVaultStrategy) // Controller
    264
                );
    265
    ✓ 47.7K
            } else if (hookType == HookType.CancelRedeem) {
    266
    ✓ 47.7K
                hookAddress = address(cancelRedeemHook);
    267
                hookCalldata = abi.encodePacked(
    268
                    bytes32(0), // yieldSourceOracleId placeholder
    269
    ✓ 47.7K
                    _getYieldSource(), // Address of the yield source
    270
                    address(superVaultStrategy) // Controller
    271
                );
    272
            } else if (hookType == HookType.SuperVaultWithdraw7540) {
    273
                hookAddress = address(superVaultWithdraw7540Hook);
    274
                hookCalldata = abi.encodePacked(
    275
                    bytes32(0), // yieldSourceOracleId placeholder
    276
                    _getYieldSource(), // Address of the yield source
    277
                    amountToInvest, // Amount to withdraw
    278
                    address(superVaultStrategy), // Receiver
    279
                    address(superVaultStrategy), // Owner
    280
                    address(superVaultStrategy) // Controller
    281
                );
    282
            }
    283
        }
    284
    
                                                    
                                                
    285
        function superVaultStrategy_fulfillRedeemRequests_clamped(
    286
            uint256 redeemAmount
    287
        ) public {
    288
            // Find a controller that has pending redeem requests
    289
    ✓ 437.9K
            address selectedController = _getActor();
    290
    ✓ 437.9K
            uint256 pendingAmount = superVaultStrategy.pendingRedeemRequest(
    291
                selectedController
    292
            );
    293
    
                                                    
                                                
    294
            // Clamp using the actor's pending amount
    295
    ✓ 437.9K
            uint256 actualRedeemAmount = redeemAmount % (pendingAmount + 1);
    296
    
                                                    
                                                
    297
    ✓ 437.9K
            address[] memory controllers = new address[](1);
    298
    ✓ 437.9K
            controllers[0] = selectedController;
    299
    
                                                    
                                                
    300
            // Determine yield source type from currently active yield source
    301
    ✓ 996.5K
            YieldSourceType activeYieldSourceType = _getYieldSourceTypeFromAddress(
    302
    ✓ 437.9K
                _getYieldSource()
    303
            );
    304
    ✓ 437.9K
            address redeemHook = _getRedeemHookForType(activeYieldSourceType);
    305
    
                                                    
                                                
    306
            // Create realistic hook calldata for redeem operation
    307
    ✓ 437.9K
            bytes memory redeemHookCalldata;
    308
    
                                                    
                                                
    309
            if (
    310
    ✓ 437.9K
                activeYieldSourceType == YieldSourceType.ERC4626 ||
    311
    ✓ 44.1K
                activeYieldSourceType == YieldSourceType.ERC5115
    312
            ) {
    313
                // ERC4626/ERC5115 Layout: bytes32 oracleId, address yieldSource, address owner, uint256 shares, bool usePrevAmount
    314
                redeemHookCalldata = abi.encodePacked(
    315
    ✓ 418.4K
                    bytes32(0), // yieldSourceOracleId placeholder
    316
    ✓ 418.4K
                    _getYieldSource(), // Current active yield source
    317
    ✓ 418.4K
                    address(superVaultStrategy), // Owner (strategy owns the yield source shares)
    318
    ✓ 418.4K
                    actualRedeemAmount, // Amount to redeem (matches controller's pending request)
    319
                    false // Don't use previous hook amount
    320
                );
    321
            } else {
    322
                // ERC7540 Layout: bytes32 oracleId, address yieldSource, uint256 shares, bool usePrevAmount
    323
                redeemHookCalldata = abi.encodePacked(
    324
    ✓ 19.5K
                    bytes32(0), // yieldSourceOracleId placeholder
    325
    ✓ 19.5K
                    _getYieldSource(), // Current active yield source
    326
    ✓ 19.5K
                    actualRedeemAmount, // Amount to redeem (matches controller's pending request)
    327
    ✓ 19.5K
                    false // Don't use previous hook amount
    328
                );
    329
            }
    330
    
                                                    
                                                
    331
            // Create arrays for FulfillArgs
    332
    ✓ 437.9K
            address[] memory hooks = new address[](1);
    333
    ✓ 437.9K
            hooks[0] = redeemHook;
    334
    
                                                    
                                                
    335
    ✓ 437.9K
            bytes[] memory hookCalldata = new bytes[](1);
    336
    ✓ 437.9K
            hookCalldata[0] = redeemHookCalldata;
    337
    
                                                    
                                                
    338
    ✓ 437.9K
            uint256[] memory expectedAssetsOrSharesOut = new uint256[](1);
    339
    ✓ 437.9K
            expectedAssetsOrSharesOut[0] = 1; // @audit Allow max losse amount matching the actual redeem
    340
    
                                                    
                                                
    341
    ✓ 437.9K
            bytes32[][] memory globalProofs = new bytes32[][](1);
    342
    ✓ 437.9K
            globalProofs[0] = new bytes32[](0); // Empty proof for UnsafeSuperVaultAggregator
    343
    
                                                    
                                                
    344
    ✓ 437.9K
            bytes32[][] memory strategyProofs = new bytes32[][](1);
    345
    ✓ 437.9K
            strategyProofs[0] = new bytes32[](0); // Empty proof
    346
    
                                                    
                                                
    347
            // Create the FulfillArgs struct
    348
    ✓ 437.9K
            ISuperVaultStrategy.FulfillArgs memory fulfillArgs = ISuperVaultStrategy
    349
                .FulfillArgs({
    350
    ✓ 437.9K
                    controllers: controllers,
    351
    ✓ 437.9K
                    hooks: hooks,
    352
    ✓ 437.9K
                    hookCalldata: hookCalldata,
    353
    ✓ 437.9K
                    expectedAssetsOrSharesOut: expectedAssetsOrSharesOut,
    354
    ✓ 437.9K
                    globalProofs: globalProofs,
    355
    ✓ 437.9K
                    strategyProofs: strategyProofs
    356
                });
    357
    
                                                    
                                                
    358
            // Execute the function
    359
    ✓ 437.9K
            superVaultStrategy_fulfillRedeemRequests_ASSERTION_STRATEGY_NO_LOSS_ON_FULFILLMENT(fulfillArgs);
    360
        }
    361
    
                                                    
                                                
    362
        /// AUTO GENERATED TARGET FUNCTIONS - WARNING: DO NOT DELETE OR MODIFY THIS LINE ///
    363
    
                                                    
                                                
    364
        function superVaultStrategy_executeHooks(
    365
            ISuperVaultStrategy.ExecuteArgs memory args
    366
        ) public payable asAdmin {
    367
    ✓ 209.7K
            superVaultStrategy.executeHooks{value: msg.value}(args);
    368
    
                                                    
                                                
    369
    ✓ 1.2K
            executeHooksSuccess = true;
    370
        }
    371
    
                                                    
                                                
    372
        /// @dev Property: superVaultStrategy does not incur loss on fulfillment
    373
        function superVaultStrategy_fulfillRedeemRequests_ASSERTION_STRATEGY_NO_LOSS_ON_FULFILLMENT(
    374
            ISuperVaultStrategy.FulfillArgs memory args
    375
    ✓ 535.5K
        ) public updateGhostsWithOpType(OpType.FULFILL) {
    376
    ✓ 535.5K
            uint256 summedExpectedAssets;
    377
    ✓ 1.2M
            for (uint256 i; i < args.expectedAssetsOrSharesOut.length; i++) {
    378
    ✓ 721.1K
                summedExpectedAssets += args.expectedAssetsOrSharesOut[i];
    379
            }
    380
    
                                                    
                                                
    381
            // no need to prank because called as admin address(this)
    382
    ✓ 440.2K
            superVaultStrategy.fulfillRedeemRequests(args);
    383
    
                                                    
                                                
    384
    ✓ 263.1K
            uint256 assetBalanceAfter = MockERC20(superVault.asset()).balanceOf(
    385
    ✓ 263.1K
                address(superVaultStrategy)
    386
            );
    387
    
                                                    
                                                
    388
    ✓ 263.1K
            gte(
    389
    ✓ 263.1K
                assetBalanceAfter,
    390
    ✓ 263.1K
                summedExpectedAssets,
    391
    ✓ 263.1K
                ASSERTION_STRATEGY_NO_LOSS_ON_FULFILLMENT
    392
            );
    393
        }
    394
    
                                                    
                                                
    395
        // Functions that require SuperGovernor access
    396
        /// @dev removed because we're bypassing hook validation
    397
        // function superVaultAggregator_setHooksRootUpdateTimelock(
    398
        //     uint256 newTimelock
    399
        // ) public asAdmin {
    400
        //     superVaultAggregator.setHooksRootUpdateTimelock(newTimelock);
    401
        // }
    402
    
                                                    
                                                
    403
        /// @dev removed because we're bypassing hook validation
    404
        // function superVaultAggregator_proposeGlobalHooksRoot(
    405
        //     bytes32 newRoot
    406
        // ) public asAdmin {
    407
        //     superVaultAggregator.proposeGlobalHooksRoot(newRoot);
    408
        // }
    409
    
                                                    
                                                
    410
        /// @dev removed because we're bypassing hook validation
    411
        // function superVaultAggregator_executeGlobalHooksRootUpdate()
    412
        //     public
    413
        //     asAdmin
    414
        // {
    415
        //     superVaultAggregator.executeGlobalHooksRootUpdate();
    416
        // }
    417
    
                                                    
                                                
    418
        /// @dev removed because we're bypassing hook validation
    419
        // function superVaultAggregator_setGlobalHooksRootVetoStatus(
    420
        //     bool vetoed
    421
        // ) public asAdmin {
    422
        //     superVaultAggregator.setGlobalHooksRootVetoStatus(vetoed);
    423
        // }
    424
    
                                                    
                                                
    425
        /// @dev removed because we're bypassing hook validation
    426
        // function superVaultAggregator_setStrategyHooksRootVetoStatus(
    427
        //     address strategy,
    428
        //     bool vetoed
    429
        // ) public asAdmin {
    430
        //     superVaultAggregator.setStrategyHooksRootVetoStatus(strategy, vetoed);
    431
        // }
    432
    
                                                    
                                                
    433
        function superVaultAggregator_changePrimaryManager(
    434
            address strategy,
    435
            address newManager
    436
        ) public asAdmin {
    437
    ✓ 172.2K
            superVaultAggregator.changePrimaryManager(strategy, newManager);
    438
        }
    439
    
                                                    
                                                
    440
        /// @dev won't achieve coverage until issue outlined here is resolved: https://github.com/Recon-Fuzz/superform-review/issues/5
    441
        function superVaultAggregator_slashStake(
    442
            address manager,
    443
            uint256 amount
    444
        ) public asAdmin {
    445
    ✓ 240.1K
            superVaultAggregator.slashStake(manager, amount);
    446
        }
    447
    
                                                    
                                                
    448
        /// Helpers
    449
    
                                                    
                                                
    450
        function _requestedSharesForControllers(
    451
            address[] memory controllers
    452
        ) internal returns (uint256) {
    453
            uint256 totalRequested;
    454
            for (uint256 i; i < controllers.length; i++) {
    455
                totalRequested += superVault.pendingRedeemRequest(
    456
                    0,
    457
                    controllers[i]
    458
                );
    459
            }
    460
    
                                                    
                                                
    461
            return totalRequested;
    462
        }
    463
    
                                                    
                                                
    464
        function _sumSuperVaultValsForControllers(
    465
            address[] memory controllers
    466
        )
    467
            internal
    468
            view
    469
            returns (uint256 sumAccumulatorShares, uint256 sumAccumulatorCostBasis)
    470
        {
    471
            for (uint256 i; i < controllers.length; i++) {
    472
                sumAccumulatorShares += superVaultStrategy
    473
                    .getSuperVaultState(controllers[i])
    474
                    .accumulatorShares;
    475
                sumAccumulatorCostBasis += superVaultStrategy
    476
                    .getSuperVaultState(controllers[i])
    477
                    .accumulatorCostBasis;
    478
            }
    479
        }
    480
    
                                                    
                                                
    481
        function _claimableMoreThanInvested(
    482
            uint256 totalAmountToDeposit
    483
    ✓ 90.6K
        ) internal returns (bool) {
    484
    ✓ 90.6K
            address[] memory actors = _getActors();
    485
    ✓ 90.6K
            uint256 totalClaimable;
    486
    ✓ 362.3K
            for (uint256 i; i < actors.length; i++) {
    487
    ✓ 271.7K
                uint256 claimableRedemptions = superVault.claimableRedeemRequest(
    488
                    0,
    489
    ✓ 271.7K
                    actors[i]
    490
                );
    491
    ✓ 271.7K
                uint256 claimableRedemptionsAsAssets = superVault.convertToAssets(
    492
                    claimableRedemptions
    493
                );
    494
    ✓ 271.7K
                totalClaimable += claimableRedemptionsAsAssets;
    495
            }
    496
    
                                                    
                                                
    497
    ✓ 90.6K
            uint256 currentStrategyBalance = MockERC20(superVault.asset())
    498
    ✓ 90.6K
                .balanceOf(address(superVaultStrategy));
    499
    
                                                    
                                                
    500
            // Don't allow investing more than the claimable amount
    501
    ✓ 90.6K
            if (totalAmountToDeposit > totalClaimable) {
    502
    ✓ 9.1K
                return true;
    503
            }
    504
    
                                                    
                                                
    505
            // Ensure strategy has sufficient assets remaining after investment to cover claimable amounts
    506
    ✓ 81.4K
            uint256 remainingStrategyBalance = currentStrategyBalance -
    507
    ✓ 81.4K
                totalAmountToDeposit;
    508
    ✓ 80.0K
            if (remainingStrategyBalance < totalClaimable) {
    509
    ✓ 1.3K
                return true;
    510
            }
    511
    
                                                    
                                                
    512
    ✓ 78.7K
            return false;
    513
        }
    514
    }
    515
    
                                                    
                                                
    87.9% test/recon/targets/DoomsdayTargets.sol
    Lines covered: 304 / 346 (87.9%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    5
    import {vm} from "@chimera/Hevm.sol";
    6
    import {Panic} from "@recon/Panic.sol";
    7
    import {MockERC20} from "@recon/MockERC20.sol";
    8
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    9
    
                                                    
                                                
    10
    import {BeforeAfter} from "../BeforeAfter.sol";
    11
    import {Properties} from "../Properties.sol";
    12
    import {ISuperVaultStrategy} from "src/interfaces/SuperVault/ISuperVaultStrategy.sol";
    13
    import {YieldSourceType} from "test/recon/managers/YieldManager.sol";
    14
    import {MockERC4626Tester} from "test/recon/mocks/MockERC4626Tester.sol";
    15
    import {MockERC5115Tester} from "test/recon/mocks/MockERC5115Tester.sol";
    16
    import {MockERC7540Tester} from "test/recon/mocks/MockERC7540Tester.sol";
    17
    
                                                    
                                                
    18
    abstract contract DoomsdayTargets is BaseTargetFunctions, Properties {
    19
        /// @dev Property: previewDeposit and deposit equivalence
    20
        function doomsday_previewDepositEquivalence_ASSERTION_PREVIEW_DEPOSIT_EQUIVALENCE(
    21
            uint256 assets
    22
        ) public stateless {
    23
    ✓ 124.2K
            uint256 previewDepositShares = superVault.previewDeposit(assets);
    24
    
                                                    
                                                
    25
    ✓ 119.9K
            vm.prank(_getActor());
    26
    ✓ 119.9K
            uint256 sharesActualDeposit = superVault.deposit(assets, _getActor());
    27
    
                                                    
                                                
    28
    ✓ 11.1K
            eq(
    29
    ✓ 11.1K
                previewDepositShares,
    30
    ✓ 11.1K
                sharesActualDeposit,
    31
    ✓ 11.1K
                ASSERTION_PREVIEW_DEPOSIT_EQUIVALENCE
    32
            );
    33
        }
    34
    
                                                    
                                                
    35
        /// @dev Property: previewMint and mint equivalence
    36
        function doomsday_previewMintEquivalence_ASSERTION_PREVIEW_MINT_EQUIVALENCE(uint256 shares) public stateless {
    37
    ✓ 116.5K
            uint256 previewMintAssets = superVault.previewMint(shares);
    38
    
                                                    
                                                
    39
    ✓ 85.7K
            vm.prank(_getActor());
    40
    ✓ 85.7K
            uint256 assetsActualMint = superVault.mint(shares, _getActor());
    41
    
                                                    
                                                
    42
    ✓ 17.7K
            eq(
    43
    ✓ 17.7K
                previewMintAssets,
    44
    ✓ 17.7K
                assetsActualMint,
    45
    ✓ 17.7K
                ASSERTION_PREVIEW_MINT_EQUIVALENCE
    46
            );
    47
        }
    48
    
                                                    
                                                
    49
        /// @dev Property: mint/redeem doesn't cause loss to user
    50
        function doomsday_mintRedeemSymmetrical_ASSERTION_MINT_REDEEM_SYMMETRICAL(
    51
            uint256 sharesToMint
    52
        ) public stateless {
    53
            // skip if there's been any gain because it complicates the assertion checking
    54
            // NOTE: removed because was previously checking that user doesn't gain only from minting/redeeming
    55
            // if (MockERC4626Tester(_getYieldSource()).totalGains() > 0) {
    56
            //     return;
    57
            // }
    58
    ✓ 132.8K
            address asset = superVault.asset();
    59
    ✓ 132.8K
            uint256 balanceBefore = MockERC20(asset).balanceOf(_getActor());
    60
    ✓ 132.8K
            uint256 feeRecipientBalanceBefore = MockERC20(asset).balanceOf(
    61
    ✓ 132.8K
                feeRecipient
    62
            );
    63
    
                                                    
                                                
    64
            // 1. Mint
    65
    ✓ 132.8K
            vm.prank(_getActor());
    66
    ✓ 132.8K
            uint256 assetsUsed = superVault.mint(sharesToMint, _getActor());
    67
    
                                                    
                                                
    68
            // 2. Deposit assets into yield strategy via executeHooks
    69
            // This is needed because the user's assets are currently in the strategy contract
    70
            // but not yet deposited into the underlying yield source
    71
    ✓ 50.1K
            uint256 strategyAssetBalance = MockERC20(asset).balanceOf(
    72
    ✓ 50.1K
                address(superVaultStrategy)
    73
            );
    74
    ✓ 50.1K
            if (strategyAssetBalance > 0) {
    75
    ✓ 50.1K
                ISuperVaultStrategy.ExecuteArgs
    76
    ✓ 50.1K
                    memory depositArgs = _createExecuteDepositArgs(
    77
    ✓ 50.1K
                        strategyAssetBalance
    78
                    );
    79
    
                                                    
                                                
    80
    ✓ 50.1K
                superVaultStrategy.executeHooks(depositArgs);
    81
            }
    82
    
                                                    
                                                
    83
            // 3. Request Redemption
    84
    ✓ 12.5K
            uint256 userShares = superVault.balanceOf(_getActor());
    85
            // get shares of strategy in the deposited yield source
    86
    ✓ 12.5K
            uint256 shares = _getSuperVaultStrategyShares();
    87
    
                                                    
                                                
    88
    ✓ 12.5K
            vm.prank(_getActor());
    89
    ✓ 12.5K
            superVault.requestRedeem(shares, _getActor(), _getActor());
    90
    
                                                    
                                                
    91
    ✓ 11.6K
            uint256 feeBalanceBefore = MockERC20(superVault.asset()).balanceOf(
    92
    ✓ 11.6K
                feeRecipient
    93
            );
    94
    
                                                    
                                                
    95
            // 4. Fulfill Redemption from yield strategy
    96
            // Now we need to redeem from the yield strategy to get assets back
    97
    ✓ 11.6K
            ISuperVaultStrategy.FulfillArgs
    98
    ✓ 11.6K
                memory fulfillArgs = _createFulfillRedeemFromStrategyArgs(shares);
    99
    
                                                    
                                                
    100
            // called by admin address(this)
    101
    ✓ 11.6K
            superVaultStrategy.fulfillRedeemRequests(fulfillArgs);
    102
    
                                                    
                                                
    103
            // 5. Claim Redemption
    104
    ✓ 8.8K
            uint256 sharesToRedeem = superVault.maxRedeem(_getActor());
    105
    
                                                    
                                                
    106
    ✓ 8.8K
            vm.prank(_getActor());
    107
    ✓ 8.8K
            superVault.redeem(sharesToRedeem, _getActor(), _getActor());
    108
    
                                                    
                                                
    109
    ✓ 7.7K
            uint256 balanceAfter = MockERC20(asset).balanceOf(_getActor());
    110
    
                                                    
                                                
    111
    ✓ 7.7K
            uint256 TOLERANCE = 10; // 10 wei max tolerance of assets lost
    112
    
                                                    
                                                
    113
    ✓ 7.7K
            uint256 feeRecipientBalanceAfter = MockERC20(superVault.asset())
    114
    ✓ 7.7K
                .balanceOf(feeRecipient);
    115
    ✓ 7.7K
            uint256 feeDelta = feeRecipientBalanceAfter - feeRecipientBalanceBefore;
    116
    
                                                    
                                                
    117
            // 6. Check that user didn't lose assets
    118
    ✓ 7.7K
            gte(
    119
    ✓ 7.7K
                balanceAfter + TOLERANCE + feeDelta,
    120
    ✓ 7.7K
                balanceBefore,
    121
    ✓ 7.7K
                ASSERTION_MINT_REDEEM_SYMMETRICAL
    122
            );
    123
        }
    124
    
                                                    
                                                
    125
        /// @dev Property: deposit/withdraw doesn't cause loss to user
    126
        function doomsday_depositWithdrawSymmetrical_ASSERTION_DEPOSIT_WITHDRAW_SYMMETRICAL(
    127
            uint256 assetsToDeposit
    128
    ✓ 103.0K
        ) public stateless returns (uint256, uint256) {
    129
            // skip if there's been any gain because it complicates the assertion checking
    130
            // NOTE: removed because was previously checking that user doesn't gain only from minting/redeeming
    131
            // if (MockERC4626Tester(_getYieldSource()).totalGains() > 0) {
    132
            //     return;
    133
            // }
    134
    ✓ 103.0K
            address asset = superVault.asset();
    135
    ✓ 103.0K
            uint256 balanceBefore = MockERC20(asset).balanceOf(_getActor());
    136
    
                                                    
                                                
    137
            // 1. Deposit
    138
    ✓ 103.0K
            vm.prank(_getActor());
    139
    ✓ 103.0K
            superVault.deposit(assetsToDeposit, _getActor());
    140
    
                                                    
                                                
    141
            // 2. Request Withdrawal (through redemption in ERC7540)
    142
    ✓ 19.4K
            uint256 shares = superVault.balanceOf(_getActor());
    143
    ✓ 19.4K
            vm.prank(_getActor());
    144
    ✓ 19.4K
            superVault.requestRedeem(shares, _getActor(), _getActor());
    145
    
                                                    
                                                
    146
            // 3. Fulfill Withdrawal
    147
    ✓ 19.4K
            ISuperVaultStrategy.FulfillArgs
    148
    ✓ 19.4K
                memory fulfillArgs = _createFulfillRedeemArgs(shares);
    149
            // fulfills as admin (address(this))
    150
    ✓ 19.4K
            superVaultStrategy.fulfillRedeemRequests(fulfillArgs);
    151
    
                                                    
                                                
    152
            // 4. Claim Withdrawal
    153
    ✓ 6.2K
            uint256 withdrawableAssets = superVault.maxWithdraw(_getActor());
    154
    ✓ 6.2K
            vm.prank(_getActor());
    155
    ✓ 6.2K
            superVault.withdraw(withdrawableAssets, _getActor(), _getActor());
    156
    
                                                    
                                                
    157
    ✓ 6.2K
            uint256 balanceAfter = MockERC20(asset).balanceOf(_getActor());
    158
    
                                                    
                                                
    159
    ✓ 6.2K
            uint256 TOLERANCE = 10; // 10 wei max tolerance of assets lost
    160
            // 5. Check that user didn't lose assets
    161
    ✓ 6.2K
            gte(
    162
    ✓ 6.2K
                balanceAfter + TOLERANCE,
    163
    ✓ 6.2K
                balanceBefore,
    164
    ✓ 6.2K
                ASSERTION_DEPOSIT_WITHDRAW_SYMMETRICAL
    165
            );
    166
    
                                                    
                                                
    167
    ✓ 2.1K
            return (balanceAfter, balanceBefore);
    168
        }
    169
    
                                                    
                                                
    170
        /// @dev Property: maxRedeem is reset to 0 after full redemption
    171
        /// @dev Property: redeeming maxRedeem shouldn't revert
    172
        function doomsday_maxRedeemResetsAfterFullRedemption_ASSERTION_MAX_REDEEM_RESETS_AFTER_FULL_REDEMPTION(
    173
            uint256 sharesToMint
    174
        ) public stateless {
    175
            // 1. Deposit to get shares
    176
    ✓ 182.1K
            vm.prank(_getActor());
    177
    ✓ 182.1K
            superVault.mint(sharesToMint, _getActor());
    178
    
                                                    
                                                
    179
            // redeem all user shares
    180
    ✓ 88.0K
            uint256 shares = superVault.balanceOf(_getActor());
    181
    
                                                    
                                                
    182
            // 2. Request full redemption
    183
    ✓ 88.0K
            vm.prank(_getActor());
    184
    ✓ 88.0K
            superVault.requestRedeem(shares, _getActor(), _getActor());
    185
    
                                                    
                                                
    186
            // 3. Fulfill the redemption request
    187
    ✓ 88.0K
            ISuperVaultStrategy.FulfillArgs
    188
    ✓ 88.0K
                memory fulfillArgs = _createFulfillRedeemArgs(shares);
    189
            // fulfill as address(this)
    190
    ✓ 88.0K
            superVaultStrategy.fulfillRedeemRequests(fulfillArgs);
    191
    
                                                    
                                                
    192
            // 4. Check maxRedeem before claiming
    193
    ✓ 62.9K
            uint256 maxRedeemBeforeClaim = superVault.maxRedeem(_getActor());
    194
    
                                                    
                                                
    195
            // 5. Claim the full redemption
    196
    ✓ 62.9K
            vm.prank(_getActor());
    197
    ✓ 62.9K
            try superVault.redeem(maxRedeemBeforeClaim, _getActor(), _getActor()) {
    198
                // 6. Check maxRedeem is reset to 0 after full redemption
    199
    ✓ 23.4K
                uint256 maxRedeemAfterClaim = superVault.maxRedeem(_getActor());
    200
    ✓ 23.4K
                eq(
    201
    ✓ 23.4K
                    maxRedeemAfterClaim,
    202
    ✓ 23.4K
                    0,
    203
    ✓ 23.4K
                    ASSERTION_MAX_REDEEM_RESETS_AFTER_FULL_REDEMPTION
    204
                );
    205
            } catch {
    206
    ✓ 39.5K
                if (maxRedeemBeforeClaim > 0) {
    207
    ✓ 39.5K
                    t(false, ASSERTION_MAX_REDEEM_RESETS_AFTER_FULL_REDEMPTION);
    208
                }
    209
            }
    210
        }
    211
    
                                                    
                                                
    212
        /// @dev Property: maxWithdraw is reset to 0 after full withdrawal
    213
        function doomsday_maxWithdrawResetsAfterFullWithdrawal_ASSERTION_MAX_WITHDRAW_RESETS_AFTER_FULL_WITHDRAWAL(
    214
            uint256 assetsToDeposit
    215
        ) public stateless {
    216
            // 1. Deposit to get shares
    217
    ✓ 178.3K
            vm.prank(_getActor());
    218
    ✓ 178.3K
            superVault.deposit(assetsToDeposit, _getActor());
    219
    
                                                    
                                                
    220
    ✓ 50.7K
            uint256 shares = superVault.balanceOf(_getActor());
    221
    
                                                    
                                                
    222
            // 2. Request redemption of all shares
    223
    ✓ 50.7K
            vm.prank(_getActor());
    224
    ✓ 50.7K
            superVault.requestRedeem(shares, _getActor(), _getActor());
    225
    
                                                    
                                                
    226
            // 3. Fulfill the redemption request
    227
    ✓ 50.7K
            ISuperVaultStrategy.FulfillArgs
    228
    ✓ 50.7K
                memory fulfillArgs = _createFulfillRedeemArgs(shares);
    229
            // called as admin address(this)
    230
    ✓ 50.7K
            superVaultStrategy.fulfillRedeemRequests(fulfillArgs);
    231
    
                                                    
                                                
    232
            // 4. Check maxWithdraw after fulfillment and use that value
    233
    ✓ 37.0K
            uint256 maxWithdrawBefore = superVault.maxWithdraw(_getActor());
    234
    
                                                    
                                                
    235
            // 5. Withdraw the exact amount returned by maxWithdraw
    236
    ✓ 37.0K
            vm.prank(_getActor());
    237
    ✓ 37.0K
            try superVault.withdraw(maxWithdrawBefore, _getActor(), _getActor()) {
    238
                // 6. Check maxWithdraw is reset to 0 after full withdrawal
    239
    ✓ 37.0K
                uint256 maxWithdrawAfter = superVault.maxWithdraw(_getActor());
    240
    ✓ 37.0K
                eq(
    241
    ✓ 37.0K
                    maxWithdrawAfter,
    242
    ✓ 37.0K
                    0,
    243
    ✓ 37.0K
                    ASSERTION_MAX_WITHDRAW_RESETS_AFTER_FULL_WITHDRAWAL
    244
                );
    245
            } catch {
    246
                t(false, ASSERTION_MAX_WITHDRAW_RESETS_AFTER_FULL_WITHDRAWAL);
    247
            }
    248
        }
    249
    
                                                    
                                                
    250
        /// @dev Property: fulfillRedeemRequests doesn't redeem more than requested for multiple actors
    251
        function doomsday_fulfillDoesntOverRedeemMultipleActors_ASSERTION_FULFILL_DOESNT_OVER_REDEEM_MULTIPLE_ACTORS(
    252
            uint256[3] memory sharesToMint,
    253
            uint256[3] memory actorIndexes
    254
        ) public stateless {
    255
    ✓ 120.6K
            address[] memory actors = _getActors();
    256
    ✓ 120.6K
            if (actors.length < 3) return; // Need at least 3 actors for this test
    257
    
                                                    
                                                
    258
            // Arrays to track actors and their requests
    259
    ✓ 120.6K
            address[] memory testActors = new address[](3);
    260
    ✓ 120.6K
            uint256[] memory requestedShares = new uint256[](3);
    261
    ✓ 120.6K
            uint256[] memory sharesBefore = new uint256[](3);
    262
    
                                                    
                                                
    263
            // 1. Setup: Each actor deposits and requests redemption
    264
    ✓ 120.6K
            uint256 totalRequestedShares;
    265
    ✓ 149.1K
            for (uint256 i = 0; i < 3; i++) {
    266
                // Get unique actor
    267
    ✓ 148.0K
                testActors[i] = actors[actorIndexes[i] % actors.length];
    268
    
                                                    
                                                
    269
                // Mint shares for this actor
    270
    ✓ 148.0K
                if (sharesToMint[i] > 0) {
    271
    ✓ 146.1K
                    vm.prank(testActors[i]);
    272
    ✓ 146.1K
                    superVault.mint(sharesToMint[i], testActors[i]);
    273
                }
    274
    
                                                    
                                                
    275
                // Get actual share balance
    276
    ✓ 29.8K
                sharesBefore[i] = superVault.maxRedeem(testActors[i]);
    277
    ✓ 29.8K
                requestedShares[i] = sharesBefore[i];
    278
    
                                                    
                                                
    279
                // Request redemption of all shares
    280
    ✓ 29.8K
                if (requestedShares[i] > 0) {
    281
    ✓ 2.6K
                    vm.prank(testActors[i]);
    282
    ✓ 2.6K
                    superVault.requestRedeem(
    283
    ✓ 2.6K
                        requestedShares[i],
    284
    ✓ 2.6K
                        testActors[i],
    285
    ✓ 2.6K
                        testActors[i]
    286
                    );
    287
                }
    288
    ✓ 28.6K
                totalRequestedShares += requestedShares[i];
    289
            }
    290
    
                                                    
                                                
    291
            // 2. Create multi-actor FulfillArgs
    292
    ✓ 1.1K
            ISuperVaultStrategy.FulfillArgs
    293
    ✓ 1.1K
                memory fulfillArgs = _createMultiActorFulfillArgs(
    294
    ✓ 1.1K
                    testActors,
    295
    ✓ 1.1K
                    requestedShares
    296
                );
    297
    
                                                    
                                                
    298
            // 3. Calculate total pending before
    299
    ✓ 1.1K
            uint256 totalPendingBefore;
    300
    ✓ 4.5K
            for (uint256 i = 0; i < 3; i++) {
    301
    ✓ 3.4K
                totalPendingBefore += superVault.pendingRedeemRequest(
    302
                    0,
    303
    ✓ 3.4K
                    testActors[i]
    304
                );
    305
            }
    306
    
                                                    
                                                
    307
            // 4. Fulfill all redemption requests at once
    308
            // fulfill as address(this)
    309
    ✓ 1.1K
            superVaultStrategy.fulfillRedeemRequests(fulfillArgs);
    310
    
                                                    
                                                
    311
            // 5. Calculate total pending after
    312
            uint256 totalPendingAfter;
    313
            for (uint256 i = 0; i < 3; i++) {
    314
                totalPendingAfter += superVault.pendingRedeemRequest(
    315
                    0,
    316
                    testActors[i]
    317
                );
    318
            }
    319
    
                                                    
                                                
    320
            lte(
    321
                totalPendingBefore - totalPendingAfter,
    322
                totalRequestedShares,
    323
                ASSERTION_FULFILL_DOESNT_OVER_REDEEM_MULTIPLE_ACTORS
    324
            );
    325
        }
    326
    
                                                    
                                                
    327
        /// @dev Property: primary manager can always be replaced by governance via `changePrimaryManager`
    328
        function doomsday_primaryManagerAlwaysChangeable_ASSERTION_PRIMARY_MANAGER_ALWAYS_CHANGEABLE() public stateless {
    329
    ✓ 149.6K
            address strategy = address(superVaultStrategy);
    330
    ✓ 149.6K
            address newManager = _getActor();
    331
    
                                                    
                                                
    332
            // Since address(this) has SUPER_GOVERNOR_ROLE, this should always succeed
    333
    ✓ 149.6K
            vm.prank(address(this));
    334
    ✓ 149.6K
            try superGovernor.changePrimaryManager(strategy, newManager) {
    335
                // Call succeeded - this is expected behavior
    336
            } catch (bytes memory err) {
    337
                bool expectedError;
    338
                expectedError = checkError(err, "MANAGER_TAKEOVERS_FROZEN()"); // custom error
    339
                t(
    340
                    !expectedError,
    341
                    ASSERTION_PRIMARY_MANAGER_ALWAYS_CHANGEABLE
    342
                );
    343
            }
    344
        }
    345
    
                                                    
                                                
    346
        /// @dev Property: all users can withdraw (solvency)
    347
        // NOTE: if withdrawing from a given strategy via fulfillRedeemRequests fails, it can be expected that one of the YieldSourceTargets would be called to switch the yield source
    348
        // this should allow fulfillments to eventually succeed so we don't need to sort through all yield sources that have currently been deposited into before fulfilling
    349
        function doomsday_allUsersCanWithdraw_ASSERTION_ALL_USERS_CAN_WITHDRAW_WHEN_UNPAUSED() public stateless {
    350
    ✓ 103.0K
            address[] memory actors = _getActors();
    351
    ✓ 103.0K
            bool paused = superVaultAggregator.isStrategyPaused(
    352
    ✓ 103.0K
                address(superVaultStrategy)
    353
            );
    354
    
                                                    
                                                
    355
            // request redemption for all actors
    356
    ✓ 115.7K
            for (uint256 i; i < actors.length; i++) {
    357
    ✓ 115.7K
                uint256 redeemableShares = superVault.balanceOf(actors[i]);
    358
    
                                                    
                                                
    359
    ✓ 115.7K
                vm.prank(actors[i]);
    360
    ✓ 115.7K
                superVault.requestRedeem(redeemableShares, actors[i], actors[i]);
    361
            }
    362
    
                                                    
                                                
    363
            // fulfill redemption for all actors
    364
            for (uint256 i; i < actors.length; i++) {
    365
                uint256 redeemableShares = superVault.pendingRedeemRequest(
    366
                    0,
    367
                    actors[i]
    368
                );
    369
    
                                                    
                                                
    370
                // switch the actor
    371
                _switchActor(i);
    372
    
                                                    
                                                
    373
                ISuperVaultStrategy.FulfillArgs
    374
                    memory fulfillArgs = _fulfillRedeemRequestsArgs(
    375
                        redeemableShares
    376
                    );
    377
                superVaultStrategy.fulfillRedeemRequests(fulfillArgs);
    378
            }
    379
    
                                                    
                                                
    380
            // try to withdraw max possible for all actors
    381
            for (uint256 i; i < actors.length; i++) {
    382
                uint256 withdrawable = superVault.maxWithdraw(actors[i]);
    383
    
                                                    
                                                
    384
                if (withdrawable > 0 && !paused) {
    385
                    vm.prank(actors[i]);
    386
                    try
    387
                        superVault.withdraw(withdrawable, actors[i], actors[i])
    388
                    {} catch {
    389
                        // if user can't maxWithdraw there's most likely an insolvency issue related to the TOLERANCE_CONSTANT
    390
                        t(
    391
                            false,
    392
                            ASSERTION_ALL_USERS_CAN_WITHDRAW_WHEN_UNPAUSED
    393
                        );
    394
                    }
    395
                }
    396
            }
    397
        }
    398
    
                                                    
                                                
    399
        /// @dev Property: Claiming redemptions should never revert with INVALID_REDEEM_CLAIM
    400
        function doomsday_redemptionsNeverReverts_ASSERTION_REDEEM_SHOULD_NOT_REVERT_INVALID_REDEEM_CLAIM(
    401
            uint256 shares
    402
        ) public asActor stateless {
    403
    ✓ 116.9K
            try superVault.redeem(shares, _getActor(), _getActor()) {} catch (
    404
                bytes memory err
    405
            ) {
    406
    ✓ 114.4K
                bool unexpectedError = checkError(err, "INVALID_REDEEM_CLAIM()");
    407
    ✓ 114.4K
                t(
    408
    ✓ 114.4K
                    !unexpectedError,
    409
    ✓ 114.4K
                    ASSERTION_REDEEM_SHOULD_NOT_REVERT_INVALID_REDEEM_CLAIM
    410
                );
    411
            }
    412
        }
    413
    
                                                    
                                                
    414
        /// @dev Get shares for SuperVaultStrategy in the current yield source
    415
    ✓ 12.5K
        function _getSuperVaultStrategyShares() internal view returns (uint256) {
    416
    ✓ 12.5K
            address yieldSource = _getYieldSource();
    417
    ✓ 12.5K
            YieldSourceType sourceType = _getYieldSourceTypeFromAddress(
    418
    ✓ 12.5K
                yieldSource
    419
            );
    420
    
                                                    
                                                
    421
    ✓ 12.5K
            if (sourceType == YieldSourceType.ERC4626) {
    422
                return
    423
    ✓ 12.5K
                    MockERC4626Tester(yieldSource).balanceOf(
    424
    ✓ 12.5K
                        address(superVaultStrategy)
    425
                    );
    426
            } else if (sourceType == YieldSourceType.ERC5115) {
    427
                return
    428
                    MockERC5115Tester(yieldSource).balanceOf(
    429
                        address(superVaultStrategy)
    430
                    );
    431
            } else if (sourceType == YieldSourceType.ERC7540) {
    432
                return
    433
                    MockERC7540Tester(yieldSource).balanceOf(
    434
                        address(superVaultStrategy)
    435
                    );
    436
            } else {
    437
                revert("Invalid yield source type");
    438
            }
    439
        }
    440
    
                                                    
                                                
    441
        // Helpers
    442
    
                                                    
                                                
    443
        /// @dev Helper function to clamp the values for the function call
    444
        function _fulfillRedeemRequestsArgs(
    445
            uint256 redeemAmount
    446
    ✓ 135.6K
        ) public returns (ISuperVaultStrategy.FulfillArgs memory fulfillArgs) {
    447
            // Find a controller that has pending redeem requests
    448
    ✓ 135.6K
            address selectedController = _getActor();
    449
    ✓ 135.6K
            uint256 pendingAmount = superVaultStrategy.pendingRedeemRequest(
    450
                selectedController
    451
            );
    452
    
                                                    
                                                
    453
            // Clamp using the actor's pending amount
    454
    ✓ 135.6K
            uint256 actualRedeemAmount = redeemAmount % (pendingAmount + 1);
    455
    
                                                    
                                                
    456
    ✓ 135.6K
            address[] memory controllers = new address[](1);
    457
    ✓ 135.6K
            controllers[0] = selectedController;
    458
    
                                                    
                                                
    459
            // Determine yield source type from currently active yield source
    460
    ✓ 135.6K
            YieldSourceType activeYieldSourceType = _getYieldSourceTypeFromAddress(
    461
    ✓ 135.6K
                _getYieldSource()
    462
            );
    463
    ✓ 135.6K
            address redeemHook = _getRedeemHookForType(activeYieldSourceType);
    464
    
                                                    
                                                
    465
            // Create realistic hook calldata for redeem operation
    466
    ✓ 135.6K
            bytes memory redeemHookCalldata;
    467
    
                                                    
                                                
    468
            if (
    469
    ✓ 135.6K
                activeYieldSourceType == YieldSourceType.ERC4626 ||
    470
    ✓ 65.5K
                activeYieldSourceType == YieldSourceType.ERC5115
    471
            ) {
    472
                // ERC4626/ERC5115 Layout: bytes32 oracleId, address yieldSource, address owner, uint256 shares, bool usePrevAmount
    473
                redeemHookCalldata = abi.encodePacked(
    474
    ✓ 113.7K
                    bytes32(0), // yieldSourceOracleId placeholder
    475
    ✓ 113.7K
                    _getYieldSource(), // Current active yield source
    476
    ✓ 113.7K
                    address(superVaultStrategy), // Owner (strategy owns the yield source shares)
    477
    ✓ 113.7K
                    actualRedeemAmount, // Amount to redeem (matches controller's pending request)
    478
                    false // Don't use previous hook amount
    479
                );
    480
            } else {
    481
                // ERC7540 Layout: bytes32 oracleId, address yieldSource, uint256 shares, bool usePrevAmount
    482
                redeemHookCalldata = abi.encodePacked(
    483
    ✓ 21.9K
                    bytes32(0), // yieldSourceOracleId placeholder
    484
    ✓ 21.9K
                    _getYieldSource(), // Current active yield source
    485
    ✓ 21.9K
                    actualRedeemAmount, // Amount to redeem (matches controller's pending request)
    486
    ✓ 21.9K
                    false // Don't use previous hook amount
    487
                );
    488
            }
    489
    
                                                    
                                                
    490
            // Create arrays for FulfillArgs
    491
    ✓ 135.6K
            address[] memory hooks = new address[](1);
    492
    ✓ 135.6K
            hooks[0] = redeemHook;
    493
    
                                                    
                                                
    494
    ✓ 135.6K
            bytes[] memory hookCalldata = new bytes[](1);
    495
    ✓ 135.6K
            hookCalldata[0] = redeemHookCalldata;
    496
    
                                                    
                                                
    497
    ✓ 135.6K
            uint256[] memory expectedAssetsOrSharesOut = new uint256[](1);
    498
    ✓ 135.6K
            expectedAssetsOrSharesOut[0] = actualRedeemAmount; // Expect amount matching the actual redeem
    499
    
                                                    
                                                
    500
    ✓ 135.6K
            bytes32[][] memory globalProofs = new bytes32[][](1);
    501
    ✓ 135.6K
            globalProofs[0] = new bytes32[](0); // Empty proof for UnsafeSuperVaultAggregator
    502
    
                                                    
                                                
    503
    ✓ 135.6K
            bytes32[][] memory strategyProofs = new bytes32[][](1);
    504
    ✓ 135.6K
            strategyProofs[0] = new bytes32[](0); // Empty proof
    505
    
                                                    
                                                
    506
            // Create the FulfillArgs struct
    507
    ✓ 135.6K
            fulfillArgs = ISuperVaultStrategy.FulfillArgs({
    508
                controllers: controllers,
    509
                hooks: hooks,
    510
                hookCalldata: hookCalldata,
    511
                expectedAssetsOrSharesOut: expectedAssetsOrSharesOut,
    512
                globalProofs: globalProofs,
    513
                strategyProofs: strategyProofs
    514
            });
    515
    
                                                    
                                                
    516
            return fulfillArgs;
    517
        }
    518
    
                                                    
                                                
    519
        /// @dev Helper function to create FulfillArgs for multiple actors
    520
        function _createMultiActorFulfillArgs(
    521
            address[] memory controllers,
    522
            uint256[] memory amounts
    523
    ✓ 1.1K
        ) internal view returns (ISuperVaultStrategy.FulfillArgs memory) {
    524
    ✓ 1.1K
            uint256 numActors = controllers.length;
    525
    
                                                    
                                                
    526
    ✓ 1.1K
            address[] memory hooks = new address[](numActors);
    527
    ✓ 1.1K
            bytes[] memory hookCalldata = new bytes[](numActors);
    528
    ✓ 1.1K
            uint256[] memory expectedAssetsOrSharesOut = new uint256[](numActors);
    529
    ✓ 1.1K
            bytes32[][] memory globalProofs = new bytes32[][](numActors);
    530
    ✓ 1.1K
            bytes32[][] memory strategyProofs = new bytes32[][](numActors);
    531
    
                                                    
                                                
    532
    ✓ 4.5K
            for (uint256 i = 0; i < numActors; i++) {
    533
    ✓ 3.4K
                hooks[i] = _getRedeemHookForType(
    534
    ✓ 3.4K
                    _getYieldSourceTypeFromAddress(_getYieldSource())
    535
                );
    536
    
                                                    
                                                
    537
                if (
    538
    ✓ 3.4K
                    _getYieldSourceTypeFromAddress(_getYieldSource()) ==
    539
    ✓ 3.4K
                    YieldSourceType.ERC4626
    540
                ) {
    541
    ✓ 2.4K
                    hookCalldata[i] = abi.encodePacked(
    542
    ✓ 2.4K
                        bytes32(0),
    543
    ✓ 2.4K
                        _getYieldSource(),
    544
    ✓ 2.4K
                        address(superVaultStrategy),
    545
    ✓ 2.4K
                        amounts[i],
    546
    ✓ 2.4K
                        false
    547
                    );
    548
                } else {
    549
    ✓ 990
                    hookCalldata[i] = abi.encodePacked(
    550
    ✓ 990
                        bytes32(0),
    551
    ✓ 990
                        _getYieldSource(),
    552
    ✓ 990
                        amounts[i],
    553
    ✓ 990
                        false
    554
                    );
    555
                }
    556
    
                                                    
                                                
    557
    ✓ 3.4K
                expectedAssetsOrSharesOut[i] = amounts[i];
    558
    ✓ 3.4K
                globalProofs[i] = new bytes32[](0);
    559
    ✓ 3.4K
                strategyProofs[i] = new bytes32[](0);
    560
            }
    561
    
                                                    
                                                
    562
            return
    563
    ✓ 1.1K
                ISuperVaultStrategy.FulfillArgs({
    564
                    controllers: controllers,
    565
                    hooks: hooks,
    566
                    hookCalldata: hookCalldata,
    567
                    expectedAssetsOrSharesOut: expectedAssetsOrSharesOut,
    568
                    globalProofs: globalProofs,
    569
                    strategyProofs: strategyProofs
    570
                });
    571
        }
    572
    
                                                    
                                                
    573
        /// @dev Helper function to create FulfillArgs for redeem requests
    574
        function _createFulfillRedeemArgs(
    575
            uint256 amount
    576
    ✓ 158.1K
        ) internal view returns (ISuperVaultStrategy.FulfillArgs memory) {
    577
    ✓ 158.1K
            address[] memory controllers = new address[](1);
    578
    ✓ 158.1K
            controllers[0] = _getActor();
    579
    
                                                    
                                                
    580
    ✓ 158.1K
            address[] memory hooks = new address[](1);
    581
    ✓ 173.1K
            hooks[0] = _getRedeemHookForType(
    582
    ✓ 158.1K
                _getYieldSourceTypeFromAddress(_getYieldSource())
    583
            );
    584
    
                                                    
                                                
    585
    ✓ 158.1K
            bytes[] memory hookCalldata = new bytes[](1);
    586
            if (
    587
    ✓ 158.1K
                _getYieldSourceTypeFromAddress(_getYieldSource()) ==
    588
    ✓ 158.1K
                YieldSourceType.ERC4626
    589
            ) {
    590
    ✓ 139.8K
                hookCalldata[0] = abi.encodePacked(
    591
    ✓ 139.8K
                    bytes32(0),
    592
    ✓ 139.8K
                    _getYieldSource(),
    593
    ✓ 139.8K
                    address(superVaultStrategy),
    594
    ✓ 139.8K
                    amount,
    595
                    false
    596
                );
    597
            } else {
    598
    ✓ 18.3K
                hookCalldata[0] = abi.encodePacked(
    599
    ✓ 18.3K
                    bytes32(0),
    600
    ✓ 18.3K
                    _getYieldSource(),
    601
    ✓ 18.3K
                    amount,
    602
    ✓ 18.3K
                    false
    603
                );
    604
            }
    605
    
                                                    
                                                
    606
    ✓ 158.1K
            uint256[] memory expectedAssetsOrSharesOut = new uint256[](1);
    607
    ✓ 158.1K
            expectedAssetsOrSharesOut[0] = amount;
    608
    
                                                    
                                                
    609
    ✓ 158.1K
            bytes32[][] memory globalProofs = new bytes32[][](1);
    610
    ✓ 158.1K
            globalProofs[0] = new bytes32[](0);
    611
    
                                                    
                                                
    612
    ✓ 158.1K
            bytes32[][] memory strategyProofs = new bytes32[][](1);
    613
    ✓ 158.1K
            strategyProofs[0] = new bytes32[](0);
    614
    
                                                    
                                                
    615
            return
    616
    ✓ 158.1K
                ISuperVaultStrategy.FulfillArgs({
    617
                    controllers: controllers,
    618
                    hooks: hooks,
    619
                    hookCalldata: hookCalldata,
    620
                    expectedAssetsOrSharesOut: expectedAssetsOrSharesOut,
    621
                    globalProofs: globalProofs,
    622
                    strategyProofs: strategyProofs
    623
                });
    624
        }
    625
    
                                                    
                                                
    626
        /// @dev Helper function to create ExecuteArgs for depositing assets into yield strategy
    627
        function _createExecuteDepositArgs(
    628
            uint256 amount
    629
    ✓ 50.1K
        ) internal view returns (ISuperVaultStrategy.ExecuteArgs memory) {
    630
    ✓ 50.1K
            address[] memory hooks = new address[](1);
    631
    ✓ 50.1K
            hooks[0] = _getApproveAndDepositHookForType(
    632
    ✓ 50.1K
                _getYieldSourceTypeFromAddress(_getYieldSource())
    633
            );
    634
    
                                                    
                                                
    635
    ✓ 50.1K
            bytes[] memory hookCalldata = new bytes[](1);
    636
    ✓ 50.1K
            YieldSourceType sourceType = _getYieldSourceTypeFromAddress(
    637
    ✓ 50.1K
                _getYieldSource()
    638
            );
    639
    
                                                    
                                                
    640
    ✓ 50.1K
            if (sourceType == YieldSourceType.ERC4626) {
    641
                // ERC4626 deposit: bytes32 oracleId, address yieldSource, address token, uint256 assets, bool usePrevAmount
    642
    ✓ 45.9K
                hookCalldata[0] = abi.encodePacked(
    643
    ✓ 43.5K
                    bytes32(0), // oracle ID placeholder
    644
    ✓ 43.5K
                    _getYieldSource(),
    645
    ✓ 43.5K
                    superVault.asset(), // token address
    646
    ✓ 43.5K
                    amount,
    647
    ✓ 43.5K
                    false // don't use previous amount
    648
                );
    649
    ✓ 6.6K
            } else if (sourceType == YieldSourceType.ERC5115) {
    650
                // ERC5115 deposit: bytes32 oracleId, address yieldSource, address token, uint256 assets, address receiver, uint256 id, bool usePrevAmount
    651
                hookCalldata[0] = abi.encodePacked(
    652
    ✓ 2.4K
                    bytes32(0),
    653
    ✓ 2.4K
                    _getYieldSource(),
    654
    ✓ 2.4K
                    superVault.asset(), // token address
    655
                    amount,
    656
    ✓ 2.4K
                    address(superVaultStrategy),
    657
                    uint256(0), // sy ID for ERC5115
    658
                    false
    659
                );
    660
    ✓ 4.2K
            } else if (sourceType == YieldSourceType.ERC7540) {
    661
                // ERC7540 requestDeposit: bytes32 oracleId, address yieldSource, address token, uint256 assets, address controller, bool usePrevAmount
    662
    ✓ 4.2K
                hookCalldata[0] = abi.encodePacked(
    663
    ✓ 4.2K
                    bytes32(0),
    664
    ✓ 4.2K
                    _getYieldSource(),
    665
    ✓ 4.2K
                    superVault.asset(), // token address
    666
                    amount,
    667
    ✓ 4.2K
                    address(superVaultStrategy),
    668
                    false
    669
                );
    670
            }
    671
    
                                                    
                                                
    672
    ✓ 50.1K
            uint256[] memory expectedAssetsOrSharesOut = new uint256[](1);
    673
            // For deposit hooks, we expect shares back. Calculate expected shares based on yield source conversion
    674
            // Use a very small amount (1) as minimum since we just want the deposit to succeed
    675
    ✓ 50.1K
            expectedAssetsOrSharesOut[0] = 1; // Minimum expected shares from deposit
    676
    
                                                    
                                                
    677
    ✓ 50.1K
            bytes32[][] memory globalProofs = new bytes32[][](1);
    678
    ✓ 50.1K
            globalProofs[0] = new bytes32[](0);
    679
    
                                                    
                                                
    680
    ✓ 50.1K
            bytes32[][] memory strategyProofs = new bytes32[][](1);
    681
    ✓ 50.1K
            strategyProofs[0] = new bytes32[](0);
    682
    
                                                    
                                                
    683
            return
    684
                ISuperVaultStrategy.ExecuteArgs({
    685
    ✓ 50.1K
                    hooks: hooks,
    686
    ✓ 50.1K
                    hookCalldata: hookCalldata,
    687
    ✓ 50.1K
                    expectedAssetsOrSharesOut: expectedAssetsOrSharesOut,
    688
    ✓ 50.1K
                    globalProofs: globalProofs,
    689
    ✓ 50.1K
                    strategyProofs: strategyProofs
    690
                });
    691
        }
    692
    
                                                    
                                                
    693
        /// @dev Helper function to create FulfillArgs for redeeming from yield strategy
    694
        function _createFulfillRedeemFromStrategyArgs(
    695
            uint256 sharesToRedeem
    696
    ✓ 11.6K
        ) internal view returns (ISuperVaultStrategy.FulfillArgs memory) {
    697
    ✓ 11.6K
            address[] memory controllers = new address[](1);
    698
    ✓ 11.6K
            controllers[0] = _getActor();
    699
    
                                                    
                                                
    700
            // Get the actual share balance that the SuperVaultStrategy holds in the yield source
    701
    ✓ 11.6K
            address yieldSource = _getYieldSource();
    702
    ✓ 11.6K
            uint256 strategyYieldSourceShares = IERC20(yieldSource).balanceOf(
    703
    ✓ 11.6K
                address(superVaultStrategy)
    704
            );
    705
    
                                                    
                                                
    706
            // Use the pending shares for the hook calldata to match what's expected by fulfillRedeemRequests
    707
    ✓ 11.6K
            uint256 pendingShares = superVault.pendingRedeemRequest(0, _getActor());
    708
    
                                                    
                                                
    709
            // For debugging: ensure we don't try to redeem more than the strategy has
    710
    ✓ 11.6K
            uint256 actualSharesToRedeem = strategyYieldSourceShares < pendingShares
    711
    ✓ 81
                ? strategyYieldSourceShares
    712
    ✓ 11.5K
                : pendingShares;
    713
    
                                                    
                                                
    714
    ✓ 11.6K
            address[] memory hooks = new address[](1);
    715
    ✓ 11.6K
            hooks[0] = _getRedeemHookForType(
    716
    ✓ 11.6K
                _getYieldSourceTypeFromAddress(yieldSource)
    717
            );
    718
    
                                                    
                                                
    719
    ✓ 11.6K
            bytes[] memory hookCalldata = new bytes[](1);
    720
    ✓ 11.6K
            YieldSourceType sourceType = _getYieldSourceTypeFromAddress(
    721
    ✓ 11.6K
                yieldSource
    722
            );
    723
    
                                                    
                                                
    724
            if (
    725
    ✓ 11.6K
                sourceType == YieldSourceType.ERC4626 ||
    726
                sourceType == YieldSourceType.ERC5115
    727
            ) {
    728
                // For ERC4626/5115: bytes32 oracleId, address yieldSource, address owner, uint256 shares, bool usePrevAmount
    729
                // Use the calculated shares to redeem (limited by what strategy actually has)
    730
    ✓ 11.6K
                hookCalldata[0] = abi.encodePacked(
    731
    ✓ 11.6K
                    bytes32(0),
    732
    ✓ 11.6K
                    yieldSource,
    733
    ✓ 11.6K
                    address(superVaultStrategy), // owner is the strategy
    734
    ✓ 11.6K
                    actualSharesToRedeem, // redeem calculated amount
    735
                    false
    736
                );
    737
            } else {
    738
                // For ERC7540: bytes32 oracleId, address yieldSource, uint256 shares, bool usePrevAmount
    739
                hookCalldata[0] = abi.encodePacked(
    740
                    bytes32(0),
    741
                    yieldSource,
    742
                    actualSharesToRedeem, // redeem calculated amount
    743
                    false
    744
                );
    745
            }
    746
    
                                                    
                                                
    747
            // For fulfillment, we expect to get assets back from redeeming the shares
    748
            // Use a conservative estimate for expected assets out
    749
    ✓ 11.6K
            uint256[] memory expectedAssetsOrSharesOut = new uint256[](1);
    750
    ✓ 11.6K
            expectedAssetsOrSharesOut[0] = actualSharesToRedeem > 0 ? 1 : 0; // Minimum 1 asset expected if redeeming anything
    751
    
                                                    
                                                
    752
    ✓ 11.6K
            bytes32[][] memory globalProofs = new bytes32[][](1);
    753
    ✓ 11.6K
            globalProofs[0] = new bytes32[](0);
    754
    
                                                    
                                                
    755
    ✓ 11.6K
            bytes32[][] memory strategyProofs = new bytes32[][](1);
    756
    ✓ 11.6K
            strategyProofs[0] = new bytes32[](0);
    757
    
                                                    
                                                
    758
            return
    759
    ✓ 11.6K
                ISuperVaultStrategy.FulfillArgs({
    760
                    controllers: controllers,
    761
                    hooks: hooks,
    762
                    hookCalldata: hookCalldata,
    763
                    expectedAssetsOrSharesOut: expectedAssetsOrSharesOut,
    764
                    globalProofs: globalProofs,
    765
                    strategyProofs: strategyProofs
    766
                });
    767
        }
    768
    }
    769
    
                                                    
                                                
    100.0% test/recon/targets/ManagersTargets.sol
    Lines covered: 8 / 8 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    5
    import {BeforeAfter} from "../BeforeAfter.sol";
    6
    import {Properties} from "../Properties.sol";
    7
    import {vm} from "@chimera/Hevm.sol";
    8
    
                                                    
                                                
    9
    import {MockERC20} from "@recon/MockERC20.sol";
    10
    
                                                    
                                                
    11
    // Target functions that are effectively inherited from the Actor and AssetManagers
    12
    // Once properly standardized, managers will expose these by default
    13
    // Keeping them out makes your project more custom
    14
    abstract contract ManagersTargets is BaseTargetFunctions, Properties {
    15
        // == ACTOR HANDLERS == //
    16
    
                                                    
                                                
    17
        /// @dev Start acting as another actor
    18
        /// @dev Update ghosts here to make global property checks not fail falsely
    19
        function switchActor(uint256 entropy) public updateGhosts {
    20
    ✓ 117.0K
            _switchActor(entropy);
    21
        }
    22
    
                                                    
                                                
    23
        /// @dev Starts using a new asset
    24
        function switch_asset(uint256 entropy) public {
    25
    ✓ 144.4K
            _switchAsset(entropy);
    26
        }
    27
    
                                                    
                                                
    28
        /// @dev Deploy a new token and add it to the list of assets, then set it as the current asset
    29
    ✓ 115.4K
        function add_new_asset(uint8 decimals) public returns (address) {
    30
    ✓ 115.4K
            address newAsset = _newAsset(decimals);
    31
            return newAsset;
    32
        }
    33
    
                                                    
                                                
    34
        /// @dev Switches the current vault based on the entropy
    35
        /// @param entropy The entropy to choose a random vault in the array for switching
    36
        function switch_vault(uint256 entropy) public {
    37
    ✓ 117.6K
            _switchVault(entropy);
    38
        }
    39
    
                                                    
                                                
    40
        /// @dev Deploy a new vault using the current asset and add it to the list of vaults,
    41
        /// then set it as the current vault
    42
        function add_new_vault() public {
    43
    ✓ 115.2K
            _newVault(superVault.asset());
    44
        }
    45
    
                                                    
                                                
    46
        /// === GHOST UPDATING HANDLERS ===///
    47
        /// We `updateGhosts` cause you never know (e.g. donations)
    48
        /// If you don't want to track donations, remove the `updateGhosts`
    49
    
                                                    
                                                
    50
        /// @dev Approve to arbitrary address, uses Actor by default
    51
        /// NOTE: You're almost always better off setting approvals in `Setup`
    52
        function asset_approve(
    53
            address to,
    54
            uint128 amt
    55
        ) public updateGhosts asActor {
    56
    ✓ 199.0K
            MockERC20(superVault.asset()).approve(to, amt);
    57
        }
    58
    
                                                    
                                                
    59
        /// @dev Mint to arbitrary address, uses owner by default, even though MockERC20 doesn't check
    60
        function asset_mint(address to, uint128 amt) public updateGhosts asAdmin {
    61
    ✓ 173.7K
            MockERC20(superVault.asset()).mint(to, amt);
    62
        }
    63
    }
    64
    
                                                    
                                                
    100.0% test/recon/targets/OracleTargets.sol
    Lines covered: 30 / 30 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    // External dependencies
    5
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    6
    import {vm} from "@chimera/Hevm.sol";
    7
    import {Panic} from "@recon/Panic.sol";
    8
    
                                                    
                                                
    9
    // Source dependencies
    10
    import {IECDSAPPSOracle} from "src/interfaces/oracles/IECDSAPPSOracle.sol";
    11
    import "test/mocks/MockYieldSourceOracle.sol";
    12
    import "../mocks/MockERC4626YieldSourceOracle.sol";
    13
    import "../mocks/MockERC5115YieldSourceOracle.sol";
    14
    import "../mocks/MockERC7540YieldSourceOracle.sol";
    15
    
                                                    
                                                
    16
    // Test suite dependencies
    17
    import {BeforeAfter} from "../BeforeAfter.sol";
    18
    import {Properties} from "../Properties.sol";
    19
    
                                                    
                                                
    20
    abstract contract OracleTargets is BaseTargetFunctions, Properties {
    21
        /// CUSTOM TARGET FUNCTIONS - Add your own target functions here ///
    22
        function yieldSourceOracle_setValidAsset_clamped() public {
    23
    ✓ 108.4K
            mockERC4626YieldSourceOracle_setValidAsset(superVault.asset(), true);
    24
        }
    25
    
                                                    
                                                
    26
        function mockERC4626YieldSourceOracle_setValidAsset(
    27
            address asset,
    28
            bool isValid
    29
        ) public asActor {
    30
    ✓ 223.0K
            MockERC4626YieldSourceOracle(address(erc4626YieldSourceOracle))
    31
                .setValidAsset(asset, isValid);
    32
        }
    33
    
                                                    
                                                
    34
        function mockERC5115YieldSourceOracle_setValidAsset(
    35
            address asset,
    36
            bool isValid
    37
        ) public asActor {
    38
    ✓ 172.3K
            MockERC5115YieldSourceOracle(address(erc5115YieldSourceOracle))
    39
                .setValidAsset(asset, isValid);
    40
        }
    41
    
                                                    
                                                
    42
        function mockERC7540YieldSourceOracle_setValidAsset(
    43
            address asset,
    44
            bool isValid
    45
        ) public asActor {
    46
    ✓ 103.0K
            MockERC7540YieldSourceOracle(address(erc7540YieldSourceOracle))
    47
                .setValidAsset(asset, isValid);
    48
        }
    49
    
                                                    
                                                
    50
        function ECDSAPPSOracle_updatePPS_clamped(uint256 pps) public {
    51
    ✓ 385.5K
            pps %= (100_000_000 * 1e18) + 1; // clamp to a reasonable max price
    52
    
                                                    
                                                
    53
    ✓ 385.5K
            address[] memory strategies = new address[](1);
    54
    ✓ 385.5K
            strategies[0] = address(superVaultStrategy);
    55
    
                                                    
                                                
    56
    ✓ 385.5K
            bytes[][] memory proofsArray = new bytes[][](1);
    57
    ✓ 385.5K
            proofsArray[0] = new bytes[](0);
    58
    
                                                    
                                                
    59
    ✓ 385.5K
            uint256[] memory ppss = new uint256[](1);
    60
    ✓ 385.5K
            ppss[0] = pps;
    61
    
                                                    
                                                
    62
    ✓ 385.5K
            uint256[] memory ppsStdevs = new uint256[](1);
    63
    ✓ 385.5K
            ppsStdevs[0] = 0;
    64
    
                                                    
                                                
    65
    ✓ 385.5K
            uint256[] memory validatorSets = new uint256[](1);
    66
    ✓ 385.5K
            validatorSets[0] = 0;
    67
    
                                                    
                                                
    68
    ✓ 385.5K
            uint256[] memory totalValidators = new uint256[](1);
    69
    ✓ 385.5K
            totalValidators[0] = 0;
    70
    
                                                    
                                                
    71
    ✓ 385.5K
            uint256[] memory timestamps = new uint256[](1);
    72
    ✓ 385.5K
            timestamps[0] = block.timestamp;
    73
    
                                                    
                                                
    74
    ✓ 385.5K
            IECDSAPPSOracle.UpdatePPSArgs memory args = IECDSAPPSOracle
    75
                .UpdatePPSArgs({
    76
    ✓ 385.5K
                    strategies: strategies,
    77
    ✓ 385.5K
                    proofsArray: proofsArray,
    78
    ✓ 385.5K
                    ppss: ppss,
    79
    ✓ 385.5K
                    ppsStdevs: ppsStdevs,
    80
    ✓ 385.5K
                    validatorSets: validatorSets,
    81
    ✓ 385.5K
                    totalValidators: totalValidators,
    82
    ✓ 385.5K
                    timestamps: timestamps
    83
                });
    84
    
                                                    
                                                
    85
    ✓ 385.5K
            ECDSAPPSOracle_updatePPS(args);
    86
        }
    87
    
                                                    
                                                
    88
        /// AUTO GENERATED TARGET FUNCTIONS - WARNING: DO NOT DELETE OR MODIFY THIS LINE ///
    89
        function ECDSAPPSOracle_updatePPS(
    90
            IECDSAPPSOracle.UpdatePPSArgs memory args
    91
        ) public asActor {
    92
    ✓ 529.1K
            ECDSAPPSOracle.updatePPS(args);
    93
    
                                                    
                                                
    94
    ✓ 377.9K
            hasUpdatedPPS = true;
    95
        }
    96
    }
    97
    
                                                    
                                                
    100.0% test/recon/targets/SuperGovernorTargets.sol
    Lines covered: 22 / 22 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    5
    import {BeforeAfter} from "../BeforeAfter.sol";
    6
    import {Properties} from "../Properties.sol";
    7
    // Chimera deps
    8
    import {vm} from "@chimera/Hevm.sol";
    9
    
                                                    
                                                
    10
    // Helpers
    11
    import {Panic} from "@recon/Panic.sol";
    12
    
                                                    
                                                
    13
    import "src/SuperGovernor.sol";
    14
    
                                                    
                                                
    15
    abstract contract SuperGovernorTargets is BaseTargetFunctions, Properties {
    16
        /// CUSTOM TARGET FUNCTIONS - Add your own target functions here ///
    17
        function superGovernor_proposeGlobalHooksRoot_clamped(
    18
            bytes32 newRoot
    19
        ) public {
    20
    ✓ 100.3K
            (bytes32 testRoot, bytes32[][] memory testProofs) = merkleHelper
    21
                .generateTestHooksRoot(
    22
    ✓ 100.3K
                    address(approveAndDeposit4626Hook),
    23
    ✓ 100.3K
                    address(redeem4626Hook),
    24
    ✓ 100.3K
                    _getYieldSource(),
    25
    ✓ 100.3K
                    superVault.asset()
    26
                );
    27
    
                                                    
                                                
    28
    ✓ 100.3K
            superGovernor_proposeGlobalHooksRoot(testRoot);
    29
        }
    30
    
                                                    
                                                
    31
        function superGovernor_proposeUpkeepPaymentsChange_clamped() public {
    32
    ✓ 117.4K
            superGovernor_proposeUpkeepPaymentsChange(true);
    33
        }
    34
    
                                                    
                                                
    35
        function superGovernor_proposeFee_clamped(
    36
            uint256 feeTypeAsUint,
    37
            uint256 value
    38
        ) public {
    39
    ✓ 125.7K
            feeTypeAsUint %= 3;
    40
    ✓ 125.7K
            superGovernor_proposeFee(FeeType(feeTypeAsUint), value);
    41
        }
    42
    
                                                    
                                                
    43
        function superGovernor_executeFeeUpdate_clamped(
    44
            uint256 feeTypeAsUint
    45
        ) public {
    46
    ✓ 108.7K
            feeTypeAsUint %= 3;
    47
    ✓ 108.7K
            superGovernor_executeFeeUpdate(FeeType(feeTypeAsUint));
    48
        }
    49
    
                                                    
                                                
    50
        /// AUTO GENERATED TARGET FUNCTIONS - WARNING: DO NOT DELETE OR MODIFY THIS LINE ///
    51
    
                                                    
                                                
    52
        function superGovernor_proposeFee(
    53
            FeeType feeType,
    54
            uint256 value
    55
        ) public asAdmin {
    56
    ✓ 142.0K
            superGovernor.proposeFee(feeType, value);
    57
        }
    58
    
                                                    
                                                
    59
        function superGovernor_executeFeeUpdate(FeeType feeType) public asAdmin {
    60
    ✓ 118.0K
            superGovernor.executeFeeUpdate(feeType);
    61
        }
    62
    
                                                    
                                                
    63
        function superGovernor_proposeMinStaleness(
    64
            uint256 newMinStaleness
    65
        ) public asAdmin {
    66
    ✓ 105.4K
            superGovernor.proposeMinStaleness(newMinStaleness);
    67
        }
    68
    
                                                    
                                                
    69
        function superGovernor_executeMinStalenesChange() public asAdmin {
    70
    ✓ 170.2K
            superGovernor.executeMinStalenesChange();
    71
        }
    72
    
                                                    
                                                
    73
        function superGovernor_executeRemoveIncentiveTokens() public asAdmin {
    74
    ✓ 98.8K
            superGovernor.executeRemoveIncentiveTokens();
    75
        }
    76
    
                                                    
                                                
    77
        function superGovernor_executeUpkeepClaim(uint256 amount) public asAdmin {
    78
    ✓ 148.0K
            superGovernor.executeUpkeepClaim(amount);
    79
        }
    80
    
                                                    
                                                
    81
        function superGovernor_proposeUpkeepPaymentsChange(
    82
            bool enabled
    83
        ) public asAdmin {
    84
    ✓ 225.7K
            superGovernor.proposeUpkeepPaymentsChange(enabled);
    85
        }
    86
    
                                                    
                                                
    87
        function superGovernor_executeUpkeepPaymentsChange() public asAdmin {
    88
    ✓ 165.7K
            superGovernor.executeUpkeepPaymentsChange();
    89
        }
    90
    
                                                    
                                                
    91
        function superGovernor_proposeAddIncentiveTokens(
    92
            address[] memory tokens
    93
        ) public asAdmin {
    94
    ✓ 223.3K
            superGovernor.proposeAddIncentiveTokens(tokens);
    95
        }
    96
    
                                                    
                                                
    97
        function superGovernor_executeAddIncentiveTokens() public asAdmin {
    98
    ✓ 101.2K
            superGovernor.executeAddIncentiveTokens();
    99
        }
    100
    
                                                    
                                                
    101
        function superGovernor_proposeGlobalHooksRoot(
    102
            bytes32 newRoot
    103
        ) public asAdmin {
    104
    ✓ 218.2K
            superGovernor.proposeGlobalHooksRoot(newRoot);
    105
        }
    106
    }
    107
    
                                                    
                                                
    100.0% test/recon/targets/SuperVaultAggregatorTargets.sol
    Lines covered: 37 / 37 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    5
    import {vm} from "@chimera/Hevm.sol";
    6
    import {Panic} from "@recon/Panic.sol";
    7
    
                                                    
                                                
    8
    import {ISuperVaultStrategy} from "src/interfaces/SuperVault/ISuperVaultStrategy.sol";
    9
    import "src/SuperVault/SuperVaultAggregator.sol";
    10
    
                                                    
                                                
    11
    import {BeforeAfter} from "../BeforeAfter.sol";
    12
    import {Properties} from "../Properties.sol";
    13
    
                                                    
                                                
    14
    abstract contract SuperVaultAggregatorTargets is
    15
        BaseTargetFunctions,
    16
        Properties
    17
    {
    18
        /// CUSTOM TARGET FUNCTIONS - Add your own target functions here ///
    19
        function superVaultAggregator_proposeChangePrimaryManager_clamped() public {
    20
            superVaultAggregator_proposeChangePrimaryManager(
    21
    ✓ 195.6K
                address(superVaultStrategy),
    22
    ✓ 195.6K
                _getActor()
    23
            );
    24
        }
    25
    
                                                    
                                                
    26
        function superVaultAggregator_executeChangePrimaryManager_clamped() public {
    27
    ✓ 138.6K
            superVaultAggregator_executeChangePrimaryManager(
    28
    ✓ 138.6K
                address(superVaultStrategy)
    29
            );
    30
        }
    31
    
                                                    
                                                
    32
        function superVaultAggregator_createVault_clamped(
    33
            uint256 minUpdateInterval,
    34
            uint256 maxStaleness,
    35
            uint256 performanceFeeBps,
    36
            uint256 managementFeeBps
    37
        ) public {
    38
            // Clamp values to reasonable ranges
    39
    ✓ 95.1K
            minUpdateInterval = minUpdateInterval % 3601; // Max 1 hour
    40
    ✓ 95.1K
            maxStaleness = (maxStaleness % 86400) + 301; // Between 5 minutes and 1 day
    41
    ✓ 95.1K
            performanceFeeBps = performanceFeeBps % 9001; // Max 90%
    42
    ✓ 95.1K
            managementFeeBps = managementFeeBps % 5001; // Max 50%
    43
    
                                                    
                                                
    44
            // Create secondary managers array
    45
    ✓ 95.1K
            address[] memory secondaryManagers = new address[](1);
    46
    
                                                    
                                                
    47
    ✓ 95.1K
            ISuperVaultAggregator.VaultCreationParams
    48
                memory params = ISuperVaultAggregator.VaultCreationParams({
    49
    ✓ 95.1K
                    asset: _getAsset(),
    50
                    name: "SuperVault",
    51
                    symbol: "SV",
    52
    ✓ 95.1K
                    mainManager: address(this),
    53
    ✓ 95.1K
                    secondaryManagers: secondaryManagers,
    54
    ✓ 95.1K
                    minUpdateInterval: minUpdateInterval,
    55
    ✓ 95.1K
                    maxStaleness: maxStaleness,
    56
                    feeConfig: ISuperVaultStrategy.FeeConfig({
    57
    ✓ 95.1K
                        performanceFeeBps: performanceFeeBps,
    58
    ✓ 95.1K
                        managementFeeBps: managementFeeBps,
    59
    ✓ 95.1K
                        recipient: address(this)
    60
                    })
    61
                });
    62
    
                                                    
                                                
    63
    ✓ 95.1K
            superVaultAggregator_createVault(params);
    64
        }
    65
    
                                                    
                                                
    66
        /// AUTO GENERATED TARGET FUNCTIONS - WARNING: DO NOT DELETE OR MODIFY THIS LINE ///
    67
    
                                                    
                                                
    68
        function superVaultAggregator_addAuthorizedCaller(
    69
            address strategy,
    70
            address caller
    71
        ) public asAdmin {
    72
    ✓ 175.1K
            superVaultAggregator.addAuthorizedCaller(strategy, caller);
    73
        }
    74
    
                                                    
                                                
    75
        function superVaultAggregator_addSecondaryManager(
    76
            address strategy,
    77
            address manager
    78
        ) public asActor {
    79
    ✓ 110.2K
            superVaultAggregator.addSecondaryManager(strategy, manager);
    80
        }
    81
    
                                                    
                                                
    82
        /// @dev removed because only callable by oracle
    83
        // function superVaultAggregator_batchForwardPPS(
    84
        //     ISuperVaultAggregator.BatchForwardPPSArgs memory args
    85
        // ) public asActor {
    86
        //     superVaultAggregator.batchForwardPPS(args);
    87
        // }
    88
    
                                                    
                                                
    89
        /// @dev irrelevant for testing because we're bypassing hook validation
    90
        // function superVaultAggregator_changeGlobalLeavesStatus(
    91
        //     bytes32[] memory leaves,
    92
        //     bool[] memory statuses,
    93
        //     address strategy
    94
        // ) public asActor {
    95
        //     superVaultAggregator.changeGlobalLeavesStatus(
    96
        //         leaves,
    97
        //         statuses,
    98
        //         strategy
    99
        //     );
    100
        // }
    101
    
                                                    
                                                
    102
        function superVaultAggregator_claimUpkeep(uint256 amount) public asActor {
    103
    ✓ 183.9K
            superVaultAggregator.claimUpkeep(amount);
    104
        }
    105
    
                                                    
                                                
    106
        function superVaultAggregator_createVault(
    107
            ISuperVaultAggregator.VaultCreationParams memory params
    108
        ) public asActor {
    109
            (
    110
    ✓ 230.7K
                address _superVault,
    111
                address _strategy,
    112
                address _escrow
    113
    ✓ 230.7K
            ) = superVaultAggregator.createVault(params);
    114
    
                                                    
                                                
    115
    ✓ 78.1K
            superVault = SuperVault(_superVault);
    116
    ✓ 78.1K
            superVaultStrategy = SuperVaultStrategy(payable(_strategy));
    117
    ✓ 78.1K
            superVaultEscrow = SuperVaultEscrow(_escrow);
    118
    
                                                    
                                                
    119
    ✓ 78.1K
            hasDeployedNewVault = true;
    120
        }
    121
    
                                                    
                                                
    122
        function superVaultAggregator_depositStake(
    123
            address manager,
    124
            uint256 amount
    125
        ) public asActor {
    126
    ✓ 120.9K
            superVaultAggregator.depositStake(manager, amount);
    127
        }
    128
    
                                                    
                                                
    129
        function superVaultAggregator_depositUpkeep(uint256 amount) public asActor {
    130
    ✓ 199.4K
            superVaultAggregator.depositUpkeep(_getActor(), amount);
    131
        }
    132
    
                                                    
                                                
    133
        function superVaultAggregator_executeChangePrimaryManager(
    134
            address strategy
    135
        ) public asActor {
    136
    ✓ 296.7K
            superVaultAggregator.executeChangePrimaryManager(strategy);
    137
        }
    138
    
                                                    
                                                
    139
        /// @dev removed because we're bypassing hook validation
    140
        // function superVaultAggregator_executeStrategyHooksRootUpdate(
    141
        //     address strategy
    142
        // ) public asActor {
    143
        //     superVaultAggregator.executeStrategyHooksRootUpdate(strategy);
    144
        // }
    145
    
                                                    
                                                
    146
        /// @dev removed because only callable by oracle
    147
        // function superVaultAggregator_forwardPPS(
    148
        //     address updateAuthority,
    149
        //     ISuperVaultAggregator.ForwardPPSArgs memory args
    150
        // ) public asActor {
    151
        //     superVaultAggregator.forwardPPS(updateAuthority, args);
    152
        // }
    153
    
                                                    
                                                
    154
        function superVaultAggregator_proposeChangePrimaryManager(
    155
            address strategy,
    156
            address newManager
    157
        ) public asActor {
    158
    ✓ 296.7K
            superVaultAggregator.proposeChangePrimaryManager(strategy, newManager);
    159
        }
    160
    
                                                    
                                                
    161
        /// @dev removed because we're bypassing hook validation
    162
        // function superVaultAggregator_proposeStrategyHooksRoot(
    163
        //     address strategy,
    164
        //     bytes32 newRoot
    165
        // ) public asActor {
    166
        //     superVaultAggregator.proposeStrategyHooksRoot(strategy, newRoot);
    167
        // }
    168
    
                                                    
                                                
    169
        function superVaultAggregator_removeAuthorizedCaller(
    170
            address strategy,
    171
            address caller
    172
        ) public asActor {
    173
    ✓ 131.9K
            superVaultAggregator.removeAuthorizedCaller(strategy, caller);
    174
        }
    175
    
                                                    
                                                
    176
        function superVaultAggregator_removeSecondaryManager(
    177
            address strategy,
    178
            address manager
    179
        ) public asActor {
    180
    ✓ 160.4K
            superVaultAggregator.removeSecondaryManager(strategy, manager);
    181
        }
    182
    
                                                    
                                                
    183
        function superVaultAggregator_updatePPSVerificationThresholds(
    184
            address strategy,
    185
            uint256 dispersionThreshold_,
    186
            uint256 deviationThreshold_,
    187
            uint256 mnThreshold_
    188
        ) public asActor {
    189
    ✓ 121.3K
            superVaultAggregator.updatePPSVerificationThresholds(
    190
                strategy,
    191
                dispersionThreshold_,
    192
                deviationThreshold_,
    193
                mnThreshold_
    194
            );
    195
        }
    196
    
                                                    
                                                
    197
        function superVaultAggregator_withdrawStake(uint256 amount) public asActor {
    198
    ✓ 150.7K
            superVaultAggregator.withdrawStake(amount);
    199
        }
    200
    
                                                    
                                                
    201
        function superVaultAggregator_withdrawUpkeep(
    202
            uint256 amount
    203
        ) public asActor {
    204
    ✓ 100.4K
            superVaultAggregator.withdrawUpkeep(amount);
    205
        }
    206
    }
    207
    
                                                    
                                                
    100.0% test/recon/targets/SuperVaultEscrowTargets.sol
    Lines covered: 3 / 3 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    5
    import {BeforeAfter} from "../BeforeAfter.sol";
    6
    import {Properties} from "../Properties.sol";
    7
    // Chimera deps
    8
    import {vm} from "@chimera/Hevm.sol";
    9
    
                                                    
                                                
    10
    // Helpers
    11
    import {Panic} from "@recon/Panic.sol";
    12
    
                                                    
                                                
    13
    import "src/SuperVault/SuperVaultEscrow.sol";
    14
    
                                                    
                                                
    15
    abstract contract SuperVaultEscrowTargets is
    16
        BaseTargetFunctions,
    17
        Properties
    18
    {
    19
        /// CUSTOM TARGET FUNCTIONS - Add your own target functions here ///
    20
    
                                                    
                                                
    21
    
                                                    
                                                
    22
        /// AUTO GENERATED TARGET FUNCTIONS - WARNING: DO NOT DELETE OR MODIFY THIS LINE ///
    23
    
                                                    
                                                
    24
        function superVaultEscrow_escrowShares(address from, uint256 amount) public asActor {
    25
    ✓ 115.2K
            superVaultEscrow.escrowShares(from, amount);
    26
        }
    27
    
                                                    
                                                
    28
        function superVaultEscrow_initialize(address vaultAddress, address strategyAddress) public asActor {
    29
    ✓ 107.8K
            superVaultEscrow.initialize(vaultAddress, strategyAddress);
    30
        }
    31
    
                                                    
                                                
    32
        function superVaultEscrow_returnShares(address to, uint256 amount) public asActor {
    33
    ✓ 116.9K
            superVaultEscrow.returnShares(to, amount);
    34
        }
    35
    }
    100.0% test/recon/targets/SuperVaultStrategyTargets.sol
    Lines covered: 24 / 24 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    5
    
                                                    
                                                
    6
    import {vm} from "@chimera/Hevm.sol";
    7
    import {Panic} from "@recon/Panic.sol";
    8
    import {MockERC20} from "@recon/MockERC20.sol";
    9
    
                                                    
                                                
    10
    import "src/SuperVault/SuperVaultStrategy.sol";
    11
    
                                                    
                                                
    12
    import {YieldSourceType} from "test/recon/managers/YieldManager.sol";
    13
    import {BeforeAfter, OpType} from "../BeforeAfter.sol";
    14
    import {Properties} from "../Properties.sol";
    15
    
                                                    
                                                
    16
    abstract contract SuperVaultStrategyTargets is BaseTargetFunctions, Properties {
    17
        /// CUSTOM TARGET FUNCTIONS - Add your own target functions here ///
    18
    
                                                    
                                                
    19
        /// @dev Clamps the action type to 0 to add a vault as a yield source
    20
        function superVaultStrategy_manageYieldSource_clamped(
    21
            uint256 sourceType
    22
        ) public {
    23
    ✓ 448.7K
            YieldSourceType clampedType = YieldSourceType(sourceType % 3); // 0=ERC4626, 1=ERC5115, 2=ERC7540
    24
    ✓ 448.7K
            address yieldSourceOracle = _getYieldSourceOracleForType(clampedType);
    25
    
                                                    
                                                
    26
    ✓ 448.7K
            superVaultStrategy_manageYieldSource(
    27
    ✓ 448.7K
                _getYieldSource(),
    28
    ✓ 448.7K
                yieldSourceOracle,
    29
    ✓ 448.7K
                0
    30
            );
    31
        }
    32
    
                                                    
                                                
    33
        function superVaultStrategy_handleOperations7540_clamped(
    34
            uint256 operation,
    35
            uint256 amount
    36
        ) public {
    37
    ✓ 97.3K
            operation %= 7; // clamp by the possible operation types in the enum
    38
    ✓ 82.1K
            superVaultStrategy_handleOperations7540(
    39
    ✓ 97.3K
                ISuperVaultStrategy.Operation(operation),
    40
                _getActor(),
    41
                _getActor(),
    42
    ✓ 82.1K
                amount
    43
            );
    44
        }
    45
    
                                                    
                                                
    46
        /// AUTO GENERATED TARGET FUNCTIONS - WARNING: DO NOT DELETE OR MODIFY THIS LINE ///
    47
    
                                                    
                                                
    48
        function superVaultStrategy_executeVaultFeeConfigUpdate() public asActor {
    49
    ✓ 184.6K
            superVaultStrategy.executeVaultFeeConfigUpdate();
    50
        }
    51
    
                                                    
                                                
    52
        function superVaultStrategy_handleOperations4626Deposit(
    53
            address controller,
    54
            uint256 assetsGross
    55
        ) public asActor {
    56
    ✓ 140.7K
            superVaultStrategy.handleOperations4626Deposit(controller, assetsGross);
    57
        }
    58
    
                                                    
                                                
    59
        function superVaultStrategy_handleOperations4626Mint(
    60
            address controller,
    61
            uint256 sharesNet,
    62
            uint256 assetsGross,
    63
            uint256 assetsNet
    64
        ) public asActor {
    65
    ✓ 117.4K
            superVaultStrategy.handleOperations4626Mint(
    66
                controller,
    67
                sharesNet,
    68
                assetsGross,
    69
                assetsNet
    70
            );
    71
        }
    72
    
                                                    
                                                
    73
        function superVaultStrategy_handleOperations7540(
    74
            ISuperVaultStrategy.Operation operation,
    75
            address controller,
    76
            address receiver,
    77
            uint256 amount
    78
        ) public asActor {
    79
    ✓ 138.0K
            superVaultStrategy.handleOperations7540(
    80
    ✓ 138.0K
                operation,
    81
    ✓ 138.0K
                controller,
    82
    ✓ 138.0K
                receiver,
    83
    ✓ 138.0K
                amount
    84
            );
    85
        }
    86
    
                                                    
                                                
    87
        function superVaultStrategy_manageEmergencyWithdraw(
    88
            uint8 action,
    89
            address recipient,
    90
            uint256 amount
    91
        ) public asActor {
    92
    ✓ 203.9K
            superVaultStrategy.manageEmergencyWithdraw(action, recipient, amount);
    93
        }
    94
    
                                                    
                                                
    95
        function superVaultStrategy_manageYieldSource(
    96
            address source,
    97
            address oracle,
    98
            uint8 actionType
    99
        ) public asActor {
    100
    ✓ 593.8K
            superVaultStrategy.manageYieldSource(source, oracle, actionType);
    101
        }
    102
    
                                                    
                                                
    103
        function superVaultStrategy_manageYieldSources(
    104
            address[] memory sources,
    105
            address[] memory oracles,
    106
            uint8[] memory actionTypes
    107
        ) public asActor {
    108
    ✓ 106.3K
            superVaultStrategy.manageYieldSources(sources, oracles, actionTypes);
    109
        }
    110
    
                                                    
                                                
    111
        function superVaultStrategy_moveAccumulatorOnTransfer(
    112
            address from,
    113
            address to,
    114
            uint256 shares
    115
        ) public asActor {
    116
    ✓ 95.3K
            superVaultStrategy.moveAccumulatorOnTransfer(from, to, shares);
    117
        }
    118
    
                                                    
                                                
    119
        function superVaultStrategy_proposeVaultFeeConfigUpdate(
    120
            uint256 performanceFeeBps,
    121
            uint256 managementFeeBps,
    122
            address recipient
    123
        ) public asActor {
    124
    ✓ 148.3K
            superVaultStrategy.proposeVaultFeeConfigUpdate(
    125
                performanceFeeBps,
    126
                managementFeeBps,
    127
                recipient
    128
            );
    129
        }
    130
    
                                                    
                                                
    131
        function superVaultStrategy_updateMaxPPSSlippage(
    132
            uint256 maxSlippageBps
    133
        ) public asActor {
    134
    ✓ 166.8K
            superVaultStrategy.updateMaxPPSSlippage(maxSlippageBps);
    135
        }
    136
    }
    137
    
                                                    
                                                
    100.0% test/recon/targets/SuperVaultTargets.sol
    Lines covered: 103 / 103 (100.0%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    // Recon deps
    5
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    6
    import {vm} from "@chimera/Hevm.sol";
    7
    import {Panic} from "@recon/Panic.sol";
    8
    import {MockERC20} from "@recon/MockERC20.sol";
    9
    
                                                    
                                                
    10
    import "src/SuperVault/SuperVault.sol";
    11
    
                                                    
                                                
    12
    import {BeforeAfter, OpType} from "test/recon/BeforeAfter.sol";
    13
    import {Properties} from "../Properties.sol";
    14
    
                                                    
                                                
    15
    /// @dev All receivers are inherently clamped to actors to make checking properties easier
    16
    abstract contract SuperVaultTargets is BaseTargetFunctions, Properties {
    17
        /// CUSTOM TARGET FUNCTIONS - Add your own target functions here ///
    18
        function superVault_requestRedeem_clamped(uint256 shares) public {
    19
    ✓ 567.7K
            shares %= superVault.balanceOf(_getActor()) + 1;
    20
    
                                                    
                                                
    21
    ✓ 567.7K
            superVault_requestRedeem(shares);
    22
        }
    23
    
                                                    
                                                
    24
        function superVault_redeem_clamped(uint256 shares) public {
    25
    ✓ 139.7K
            uint256 claimableAssets = superVault.maxWithdraw(_getActor());
    26
    ✓ 139.7K
            uint256 claimableShares = superVault.convertToShares(claimableAssets);
    27
    
                                                    
                                                
    28
    ✓ 139.7K
            shares %= claimableShares + 1;
    29
    
                                                    
                                                
    30
    ✓ 139.7K
            superVault_redeem(shares);
    31
        }
    32
    
                                                    
                                                
    33
        function superVault_withdraw_clamped(uint256 assets) public {
    34
    ✓ 213.1K
            uint256 claimableAssets = superVault.maxWithdraw(_getActor());
    35
    ✓ 213.1K
            assets %= claimableAssets + 1;
    36
    
                                                    
                                                
    37
    ✓ 213.1K
            superVault_withdraw(assets);
    38
        }
    39
    
                                                    
                                                
    40
        /// AUTO GENERATED TARGET FUNCTIONS - WARNING: DO NOT DELETE OR MODIFY THIS LINE ///
    41
    
                                                    
                                                
    42
        function superVault_approve(address spender, uint256 value) public asActor {
    43
    ✓ 140.7K
            superVault.approve(spender, value);
    44
        }
    45
    
                                                    
                                                
    46
        function superVault_burnShares(uint256 amount) public asActor {
    47
    ✓ 117.9K
            superVault.burnShares(amount);
    48
        }
    49
    
                                                    
                                                
    50
        /// @dev Property: pendingRedeemRequest should be 0 after a user calls cancelRedeem
    51
        /// @dev Property: averageRequestPPS should be 0 after a user calls cancelRedeem
    52
        /// @dev Property: user shouldn't receive more than convertToAssets(pendingRedeemRequest) after cancelRedeem
    53
        function superVault_cancelRedeem_ASSERTION_CANCEL_REDEEM_NO_OVERPAY()
    54
            public
    55
    ✓ 101.0K
            updateGhostsWithOpType(OpType.CANCEL)
    56
        {
    57
    ✓ 101.0K
            uint256 pendingRedeemRequestsBefore = superVault.pendingRedeemRequest(
    58
                0,
    59
    ✓ 101.0K
                _getActor()
    60
            );
    61
    ✓ 101.0K
            uint256 pendingRedeemRequestsAsAssets = superVault.convertToAssets(
    62
                pendingRedeemRequestsBefore
    63
            );
    64
    ✓ 101.0K
            uint256 balanceBefore = MockERC20(superVault.asset()).balanceOf(
    65
    ✓ 101.0K
                _getActor()
    66
            );
    67
    
                                                    
                                                
    68
    ✓ 101.0K
            vm.prank(_getActor());
    69
    ✓ 101.0K
            superVault.cancelRedeem(_getActor());
    70
    
                                                    
                                                
    71
    ✓ 1.3K
            uint256 pendingRedeemRequestsAfter = superVault.pendingRedeemRequest(
    72
                0,
    73
    ✓ 1.3K
                _getActor()
    74
            );
    75
    ✓ 1.3K
            uint256 averageRequestPPS = superVaultStrategy
    76
    ✓ 1.3K
                .getSuperVaultState(_getActor())
    77
                .averageRequestPPS;
    78
    ✓ 1.3K
            uint256 balanceAfter = MockERC20(superVault.asset()).balanceOf(
    79
    ✓ 1.3K
                _getActor()
    80
            );
    81
    
                                                    
                                                
    82
            // Checks
    83
    ✓ 1.3K
            eq(
    84
    ✓ 1.3K
                pendingRedeemRequestsAfter,
    85
    ✓ 1.3K
                0,
    86
    ✓ 1.3K
                ASSERTION_CANCEL_REDEEM_NO_OVERPAY
    87
            );
    88
    ✓ 1.3K
            eq(
    89
    ✓ 1.3K
                averageRequestPPS,
    90
    ✓ 1.3K
                0,
    91
    ✓ 1.3K
                ASSERTION_CANCEL_REDEEM_NO_OVERPAY
    92
            );
    93
    ✓ 1.3K
            lte(
    94
    ✓ 1.3K
                balanceAfter - balanceBefore,
    95
    ✓ 1.3K
                pendingRedeemRequestsAsAssets,
    96
    ✓ 1.3K
                ASSERTION_CANCEL_REDEEM_NO_OVERPAY
    97
            );
    98
        }
    99
    
                                                    
                                                
    100
        /// @dev Property: previewDeposit returns the correct amounts compared to executing a deposit
    101
        function superVault_deposit_ASSERTION_PREVIEW_DEPOSIT_MATCHES_EXECUTION(
    102
            uint256 assets
    103
    ✓ 364.7K
        ) public updateGhostsWithOpType(OpType.ADD) {
    104
    ✓ 364.7K
            uint256 previewShares = superVault.previewDeposit(assets);
    105
    
                                                    
                                                
    106
    ✓ 360.6K
            vm.prank(_getActor());
    107
    ✓ 360.6K
            uint256 shares = superVault.deposit(assets, _getActor());
    108
    
                                                    
                                                
    109
    ✓ 253.7K
            eq(
    110
    ✓ 253.7K
                previewShares,
    111
    ✓ 253.7K
                shares,
    112
    ✓ 253.7K
                ASSERTION_PREVIEW_DEPOSIT_MATCHES_EXECUTION
    113
            );
    114
        }
    115
    
                                                    
                                                
    116
        /// @dev Property: previewMint returns the correct amounts compared to executing a mint
    117
        function superVault_mint_ASSERTION_PREVIEW_MINT_MATCHES_EXECUTION(
    118
            uint256 shares
    119
    ✓ 93.2K
        ) public updateGhostsWithOpType(OpType.ADD) {
    120
    ✓ 93.2K
            uint256 previewMint = superVault.previewMint(shares);
    121
    
                                                    
                                                
    122
    ✓ 64.3K
            vm.prank(_getActor());
    123
    ✓ 64.3K
            uint256 assets = superVault.mint(shares, _getActor());
    124
    
                                                    
                                                
    125
    ✓ 14.0K
            eq(
    126
    ✓ 14.0K
                assets,
    127
    ✓ 14.0K
                previewMint,
    128
    ✓ 14.0K
                ASSERTION_PREVIEW_MINT_MATCHES_EXECUTION
    129
            );
    130
        }
    131
    
                                                    
                                                
    132
        function superVault_invalidateNonce(bytes32 nonce) public asActor {
    133
    ✓ 134.6K
            superVault.invalidateNonce(nonce);
    134
        }
    135
    
                                                    
                                                
    136
        function superVault_redeem(
    137
            uint256 shares
    138
    ✓ 304.0K
        ) public updateGhostsWithOpType(OpType.REMOVE) asActor {
    139
    ✓ 304.0K
            superVault.redeem(shares, _getActor(), _getActor());
    140
        }
    141
    
                                                    
                                                
    142
        function superVault_withdraw(
    143
            uint256 assets
    144
    ✓ 325.3K
        ) public updateGhostsWithOpType(OpType.REMOVE) asActor {
    145
    ✓ 325.3K
            superVault.withdraw(assets, _getActor(), _getActor());
    146
        }
    147
    
                                                    
                                                
    148
        function superVault_requestRedeem(
    149
            uint256 shares
    150
    ✓ 741.6K
        ) public updateGhostsWithOpType(OpType.REQUEST) asActor {
    151
    ✓ 1.4M
            superVault.requestRedeem(shares, _getActor(), _getActor());
    152
        }
    153
    
                                                    
                                                
    154
        function superVault_setOperator(
    155
            uint256 entropy,
    156
            bool approved
    157
        ) public asActor {
    158
    ✓ 123.7K
            address operator = _getRandomActor(entropy);
    159
    ✓ 123.7K
            superVault.setOperator(operator, approved);
    160
        }
    161
    
                                                    
                                                
    162
        /// @dev Propery: _update should never revert
    163
        /// @dev Property: Transfers of shares should transfer the exact amount of accumulatorShares to the recipient
    164
        /// @dev Property: Transfers of shares should transfer the exact amount of accumulatorCostBasis to the recipient
    165
        // NOTE: _update only gets called on transfer of Vault shares
    166
        function superVault_transfer_ASSERTION_TRANSFER_SHARES_CONSERVED(
    167
            uint256 entropy,
    168
            uint256 value
    169
    ✓ 116.7K
        ) public updateGhostsWithOpType(OpType.TRANSFER) {
    170
    ✓ 116.7K
            address to = _getRandomActor(entropy);
    171
    ✓ 116.7K
            ISuperVaultStrategy.SuperVaultState
    172
    ✓ 116.7K
                memory stateSenderBefore = superVaultStrategy.getSuperVaultState(
    173
    ✓ 116.7K
                    _getActor()
    174
                );
    175
    ✓ 116.7K
            ISuperVaultStrategy.SuperVaultState
    176
    ✓ 116.7K
                memory stateRecipientBefore = superVaultStrategy.getSuperVaultState(
    177
                    to
    178
                );
    179
    
                                                    
                                                
    180
    ✓ 116.7K
            vm.prank(_getActor());
    181
    ✓ 116.7K
            try superVault.transfer(to, value) {
    182
    ✓ 5.6K
                ISuperVaultStrategy.SuperVaultState
    183
    ✓ 5.6K
                    memory stateSenderAfter = superVaultStrategy.getSuperVaultState(
    184
    ✓ 5.6K
                        _getActor()
    185
                    );
    186
    ✓ 5.6K
                ISuperVaultStrategy.SuperVaultState
    187
    ✓ 5.6K
                    memory stateRecipientAfter = superVaultStrategy
    188
                        .getSuperVaultState(to);
    189
    
                                                    
                                                
    190
    ✓ 11.2K
                eq(
    191
    ✓ 5.6K
                    stateSenderBefore.accumulatorShares -
    192
    ✓ 5.6K
                        stateSenderAfter.accumulatorShares,
    193
    ✓ 5.6K
                    stateRecipientAfter.accumulatorShares -
    194
    ✓ 5.6K
                        stateRecipientBefore.accumulatorShares,
    195
    ✓ 11.2K
                    ASSERTION_TRANSFER_SHARES_CONSERVED
    196
                );
    197
                eq(
    198
    ✓ 5.6K
                    stateSenderBefore.accumulatorCostBasis -
    199
    ✓ 5.6K
                        stateSenderAfter.accumulatorCostBasis,
    200
    ✓ 5.6K
                    stateRecipientAfter.accumulatorCostBasis -
    201
    ✓ 5.6K
                        stateRecipientBefore.accumulatorCostBasis,
    202
                    ASSERTION_TRANSFER_SHARES_CONSERVED
    203
                );
    204
            } catch (bytes memory err) {
    205
    ✓ 111.1K
                bool expectedError;
    206
    ✓ 111.1K
                expectedError = checkError(
    207
    ✓ 111.1K
                    err,
    208
                    "ERC20InsufficientBalance(address,uint256,uint256)"
    209
                );
    210
    ✓ 111.1K
                t(expectedError, ASSERTION_TRANSFER_SHARES_CONSERVED);
    211
            }
    212
        }
    213
    
                                                    
                                                
    214
        /// @dev Propery: _update should never revert
    215
        // NOTE: _update only gets called on transfer of Vault shares
    216
        function superVault_transferFrom_ASSERTION_UPDATE_SHOULD_NOT_REVERT_TRANSFER_FROM(
    217
            uint256 entropyFrom,
    218
            uint256 entropyTo,
    219
            uint256 value
    220
    ✓ 152.4K
        ) public updateGhostsWithOpType(OpType.TRANSFER) {
    221
    ✓ 152.4K
            address from = _getRandomActor(entropyFrom);
    222
    ✓ 152.4K
            address to = _getRandomActor(entropyTo);
    223
    
                                                    
                                                
    224
    ✓ 152.4K
            vm.prank(_getActor());
    225
    ✓ 152.4K
            try superVault.transferFrom(from, to, value) {} catch (
    226
                bytes memory err
    227
            ) {
    228
    ✓ 150.1K
                bool expectedError;
    229
                expectedError =
    230
    ✓ 150.1K
                    checkError(
    231
    ✓ 150.1K
                        err,
    232
                        "ERC20InsufficientBalance(address,uint256,uint256)"
    233
                    ) ||
    234
    ✓ 150.1K
                    checkError(
    235
    ✓ 150.1K
                        err,
    236
                        "ERC20InsufficientAllowance(address,uint256,uint256)"
    237
                    );
    238
    ✓ 150.1K
                t(
    239
    ✓ 150.1K
                    expectedError,
    240
    ✓ 150.1K
                    ASSERTION_UPDATE_SHOULD_NOT_REVERT_TRANSFER_FROM
    241
                );
    242
            }
    243
        }
    244
    
                                                    
                                                
    245
        /// @dev removed because signature components not fuzzable
    246
        // function superVault_authorizeOperator(
    247
        //     address controller,
    248
        //     address operator,
    249
        //     bool approved,
    250
        //     bytes32 nonce,
    251
        //     uint256 deadline,
    252
        //     bytes memory signature
    253
        // ) public asActor {
    254
        //     superVault.authorizeOperator(
    255
        //         controller,
    256
        //         operator,
    257
        //         approved,
    258
        //         nonce,
    259
        //         deadline,
    260
        //         signature
    261
        //     );
    262
        // }
    263
    }
    264
    
                                                    
                                                
    93.2% test/recon/targets/YieldSourceTargets.sol
    Lines covered: 138 / 148 (93.2%)
    1
    // SPDX-License-Identifier: GPL-2.0
    2
    pragma solidity ^0.8.0;
    3
    
                                                    
                                                
    4
    // Chimera deps
    5
    import {BaseTargetFunctions} from "@chimera/BaseTargetFunctions.sol";
    6
    import {vm} from "@chimera/Hevm.sol";
    7
    
                                                    
                                                
    8
    // Helpers
    9
    import {Panic} from "@recon/Panic.sol";
    10
    
                                                    
                                                
    11
    import {Properties} from "../Properties.sol";
    12
    import {MockERC4626Tester, FunctionType, RevertType} from "../mocks/MockERC4626Tester.sol";
    13
    import {MockERC5115Tester, RevertType as RevertType5115} from "../mocks/MockERC5115Tester.sol";
    14
    import {MockERC7540Tester} from "../mocks/MockERC7540Tester.sol";
    15
    import {YieldSourceType} from "../managers/YieldManager.sol";
    16
    
                                                    
                                                
    17
    /// @dev Target functions for yield source testers which are used as yield sources in SuperVaultStrategy
    18
    abstract contract YieldSourceTargets is BaseTargetFunctions, Properties {
    19
        /// AUTO GENERATED TARGET FUNCTIONS - WARNING: DO NOT DELETE OR MODIFY THIS LINE ///
    20
    
                                                    
                                                
    21
        /// ERC20 functions (available across all types as they inherit from MockERC20) ///
    22
        function yieldSource_approve(
    23
            address spender,
    24
            uint256 value
    25
        ) public asActor {
    26
    ✓ 163.6K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    27
    ✓ 163.6K
            address yieldSource = _getYieldSource();
    28
    
                                                    
                                                
    29
    ✓ 163.6K
            if (currentType == YieldSourceType.ERC4626) {
    30
    ✓ 30.1K
                MockERC4626Tester(yieldSource).approve(spender, value);
    31
    ✓ 133.4K
            } else if (currentType == YieldSourceType.ERC5115) {
    32
                MockERC5115Tester(yieldSource).approve(spender, value);
    33
    ✓ 133.4K
            } else if (currentType == YieldSourceType.ERC7540) {
    34
    ✓ 133.4K
                MockERC7540Tester(yieldSource).approve(spender, value);
    35
            }
    36
        }
    37
    
                                                    
                                                
    38
        function yieldSource_setDecimalsOffset(
    39
            uint8 targetDecimalsOffset
    40
        ) public asActor {
    41
    ✓ 185.2K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    42
    ✓ 185.2K
            address yieldSource = _getYieldSource();
    43
    
                                                    
                                                
    44
    ✓ 185.2K
            if (currentType == YieldSourceType.ERC4626) {
    45
    ✓ 27.8K
                MockERC4626Tester(yieldSource).setDecimalsOffset(
    46
                    targetDecimalsOffset
    47
                );
    48
            }
    49
            // Note: ERC5115 and ERC7540 don't have setDecimalsOffset function
    50
        }
    51
    
                                                    
                                                
    52
        function yieldSource_transfer(address to, uint256 value) public asActor {
    53
    ✓ 161.1K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    54
    ✓ 161.1K
            address yieldSource = _getYieldSource();
    55
    
                                                    
                                                
    56
    ✓ 161.1K
            if (currentType == YieldSourceType.ERC4626) {
    57
    ✓ 28.0K
                MockERC4626Tester(yieldSource).transfer(to, value);
    58
    ✓ 133.1K
            } else if (currentType == YieldSourceType.ERC5115) {
    59
                MockERC5115Tester(yieldSource).transfer(to, value);
    60
    ✓ 133.1K
            } else if (currentType == YieldSourceType.ERC7540) {
    61
    ✓ 133.1K
                MockERC7540Tester(yieldSource).transfer(to, value);
    62
            }
    63
        }
    64
    
                                                    
                                                
    65
        function yieldSource_transferFrom(
    66
            address from,
    67
            address to,
    68
            uint256 value
    69
        ) public asActor {
    70
    ✓ 177.7K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    71
    ✓ 177.7K
            address yieldSource = _getYieldSource();
    72
    
                                                    
                                                
    73
    ✓ 177.7K
            if (currentType == YieldSourceType.ERC4626) {
    74
    ✓ 25.6K
                MockERC4626Tester(yieldSource).transferFrom(from, to, value);
    75
    ✓ 152.2K
            } else if (currentType == YieldSourceType.ERC5115) {
    76
                MockERC5115Tester(yieldSource).transferFrom(from, to, value);
    77
    ✓ 152.2K
            } else if (currentType == YieldSourceType.ERC7540) {
    78
    ✓ 152.2K
                MockERC7540Tester(yieldSource).transferFrom(from, to, value);
    79
            }
    80
        }
    81
    
                                                    
                                                
    82
        /// Core Vault Functions (ERC4626/ERC4626-like) ///
    83
        function yieldSource_deposit(
    84
            uint256 assets,
    85
            address receiver
    86
        ) public asActor {
    87
    ✓ 104.5K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    88
    ✓ 104.5K
            address yieldSource = _getYieldSource();
    89
    
                                                    
                                                
    90
    ✓ 104.5K
            if (currentType == YieldSourceType.ERC4626) {
    91
    ✓ 35.8K
                MockERC4626Tester(yieldSource).deposit(assets, receiver);
    92
            }
    93
            // Note: ERC5115 has different deposit signature, handled separately
    94
            // Note: ERC7540 has different deposit signature, handled separately
    95
        }
    96
    
                                                    
                                                
    97
        function yieldSource_mint(uint256 shares, address receiver) public asActor {
    98
    ✓ 381.3K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    99
    ✓ 381.3K
            address yieldSource = _getYieldSource();
    100
    
                                                    
                                                
    101
    ✓ 381.3K
            if (currentType == YieldSourceType.ERC4626) {
    102
    ✓ 37.9K
                MockERC4626Tester(yieldSource).mint(shares, receiver);
    103
    ✓ 343.4K
            } else if (currentType == YieldSourceType.ERC7540) {
    104
    ✓ 343.4K
                MockERC7540Tester(yieldSource).mint(shares, receiver);
    105
            }
    106
            // Note: ERC5115 doesn't have mint function
    107
        }
    108
    
                                                    
                                                
    109
        function yieldSource_withdraw(
    110
            uint256 assets,
    111
            address receiver,
    112
            address owner
    113
        ) public asActor {
    114
    ✓ 110.9K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    115
    ✓ 110.9K
            address yieldSource = _getYieldSource();
    116
    
                                                    
                                                
    117
    ✓ 110.9K
            if (currentType == YieldSourceType.ERC4626) {
    118
    ✓ 24.3K
                MockERC4626Tester(yieldSource).withdraw(assets, receiver, owner);
    119
    ✓ 86.6K
            } else if (currentType == YieldSourceType.ERC7540) {
    120
    ✓ 86.6K
                MockERC7540Tester(yieldSource).withdraw(assets, receiver, owner);
    121
            }
    122
            // Note: ERC5115 doesn't have withdraw function
    123
        }
    124
    
                                                    
                                                
    125
        function yieldSource_redeem(
    126
            uint256 shares,
    127
            address receiver,
    128
            address owner
    129
        ) public asActor {
    130
    ✓ 128.6K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    131
    ✓ 128.6K
            address yieldSource = _getYieldSource();
    132
    
                                                    
                                                
    133
    ✓ 128.6K
            if (currentType == YieldSourceType.ERC4626) {
    134
    ✓ 27.4K
                MockERC4626Tester(yieldSource).redeem(shares, receiver, owner);
    135
    ✓ 101.2K
            } else if (currentType == YieldSourceType.ERC7540) {
    136
    ✓ 101.2K
                MockERC7540Tester(yieldSource).redeem(shares, receiver, owner);
    137
            }
    138
            // Note: ERC5115 has different redeem signature, handled separately
    139
        }
    140
    
                                                    
                                                
    141
        /// ERC5115-specific functions ///
    142
        function yieldSource_deposit5115(
    143
            address receiver,
    144
            address tokenIn,
    145
            uint256 amountTokenToDeposit,
    146
            uint256 minSharesOut,
    147
            bool depositFromInternalBalance
    148
        ) public asActor {
    149
    ✓ 111.8K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    150
    ✓ 111.8K
            address yieldSource = _getYieldSource();
    151
    
                                                    
                                                
    152
    ✓ 111.8K
            if (currentType == YieldSourceType.ERC5115) {
    153
                MockERC5115Tester(yieldSource).deposit(
    154
                    receiver,
    155
                    tokenIn,
    156
                    amountTokenToDeposit,
    157
                    minSharesOut,
    158
                    depositFromInternalBalance
    159
                );
    160
            }
    161
        }
    162
    
                                                    
                                                
    163
        function yieldSource_redeem5115(
    164
            address receiver,
    165
            uint256 amountSharesToRedeem,
    166
            address tokenOut,
    167
            uint256 minTokenOut,
    168
            bool burnFromInternalBalance
    169
        ) public asActor {
    170
    ✓ 165.1K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    171
    ✓ 165.1K
            address yieldSource = _getYieldSource();
    172
    
                                                    
                                                
    173
    ✓ 165.1K
            if (currentType == YieldSourceType.ERC5115) {
    174
                MockERC5115Tester(yieldSource).redeem(
    175
                    receiver,
    176
                    amountSharesToRedeem,
    177
                    tokenOut,
    178
                    minTokenOut,
    179
                    burnFromInternalBalance
    180
                );
    181
            }
    182
        }
    183
    
                                                    
                                                
    184
        /// ERC7540-specific functions ///
    185
        function yieldSource_setOperator(
    186
            address operator,
    187
            bool approved
    188
        ) public asActor {
    189
    ✓ 107.5K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    190
    ✓ 107.5K
            address yieldSource = _getYieldSource();
    191
    
                                                    
                                                
    192
    ✓ 107.5K
            if (currentType == YieldSourceType.ERC7540) {
    193
    ✓ 81.8K
                MockERC7540Tester(yieldSource).setOperator(operator, approved);
    194
            }
    195
        }
    196
    
                                                    
                                                
    197
        function yieldSource_requestDeposit(
    198
            uint256 assets,
    199
            address controller,
    200
            address owner
    201
        ) public asActor {
    202
    ✓ 120.9K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    203
    ✓ 120.9K
            address yieldSource = _getYieldSource();
    204
    
                                                    
                                                
    205
    ✓ 120.9K
            if (currentType == YieldSourceType.ERC7540) {
    206
    ✓ 90.1K
                MockERC7540Tester(yieldSource).requestDeposit(
    207
    ✓ 90.1K
                    assets,
    208
    ✓ 90.1K
                    controller,
    209
    ✓ 90.1K
                    owner
    210
                );
    211
            }
    212
        }
    213
    
                                                    
                                                
    214
        function yieldSource_requestRedeem(
    215
            uint256 shares,
    216
            address controller,
    217
            address owner
    218
        ) public asActor {
    219
    ✓ 138.1K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    220
    ✓ 138.1K
            address yieldSource = _getYieldSource();
    221
    
                                                    
                                                
    222
    ✓ 138.1K
            if (currentType == YieldSourceType.ERC7540) {
    223
    ✓ 107.3K
                MockERC7540Tester(yieldSource).requestRedeem(
    224
    ✓ 107.3K
                    shares,
    225
    ✓ 107.3K
                    controller,
    226
    ✓ 107.3K
                    owner
    227
                );
    228
            }
    229
        }
    230
    
                                                    
                                                
    231
        function yieldSource_cancelDepositRequest(
    232
            uint256 requestId,
    233
            address controller
    234
        ) public asActor {
    235
    ✓ 125.5K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    236
    ✓ 125.5K
            address yieldSource = _getYieldSource();
    237
    
                                                    
                                                
    238
    ✓ 125.5K
            if (currentType == YieldSourceType.ERC7540) {
    239
    ✓ 87.1K
                MockERC7540Tester(yieldSource).cancelDepositRequest(
    240
    ✓ 87.1K
                    requestId,
    241
    ✓ 87.1K
                    controller
    242
                );
    243
            }
    244
        }
    245
    
                                                    
                                                
    246
        function yieldSource_cancelRedeemRequest(
    247
            uint256 requestId,
    248
            address controller
    249
        ) public asActor {
    250
    ✓ 140.0K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    251
    ✓ 140.0K
            address yieldSource = _getYieldSource();
    252
    
                                                    
                                                
    253
    ✓ 140.0K
            if (currentType == YieldSourceType.ERC7540) {
    254
    ✓ 103.3K
                MockERC7540Tester(yieldSource).cancelRedeemRequest(
    255
    ✓ 103.3K
                    requestId,
    256
    ✓ 103.3K
                    controller
    257
                );
    258
            }
    259
        }
    260
    
                                                    
                                                
    261
        function yieldSource_claimCancelDepositRequest(
    262
            uint256 requestId,
    263
            address receiver,
    264
            address controller
    265
        ) public asActor {
    266
    ✓ 126.8K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    267
    ✓ 126.8K
            address yieldSource = _getYieldSource();
    268
    
                                                    
                                                
    269
    ✓ 126.8K
            if (currentType == YieldSourceType.ERC7540) {
    270
    ✓ 91.3K
                MockERC7540Tester(yieldSource).claimCancelDepositRequest(
    271
    ✓ 91.3K
                    requestId,
    272
    ✓ 91.3K
                    receiver,
    273
    ✓ 91.3K
                    controller
    274
                );
    275
            }
    276
        }
    277
    
                                                    
                                                
    278
        function yieldSource_claimCancelRedeemRequest(
    279
            uint256 requestId,
    280
            address receiver,
    281
            address controller
    282
        ) public asActor {
    283
    ✓ 107.3K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    284
    ✓ 107.3K
            address yieldSource = _getYieldSource();
    285
    
                                                    
                                                
    286
    ✓ 107.3K
            if (currentType == YieldSourceType.ERC7540) {
    287
    ✓ 80.1K
                MockERC7540Tester(yieldSource).claimCancelRedeemRequest(
    288
    ✓ 80.1K
                    requestId,
    289
    ✓ 80.1K
                    receiver,
    290
    ✓ 80.1K
                    controller
    291
                );
    292
            }
    293
        }
    294
    
                                                    
                                                
    295
        function yieldSource_deposit7540(
    296
            uint256 assets,
    297
            address receiver,
    298
            address controller
    299
        ) public asActor {
    300
    ✓ 163.9K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    301
    ✓ 163.9K
            address yieldSource = _getYieldSource();
    302
    
                                                    
                                                
    303
    ✓ 163.9K
            if (currentType == YieldSourceType.ERC7540) {
    304
    ✓ 126.3K
                MockERC7540Tester(yieldSource).deposit(
    305
    ✓ 126.3K
                    assets,
    306
    ✓ 126.3K
                    receiver,
    307
    ✓ 126.3K
                    controller
    308
                );
    309
            }
    310
        }
    311
    
                                                    
                                                
    312
        /// Testing-specific functions (ERC4626 only) ///
    313
        function yieldSource_setRevertBehavior4626(
    314
            uint8 functionType,
    315
            uint8 revertType
    316
        ) public asActor {
    317
    ✓ 101.2K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    318
    ✓ 101.2K
            address yieldSource = _getYieldSource();
    319
    
                                                    
                                                
    320
    ✓ 101.2K
            if (currentType == YieldSourceType.ERC4626) {
    321
    ✓ 25.9K
                MockERC4626Tester(yieldSource).setRevertBehavior(
    322
    ✓ 25.9K
                    FunctionType(functionType),
    323
    ✓ 5.5K
                    RevertType(revertType)
    324
                );
    325
            }
    326
        }
    327
    
                                                    
                                                
    328
        /// Testing-specific functions (ERC5115 only) ///
    329
        function yieldSource_setRevertBehavior5115(
    330
            uint8 revertType
    331
        ) public asActor {
    332
    ✓ 126.5K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    333
    ✓ 126.5K
            address yieldSource = _getYieldSource();
    334
    
                                                    
                                                
    335
    ✓ 126.5K
            if (currentType == YieldSourceType.ERC5115) {
    336
                MockERC5115Tester(yieldSource).setRevertBehavior(
    337
                    RevertType5115(revertType)
    338
                );
    339
            }
    340
        }
    341
    
                                                    
                                                
    342
        /// Common yield manipulation functions ///
    343
    
                                                    
                                                
    344
        function yieldSource_simulateLoss(uint256 lossAmount) public {
    345
    ✓ 131.5K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    346
    ✓ 131.5K
            address yieldSource = _getYieldSource();
    347
    
                                                    
                                                
    348
    ✓ 131.5K
            if (currentType == YieldSourceType.ERC4626) {
    349
    ✓ 31.0K
                MockERC4626Tester(yieldSource).simulateLoss(lossAmount);
    350
    ✓ 100.4K
            } else if (currentType == YieldSourceType.ERC5115) {
    351
                MockERC5115Tester(yieldSource).simulateLoss(lossAmount);
    352
    ✓ 100.4K
            } else if (currentType == YieldSourceType.ERC7540) {
    353
    ✓ 100.4K
                MockERC7540Tester(yieldSource).simulateLoss(lossAmount);
    354
            }
    355
        }
    356
    
                                                    
                                                
    357
        function yieldSource_simulateGain(uint256 gainAmount) public {
    358
    ✓ 132.2K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    359
    ✓ 132.2K
            address yieldSource = _getYieldSource();
    360
    
                                                    
                                                
    361
    ✓ 132.2K
            if (currentType == YieldSourceType.ERC4626) {
    362
    ✓ 40.1K
                MockERC4626Tester(yieldSource).simulateGain(gainAmount);
    363
    ✓ 92.2K
            } else if (currentType == YieldSourceType.ERC5115) {
    364
                MockERC5115Tester(yieldSource).simulateGain(gainAmount);
    365
    ✓ 92.2K
            } else if (currentType == YieldSourceType.ERC7540) {
    366
    ✓ 92.2K
                MockERC7540Tester(yieldSource).simulateGain(gainAmount);
    367
            }
    368
        }
    369
    
                                                    
                                                
    370
        function yieldSource_setLossOnWithdraw(
    371
            uint256 lossOnWithdraw
    372
        ) public asActor {
    373
    ✓ 141.7K
            YieldSourceType currentType = _getCurrentYieldSourceType();
    374
    ✓ 141.7K
            address yieldSource = _getYieldSource();
    375
    
                                                    
                                                
    376
    ✓ 141.7K
            if (currentType == YieldSourceType.ERC4626) {
    377
    ✓ 40.2K
                MockERC4626Tester(yieldSource).setLossOnWithdraw(lossOnWithdraw);
    378
    ✓ 101.5K
            } else if (currentType == YieldSourceType.ERC5115) {
    379
                MockERC5115Tester(yieldSource).setLossOnWithdraw(lossOnWithdraw);
    380
    ✓ 101.5K
            } else if (currentType == YieldSourceType.ERC7540) {
    381
    ✓ 101.5K
                MockERC7540Tester(yieldSource).setLossOnWithdraw(lossOnWithdraw);
    382
            }
    383
        }
    384
    
                                                    
                                                
    385
        /// Yield source management functions ///
    386
        function yieldSource_switchToERC4626() public {
    387
    ✓ 153.2K
            _switchYieldSource(0); // Switch to first yield source (ERC4626)
    388
        }
    389
    
                                                    
                                                
    390
        function yieldSource_switchToERC5115() public {
    391
    ✓ 131.7K
            _switchYieldSource(1); // Switch to second yield source (ERC5115)
    392
        }
    393
    
                                                    
                                                
    394
        function yieldSource_switchToERC7540() public {
    395
    ✓ 106.0K
            _switchYieldSource(2); // Switch to third yield source (ERC7540)
    396
        }
    397
    
                                                    
                                                
    398
        function yieldSource_switchRandom(uint256 entropy) public {
    399
            // Randomly switch between the three deployed yield sources
    400
    ✓ 257.7K
            _switchYieldSource(entropy);
    401
        }
    402
    }
    403