Forkchoice Ethereum Mainnet

Address Contract Partially Verified

Address 0xB68d82e2efC18D8d20fB56735008aDA693bb9005
Balance 0 ETH
Nonce 1
Code Size 7616 bytes
Indexed Transactions 0 (1 on-chain, 1.3% indexed)
External Etherscan · Sourcify

Contract Bytecode

7616 bytes
0x608060405234801561000f575f5ffd5b50600436106101bb575f3560e01c80638e1ac2d2116100f3578063d751d25f11610093578063e110dfea1161006e578063e110dfea14610379578063f2fde38b1461038c578063f3217f501461039f578063ff2a7d30146103ae575f5ffd5b8063d751d25f14610356578063d826f88f14610369578063db2e21bc14610371575f5ffd5b8063be5ed586116100ce578063be5ed5861461031f578063c1f5a1bc14610328578063c22223fd1461033b578063cac3e14714610343575f5ffd5b80638e1ac2d2146102f1578063b69ef8a814610304578063b6b55f251461030c575f5ffd5b80635169379c1161015e5780637da2d1f4116101395780637da2d1f4146102945780638088c3181461029c578063842a69ae146102af5780638da5cb5b146102e1575f5ffd5b80635169379c14610252578063715018a6146102795780637766059d14610281575f5ffd5b806338d52e0f1161019957806338d52e0f1461021957806339965dde1461022c57806339ddcb18146102345780633e09a95f1461023d575f5ffd5b8063193bc784146101bf5780632e1a7d4d146101ef578063329d0fa514610210575b5f5ffd5b6004546101d2906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6102026101fd366004611a05565b6103c1565b6040519081526020016101e6565b61020260075481565b6002546101d2906001600160a01b031681565b610202610427565b61020260085481565b61025061024b366004611a32565b610443565b005b6101d27f0000000000000000000000008eb67a509616cd6a7c1b3c8c21d48ff57df3d45881565b6102506104a0565b61020261028f366004611a93565b6104b3565b6102026104dc565b6001546101d2906001600160a01b031681565b6102d16102bd366004611a32565b600a6020525f908152604090205460ff1681565b60405190151581526020016101e6565b5f546001600160a01b03166101d2565b6102506102ff366004611ad2565b6104f3565b61020261065f565b61025061031a366004611a05565b610734565b61020260095481565b610250610336366004611b36565b6107a5565b6102026107fe565b610250610351366004611a05565b610817565b6003546101d2906001600160a01b031681565b61025061084d565b61025061088e565b6101d2610387366004611a05565b6108ae565b61025061039a366004611a32565b6108d6565b610202670de0b6b3a764000081565b6006546101d2906001600160a01b031681565b5f6103d36103cd61065f565b5f61094f565b600954600854111561040c57600854600954604051631d5ec82d60e01b8152600481019290925260248201526044015b60405180910390fd5b61041582610970565b905061041f61065f565b600755919050565b5f61043e61043361065f565b600754600854610a81565b905090565b61044b610b93565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f83c107821f0b12bef59991a4b4b5ee9a7e236013a645cdeaca760eb05bac9e7a906020015b60405180910390a150565b6104a8610b93565b6104b15f610bec565b565b5f6104bf6103cd61065f565b6104c98383610c3b565b90506104d361065f565b60075592915050565b5f6104ed6104e8610d06565b610e26565b50919050565b6104fb610b93565b6001600160a01b0381166105225760405163aec93d5360e01b815260040160405180910390fd5b815f5b8181101561065857600a5f86868481811061054257610542611c01565b90506020020160208101906105579190611a32565b6001600160a01b0316815260208101919091526040015f205460ff16156105915760405163f6cc2b0d60e01b815260040160405180910390fd5b6106508585838181106105a6576105a6611c01565b90506020020160208101906105bb9190611a32565b848787858181106105ce576105ce611c01565b90506020020160208101906105e39190611a32565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015610627573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061064b9190611c15565b610f52565b600101610525565b5050505050565b5f5f610669610d06565b90505f5f61067683610e26565b9092509050801561072d5760015460035460025460405163178f90e760e01b81525f936001600160a01b039081169363178f90e7936106bf939183169216908990600401611c2c565b602060405180830381865afa1580156106da573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106fe9190611c15565b90508082101561072b5760405163022659ef60e61b81526004810182905260248101839052604401610403565b505b5092915050565b5f61073d61065f565b9050610749815f61094f565b600954600854111561077d57600854600954604051631d5ec82d60e01b815260048101929092526024820152604401610403565b600781905561078b82610fde565b61079361065f565b905061079f818361094f565b60075550565b6107ad610b93565b7f3efd0a5ae591a028bb1ac99ff202c1bc7f03d0ad2a413258f7c463aaa660d1da6005826040516107df929190611c50565b60405180910390a180516107fa906005906020840190611997565b5050565b5f6108076110c8565b905061081161065f565b60075590565b61081f610b93565b670de0b6b3a764000081111561084857604051637c945f2b60e01b815260040160405180910390fd5b600955565b610855610b93565b5f60085561086161065f565b6007556040517f2f19ec1b64383cd5e4954998758cd5a7af4d21d5ea7b825c4c6705a4c7c70ca9905f90a1565b6108996103cd61065f565b6108a1611191565b6108a961065f565b600755565b600581815481106108bd575f80fd5b5f918252602090912001546001600160a01b0316905081565b6108de610b93565b6001600160a01b0381166109435760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610403565b61094c81610bec565b50565b61096982826007546109619190611cf9565b600854610a81565b6008555050565b6004545f906001600160a01b0316331461099d576040516317189ea960e01b815260040160405180910390fd5b8115610a7c575f1982036109b6576109b36104dc565b91505b6109bf826111cb565b6002546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015610a05573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a299190611c15565b90508015610a4857600254610a48906001600160a01b03163383610f52565b6040518181527f5b6b431d4476a211bb7d41c20d1aab9ae2321deee0d20be3d9fc9b1093fa6e3d9060200160405180910390a15b919050565b5f825f03610a9057505f610b8c565b82841115610b11575f83670de0b6b3a7640000610aad8288611d0c565b610ab79190611d1f565b610ac19190611d36565b90505f670de0b6b3a7640000610ad78386611d1f565b610ae19190611d36565b610aeb9085611cf9565b905081811115610b0657610aff8282611d0c565b9350610b0a565b5f93505b5050610b89565b835f03610b2857670de0b6b3a76400009150610b89565b5f83670de0b6b3a7640000610b3d8783611d0c565b610b479190611d1f565b610b519190611d36565b905080670de0b6b3a7640000610b678286611d1f565b610b719190611d36565b610b7b9085611d0c565b610b859190611cf9565b9250505b50805b9392505050565b5f546001600160a01b031633146104b15760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610403565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f610c44610b93565b610c4e8383611453565b6002546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015610c94573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb89190611c15565b600254600454919250610cd8916001600160a01b03918216911683610f52565b6040517f284104a992f983bf9107b9b595674e36387d4b9df406a1f566216f84a0277e9a905f90a192915050565b6040516370a0823160e01b81523060048201525f9081906001600160a01b037f0000000000000000000000008eb67a509616cd6a7c1b3c8c21d48ff57df3d45816906370a0823190602401602060405180830381865afa158015610d6c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d909190611c15565b90508015610e22576040516303d1689d60e11b8152600481018290527f0000000000000000000000008eb67a509616cd6a7c1b3c8c21d48ff57df3d4586001600160a01b0316906307a2d13a90602401602060405180830381865afa158015610dfb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e1f9190611c15565b91505b5090565b6002546040516370a0823160e01b81523060048201525f9182916001600160a01b03909116906370a0823190602401602060405180830381865afa158015610e70573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e949190611c15565b91508215610f4d576002546003546001600160a01b03918216911603610ec557610ebe8383611cf9565b9150915091565b600154600354600254604051632550332960e11b81526001600160a01b0393841693634aa0665293610f0293908216929116908890600401611c2c565b602060405180830381865afa158015610f1d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f419190611c15565b9050610ebe8183611cf9565b915091565b6040516001600160a01b0383166024820152604481018290525f90610fb790859063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526114a8565b90508015610fd8576040516314a1430160e21b815260040160405180910390fd5b50505050565b6004546001600160a01b03163314611009576040516317189ea960e01b815260040160405180910390fd5b801561094c57600254611027906001600160a01b031633308461154b565b6002546040516370a0823160e01b8152306004820152611098916001600160a01b0316906370a0823190602401602060405180830381865afa15801561106f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110939190611c15565b61158e565b6040518181527f4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e3842690602001610495565b5f6110d1611673565b6002546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611117573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061113b9190611c15565b60025460065491925061115b916001600160a01b03918216911683610f52565b6040518181527f8a8d0a0fcbd5e0a9953099de7132a0e4d85d01e8b53d9bf89ac03260215ac5549060200160405180910390a190565b611199610b93565b6111a16116c9565b6040517fcc6a1a065ab514031862e10458cbf117148ff1f8a168cfacab350e6644c174f0905f90a1565b60025460035482916001600160a01b03918216911614611264576001546003546002546040516353b609b560e01b81526001600160a01b03938416936353b609b59361122293908216929116908690600401611c2c565b602060405180830381865afa15801561123d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112619190611c15565b90505b60405163ce96cb7760e01b81523060048201525f907f0000000000000000000000008eb67a509616cd6a7c1b3c8c21d48ff57df3d4586001600160a01b03169063ce96cb7790602401602060405180830381865afa1580156112c8573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112ec9190611c15565b90508082111561139857801561139357604051632d182be560e21b815260048101829052306024820181905260448201527f0000000000000000000000008eb67a509616cd6a7c1b3c8c21d48ff57df3d4586001600160a01b03169063b460af94906064016020604051808303815f875af115801561136d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113919190611c15565b505b611435565b801561143557604051632d182be560e21b815260048101839052306024820181905260448201527f0000000000000000000000008eb67a509616cd6a7c1b3c8c21d48ff57df3d4586001600160a01b03169063b460af94906064016020604051808303815f875af115801561140f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114339190611c15565b505b600354600254610fd8916001600160a01b0390811691165f196117c3565b5f5b818110156114a35761149a83838381811061147257611472611c01565b90506020020160208101906114879190611a32565b6002546001600160a01b03165f196117c3565b50600101611455565b505050565b5f5f5f846001600160a01b0316846040516114c39190611d55565b5f604051808303815f865af19150503d805f81146114fc576040519150601f19603f3d011682016040523d82523d5f602084013e611501565b606091505b509150915081158061152f57505f815111801561152f57508080602001905181019061152d9190611d6b565b155b1561153f57600192505050611545565b5f925050505b92915050565b5f61156d856323b872dd60e01b868686604051602401610f8093929190611c2c565b9050801561065857604051630b67169d60e11b815260040160405180910390fd5b6002546003546115ab916001600160a01b039081169116836117c3565b6003549091506115e5906001600160a01b03167f0000000000000000000000008eb67a509616cd6a7c1b3c8c21d48ff57df3d458836118e8565b604051636e553f6560e01b8152600481018290523060248201527f0000000000000000000000008eb67a509616cd6a7c1b3c8c21d48ff57df3d4586001600160a01b031690636e553f65906044016020604051808303815f875af115801561164f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107fa9190611c15565b5f5b6005548110156116c0576116b76005828154811061169557611695611c01565b5f918252602090912001546002546001600160a01b0391821691165f196117c3565b50600101611675565b506104b161198c565b604051636c82bbbf60e11b81523060048201527f0000000000000000000000008eb67a509616cd6a7c1b3c8c21d48ff57df3d4586001600160a01b03169063ba08765290829063d905777e90602401602060405180830381865afa158015611733573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117579190611c15565b6040516001600160e01b031960e084901b1681526004810191909152306024820181905260448201526064016020604051808303815f875af115801561179f573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061094c9190611c15565b5f826001600160a01b0316846001600160a01b031614610b89575f19820361184e576040516370a0823160e01b81523060048201526001600160a01b038516906370a0823190602401602060405180830381865afa158015611827573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061184b9190611c15565b91505b8115610b895760015461186c9085906001600160a01b0316846118e8565b6001546040516304e144ed60e51b81526001600160a01b0390911690639c289da0906118a090879087908790600401611c2c565b6020604051808303815f875af11580156118bc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118e09190611c15565b949350505050565b6040516001600160a01b03831660248201525f604482018190529061191a90859063095ea7b360e01b90606401610f80565b9050801561193b57604051637ede7c4760e11b815260040160405180910390fd5b6040516001600160a01b03841660248201526044810183905261196b90859063095ea7b360e01b90606401610f80565b90508015610fd85760405163a31635f560e01b815260040160405180910390fd5b6104b16103cd61065f565b828054828255905f5260205f209081019282156119ea579160200282015b828111156119ea57825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906119b5565b50610e229291505b80821115610e22575f81556001016119f2565b5f60208284031215611a15575f5ffd5b5035919050565b80356001600160a01b0381168114610a7c575f5ffd5b5f60208284031215611a42575f5ffd5b610b8c82611a1c565b5f5f83601f840112611a5b575f5ffd5b50813567ffffffffffffffff811115611a72575f5ffd5b6020830191508360208260051b8501011115611a8c575f5ffd5b9250929050565b5f5f60208385031215611aa4575f5ffd5b823567ffffffffffffffff811115611aba575f5ffd5b611ac685828601611a4b565b90969095509350505050565b5f5f5f60408486031215611ae4575f5ffd5b833567ffffffffffffffff811115611afa575f5ffd5b611b0686828701611a4b565b9094509250611b19905060208501611a1c565b90509250925092565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215611b46575f5ffd5b813567ffffffffffffffff811115611b5c575f5ffd5b8201601f81018413611b6c575f5ffd5b803567ffffffffffffffff811115611b8657611b86611b22565b8060051b604051601f19603f830116810181811067ffffffffffffffff82111715611bb357611bb3611b22565b604052918252602081840181019290810187841115611bd0575f5ffd5b6020850194505b83851015611bf657611be885611a1c565b815260209485019401611bd7565b509695505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215611c25575f5ffd5b5051919050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b604080825283549082018190525f8481526020812090916060840190835b81811015611c955783546001600160a01b0316835260019384019360209093019201611c6e565b5050838103602080860191909152855180835291810192508501905f5b81811015611cd95782516001600160a01b0316845260209384019390920191600101611cb2565b50919695505050505050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561154557611545611ce5565b8181038181111561154557611545611ce5565b808202811582820484141761154557611545611ce5565b5f82611d5057634e487b7160e01b5f52601260045260245ffd5b500490565b5f82518060208501845e5f920191825250919050565b5f60208284031215611d7b575f5ffd5b81518015158114610b8c575f5ffdfea26469706673582212209a3327ae13c5e11a7dfc2563e2664876b7f43b2c0d69969c718f62b3ffa18de264736f6c634300081c0033

Verified Source Code Partial Match

Compiler: v0.8.28+commit.7893614a EVM: cancun Optimization: Yes (200 runs)
IIngress.sol 82 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "@openzeppelin/contracts/access/IAccessControl.sol";

/**
 * @title Ingress interface
 * @author Altitude Labs
 **/

interface IIngress is IAccessControl {
    // Type of transactions the rate limit can apply to
    enum RateLimitType {
        Withdraw,
        Borrow,
        Claim
    }

    event UpdateRateLimit(uint256[3] _rateLimitPeriod, uint256[3] _rateLimitAmount);
    event UpdateSanctionedList(address[] sanctionedList, bool toSanction);
    event UpdateDepositLimits(uint256 userMinDepositLimit, uint256 userMaxDepositLimit, uint256 vaultDepositLimit);
    event SetProtocolPause(bool toPause);
    event SetFunctionsState(bytes4[] functions, bool toPause);

    // Ingress Control Errors
    error IN_V1_FUNCTION_PAUSED();
    error IN_V1_PROTOCOL_PAUSED();
    error IN_V1_CLAIM_RATE_LIMIT();
    error IN_V1_BORROW_RATE_LIMIT();
    error IN_V1_WITHDRAW_RATE_LIMIT();
    error IN_V1_USER_DEPOSIT_MINIMUM_UNMET();
    error IN_V1_ACCOUNT_HAS_BEEN_SANCTIONED();
    error IN_V1_USER_DEPOSIT_LIMIT_EXCEEDED();
    error IN_V1_VAULT_DEPOSIT_LIMIT_EXCEEDED();
    error IN_ACCOUNT_CAN_NOT_CALL_THIS_FUNCTION();

    function sanctioned(address) external view returns (bool);

    function userMinDepositLimit() external view returns (uint256);

    function userMaxDepositLimit() external view returns (uint256);

    function vaultDepositLimit() external view returns (uint256);

    function pause() external view returns (bool);

    function setSanctioned(address[] memory _sanctioned, bool toSanction) external;

    function isFunctionDisabled(bytes4) external view returns (bool);

    function setRateLimit(uint256[3] memory _rateLimitPeriod, uint256[3] memory _rateLimitAmount) external;

    function setDepositLimits(
        uint256 _userMinDepositLimit,
        uint256 _userMaxDepositLimit,
        uint256 _vaultDepositLimit
    ) external;

    function setProtocolPause(bool toPause) external;

    function setFunctionsPause(bytes4[] memory functions, bool toPause) external;

    function validateDeposit(address depositor, address recipient, uint256 amount) external view;

    function validateWithdraw(address withdrawer, address recipient, uint256 amount) external;

    function validateBorrow(uint256 amount, address onBehalfOf, address receiver) external;

    function validateRepay(address repayer, address recipient) external view;

    function validateTransfer(address from, address to) external view;

    function validateClaimRewards(address claimer, uint256 amount) external;

    function validateCommit() external view;

    function validateRebalance(address sender) external view;

    function validateLiquidateUsers(address liquidator) external view;

    function validateSnapshotSupplyLoss(address sender) external view;
}
IBorrowVerifier.sol 24 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

/**
 * @author Altitude Protocol
 **/

interface IBorrowVerifier {
    // Borrow Verifier Errors
    error BV_DEADLINE_PASSED();
    error BV_INVALID_SIGNATURE();
    error BV_ONLY_VAULT();
    error BV_INVALID_VAULT();

    function nonce(address) external returns (uint256);

    function verifyAndBurnNonce(
        uint256 amount,
        address onBehalfOf,
        address receiver,
        uint256 deadline,
        bytes calldata signature
    ) external;
}
IPriceSource.sol 14 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

/**
 * @author Altitude Protocol
 **/

interface IPriceSource {
    /// @return Price of 1 `fromAsset` in `toAsset`, using toAsset's decimal count
    function getInBase(address fromAsset, address toAsset) external view returns (uint256);

    /// @return Price of 1 `fromAsset` in USD, using 8 decimal count
    function getInUSD(address fromAsset) external view returns (uint256);
}
IFlashLoanCallback.sol 10 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

/**
 * @author Altitude Protocol
 **/

interface IFlashLoanCallback {
    function flashLoanCallback(bytes calldata, uint256) external;
}
ISkimStrategy.sol 17 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

/**
 * @author Altitude Protocol
 **/

interface ISkimStrategy {
    /** @notice Emitted when an asset is not allowed for skim */
    error SK_NON_SKIM_ASSET();
    /** @notice Emitted when the receiver address is invalid */
    error SK_INVALID_RECEIVER();

    function nonSkimAssets(address asset) external view returns (bool);

    function skim(address[] calldata assets, address receiver) external;
}
IFarmDispatcher.sol 73 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";

interface IFarmDispatcher is IAccessControl {
    struct Strategy {
        bool active;
        uint256 maxAmount;
        uint256 totalDeposit;
        address prev;
        address next;
    }

    event Disable(uint256 amount);
    event Enable(uint256 amount);
    event Withdraw(uint256 amount);
    event WithdrawAll(uint256 amount);
    event SetStrategyMax(address strategyAddress, uint256 max);
    event SetStrategyPiority(address strategy, address strategyPosition);
    event Dispatch(uint256 amount);
    event StrategyError(address strategy, bytes lowLevelData);

    event DeactivateStrategy(address strategyAddress, bool toWithdraw);
    event AddStrategy(address strategyAddress, uint256 max, address position);
    event RecogniseRewards(uint256 allRewards, uint256 failedStrategies);

    error FD_ONLY_VAULT();
    error FD_VAULT_OR_OWNER();
    error FD_STRATEGY_EXISTS();
    error FD_ZERO_ASSET();
    error FS_EMPTY_STRATEGIES();
    error FS_STRATEGIES_MISMATCH();
    error FD_ZERO_STRATEGY_REMOVAL();
    error FD_STRATEGY_PRIORITY_THE_SAME();
    error FD_INACTIVE_STRATEGY();
    error FD_INACTIVE_STRATEGY_POSITION();
    error FD_INVALID_STRATEGY_DISPATCHER();

    function vault() external view returns (address);

    function asset() external view returns (address);

    function STRATEGY_ZERO() external view returns (address);

    function strategies(address) external view returns (bool, uint256, uint256, address, address);

    function initialize(address vaultAddress, address workingAsset, address owner) external;

    function setStrategyPriority(address strategyAddress, address strategyPosition) external;

    function setStrategyMax(address strategyAddress, uint256 max) external;

    function addStrategy(address strategyAddress, uint256 max, address position) external;

    function addStrategies(address[] calldata strategies, uint256[] calldata max, address position) external;

    function deactivateStrategy(address strategyAddress, bool toWithdraw) external;

    function dispatch() external;

    function withdraw(uint256 amountRequested) external returns (uint256 amountWithdrawn);

    function balance() external view returns (uint256 totalBalance);

    function balanceAvailable() external view returns (uint256 totalBalance, uint256 failedStrategies);

    function getNextStrategy(address strategy) external view returns (address);

    function recogniseRewards() external returns (uint256 allRewards, uint256 failedStrategies);

    function availableLimit() external view returns (uint256 amount);
}
IFarmDropStrategy.sol 28 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "./IFarmStrategy.sol";

/**
 * @author Altitude Protocol
 **/
interface IFarmDropStrategy is IFarmStrategy {
    event ResetDropPercentage();

    error FDS_DROP_EXCEEDED(uint256 current, uint256 threshold);
    error FDS_OUT_OF_BOUNDS();

    function dropThreshold() external view returns (uint256);

    function dropPercentage() external view returns (uint256);

    function expectedBalance() external view returns (uint256);

    function currentDropPercentage() external view returns (uint256);

    function DROP_UNITS() external view returns (uint256);

    function setDropThreshold(uint256 dropThreshold_) external;

    function reset() external;
}
IFarmStrategy.sol 45 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "../swap/ISwapStrategyConfiguration.sol";

/**
 * @author Altitude Protocol
 **/

interface IFarmStrategy is ISwapStrategyConfiguration {
    event Deposit(uint256 amount);
    event Withdraw(uint256 amount);
    event RewardsRecognition(uint256 rewards);
    event EmergencyWithdraw();
    event EmergencySwap();
    event SetRewardAssets(address[] oldAssets, address[] newAssets);

    error FS_ONLY_DISPATCHER();

    function rewardAssets(uint256) external returns (address);

    function asset() external view returns (address);

    function farmAsset() external view returns (address);

    function farmDispatcher() external view returns (address);

    function rewardsRecipient() external view returns (address);

    function deposit(uint256 amount) external;

    function recogniseRewardsInBase() external returns (uint256 rewards);

    function withdraw(uint256 amount) external returns (uint256 amountWithdrawn);

    function emergencyWithdraw() external;

    function emergencySwap(address[] calldata tokens) external returns (uint256 amountWithdrawn);

    function balance() external view returns (uint256);

    function balanceAvailable() external view returns (uint256);

    function setRewardAssets(address[] memory rewardAssets_) external;
}
IMorphoVault.sol 13 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/interfaces/IERC4626.sol";
import "./IFarmStrategy.sol";

/**
 * @author Altitude Protocol
 **/

interface IMorphoVault is IFarmStrategy {
    function morphoVault() external returns (IERC4626);
}
ISwapStrategy.sol 70 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import "../../oracles/IPriceSource.sol";

/**
 * @author Altitude Protocol
 **/

interface ISwapStrategy {
    error SWAP_STRATEGY_UNKNOWN_PAIR();
    error SWAP_STRATEGY_SWAP_NOT_PROCEEDED();
    error SWAP_STRATEGY_INVALID_DESTINATION();
    error SWAP_STRATEGY_PRICE_SOURCE_GET_IN_BASE();
    error SWAP_STRATEGY_SET_SWAP_PAIR_INPUT_INVALID();
    error SWAP_STRATEGY_ROUTE_NOT_FOUND(address assetFrom, address assetTo);

    event PriceSourceUpdated(address newSource);

    function SLIPPAGE_BASE() external view returns (uint256);

    function swapRouter() external view returns (address);

    function priceSource() external view returns (IPriceSource);

    function setPriceSource(address newPriceSource) external;

    function getMinimumAmountOut(
        address assetFrom,
        address assetTo,
        uint256 baseAmount
    ) external view returns (uint256);

    function getMinimumAmountOut(
        address assetFrom,
        address assetTo,
        uint256 baseAmount,
        uint256 slippage
    ) external view returns (uint256);

    function getMaximumAmountIn(address assetFrom, address assetTo, uint256 amountOut) external view returns (uint256);

    function getMaximumAmountIn(
        address assetFrom,
        address assetTo,
        uint256 amountOut,
        uint256 slippage
    ) external view returns (uint256);

    function swapInBase(address assetFrom, address assetTo, uint256 amount) external returns (uint256);

    function swapOutBase(
        address assetFrom,
        address assetTo,
        uint256 amount,
        uint256 amountInMaximum
    ) external returns (uint256);

    function getAmountOut(
        address assetFrom,
        address assetTo,
        uint256 amountIn
    ) external view returns (uint256 amountOut);

    function getAmountIn(
        address assetFrom,
        address assetTo,
        uint256 amountOut
    ) external view returns (uint256 amountIn);
}
ISwapStrategyConfiguration.sol 14 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import "./ISwapStrategy.sol";

interface ISwapStrategyConfiguration {
    error SSC_SWAP_AMOUNT(uint256 actualAmount, uint256 expectedAmount);

    event UpdateSwapStrategy(address newSwapStrategy);

    function swapStrategy() external view returns (ISwapStrategy);

    function setSwapStrategy(address newSwapStrategy) external;
}
IDebtToken.sol 18 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "./IInterestToken.sol";

/**
 * @author Altitude Protocol
 **/

interface IDebtToken is IInterestToken {
    // Debt token Errors
    error DT_APPROVAL_NOT_SUPPORTED();
    error DT_TRANSFER_NOT_SUPPORTED();
    error DT_ALLOWANCE_INCREASE_NOT_SUPPORTED();
    error DT_ALLOWANCE_DECREASE_NOT_SUPPORTED();

    function balanceOfDetails(address account) external view returns (uint256, uint256, uint256);
}
IInterestToken.sol 66 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";

/**
 * @author Altitude Protocol
 **/

interface IInterestToken is IERC20Upgradeable, IERC20MetadataUpgradeable {
    event UserSnapshot(address account, uint256 _interestIndex);

    // Interest Token Errors
    error IT_ONLY_VAULT();
    error IT_MINT_MORE_THAN_SIZE();
    error IT_INTEREST_INDEX_OUT_OF_RANGE();
    error IT_TRANSFER_BETWEEN_THE_SAME_ADDRESSES();

    function MATH_UNITS() external view returns (uint256);

    function vault() external view returns (address);

    function underlying() external view returns (address);

    function activeLenderStrategy() external view returns (address);

    function userIndex(address user) external view returns (uint256);

    function interestIndex() external view returns (uint256);

    function initialize(
        string memory name,
        string memory symbol,
        address vaultAddress,
        address underlyingAsset,
        address lenderStrategy,
        uint256 mathUnits
    ) external;

    function mint(address account, uint256 amount) external;

    function burn(address account, uint256 amount) external;

    function vaultTransfer(address owner, address to, uint256 amount) external returns (bool);

    function setActiveLenderStrategy(address newStrategy) external;

    function snapshotUser(address account) external returns (uint256, uint256);

    function snapshot() external;

    function calcNewIndex() external view returns (uint256 index);

    function calcIndex(uint256 balanceOld, uint256 balanceNew) external view returns (uint256);

    function balanceStored(address account) external view returns (uint256);

    function setInterestIndex(uint256 newIndex) external;

    function setBalance(address account, uint256 newBalance, uint256 newIndex) external;

    function storedTotalSupply() external view returns (uint256);

    function balanceOfAt(address account, uint256 snapshotId) external view returns (uint256);
}
ISupplyToken.sol 19 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "./IDebtToken.sol";
import "./IInterestToken.sol";

/**
 * @author Altitude Protocol
 **/

interface ISupplyToken is IInterestToken {
    // Supply token Errors
    error ST_NOT_ENOUGH_BALANCE();
    error ST_NOT_ENOUGH_ALLOWANCE();

    function transferMax(address to) external returns (bool);

    function transferFromMax(address from, address to) external returns (bool);
}
IVaultCore.sol 72 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "./IVaultStorage.sol";
import "./extensions/groomable/IGroomableVault.sol";
import "./extensions/snapshotable/ISnapshotableVault.sol";
import "./extensions/liquidatable/ILiquidatableVault.sol";
import "./extensions/configurable/IConfigurableVault.sol";

/**
 * @author Altitude Protocol
 **/

interface IVaultCoreV1 is
    IVaultStorage,
    IConfigurableVaultV1,
    IGroomableVaultV1,
    ILiquidatableVaultV1,
    ISnapshotableVaultV1
{
    event Deposit(address indexed depositor, address indexed onBehalfOf, uint256 amount);
    event Borrow(address indexed borrower, address indexed onBehalfOf, uint256 amount);
    event Withdraw(
        address indexed withdrawer,
        address indexed recipient,
        uint256 amount,
        uint256 fee,
        uint256 lenderFee
    );
    event Repay(address indexed repayer, address indexed onBehalfOf, uint256 amount);
    event RepayBadDebt(address indexed repayer, address indexed onBehalfOf, uint256 amount);

    // Vault Core V1 Errors
    error VC_V1_USER_HAS_SUPPLY();
    error VC_V1_NOT_ENOUGH_SUPPLY();
    error VC_V1_INVALID_REPAY_AMOUNT();
    error VC_V1_INVALID_BORROW_AMOUNT();
    error VC_V1_INVALID_DEPOSIT_AMOUNT();
    error VC_V1_INVALID_WITHDRAW_AMOUNT();
    error VC_V1_ETH_INSUFFICIENT_AMOUNT();
    error VC_V1_FARM_WITHDRAW_INSUFFICIENT();
    error VC_V1_NOT_ALLOWED_TO_ACT_ON_BEHALF();
    error VC_V1_NOT_AUTHORIZED_TO_DEAL_WITH_TRANSFERS();
    error VC_V1_UNHEALTHY_VAULT_RISK();
    error VC_V1_NO_DEBT_TO_REPAY();

    function preTransfer(address from, address to, uint256 amount, bytes4 transferSelector) external;

    function postTransfer(address from, address to) external;

    function deposit(uint256 amount, address onBehalfOf) external payable;

    function borrow(uint256 amount) external;

    function borrowOnBehalfOf(uint256 amount, address onBehalfOf, uint256 deadline, bytes calldata signature) external;

    function withdraw(uint256 amount, address to) external returns (uint256);

    function repay(uint256 amount, address onBehalfOf) external returns (uint256);

    function repayBadDebt(uint256 amount, address onBehalfOf) external returns (uint256);

    function depositAndBorrow(uint256 depositAmount, uint256 borrowAmount) external payable;

    function repayAndWithdraw(
        uint256 repayAmount,
        uint256 withdrawAmount,
        address to
    ) external returns (uint256, uint256);

    function calcWithdrawFee(address account, uint256 withdrawAmount) external view returns (uint256, uint256);
}
IVaultStorage.sol 57 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "../tokens/ISupplyToken.sol";
import "../tokens/IDebtToken.sol";
import "../misc/IBorrowVerifier.sol";

/**
 * @author Altitude Protocol
 **/

interface IVaultStorage {
    // Vault Storage V1 Errors
    error VS_V1_ONLY_OWNER();

    function owner() external view returns (address);

    function supplyToken() external view returns (ISupplyToken);

    function debtToken() external view returns (IDebtToken);

    function supplyUnderlying() external view returns (address);

    function borrowUnderlying() external view returns (address);

    function borrowVerifier() external view returns (IBorrowVerifier);

    function userLastDepositBlock(address) external view returns (uint256);

    function withdrawFeeFactor() external view returns (uint256);

    function withdrawFeePeriod() external view returns (uint256);

    function supplyThreshold() external view returns (uint256);

    function liquidationThreshold() external view returns (uint256);

    function targetThreshold() external view returns (uint256);

    function activeFarmStrategy() external view returns (address);

    function activeLenderStrategy() external view returns (address);

    function allowOnBehalfList(address, address) external view returns (bool);

    function onBehalfFunctions(bytes4) external view returns (bool);

    function snapshots(uint256) external view returns (uint256, uint256, uint256, uint256);

    function userSnapshots(address) external view returns (uint256);

    function configurableManager() external view returns (address);

    function swapStrategy() external view returns (address);

    function ingressControl() external view returns (address);
}
IConfigurableVault.sol 36 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

/**
 * @author Altitude Protocol
 **/

interface IConfigurableVaultV1 {
    // Configurable vault Errors
    error VCONF_V1_TARGET_THRESHOLD_NOT_REDUCED();
    error VCONF_V1_TARGET_THRESHOLD_OUT_OF_RANGE();
    error VCONF_V1_SUPPLY_THRESHOLD_OUT_OF_RANGE();
    error VCONF_V1_WITHDRAW_FEE_FACTOR_OUT_OF_RANGE();
    error VCONF_V1_LIQUIDATION_THRESHOLD_OUT_OF_RANGE();

    function setConfig(
        address confugrableManager_,
        address swapStrategy_,
        address ingressControl_,
        address borrowVerifier_,
        uint256 withdrawFeeFactor_,
        uint256 withdrawFeePeriod_
    ) external;

    function setBorrowLimits(
        uint256 supplyThreshold_,
        uint256 liquidationThreshold_,
        uint256 targetThreshold_
    ) external;

    function reduceTargetThreshold(uint256 targetThreshold_) external;

    function allowOnBehalf(address[] memory allowees, bool toAllow) external;

    function disableOnBehalfValidation(bytes4[] memory functions, bool toDisable) external;
}
IGroomableManager.sol 33 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "../../../strategy/IFlashLoanCallback.sol";

/**
 * @author Altitude Protocol
 **/

interface IGroomableManager is IFlashLoanCallback {
    event RebalanceVaultLimit(bool shouldBorrow, uint256 calculatedAmount);
    event RebalanceVaultBorrow(uint256 amountToBorrow);
    event RebalanceVaultRepay(uint256 amountToRepay, uint256 amountWithdrawn);
    event MigrateLenderStrategy(address oldStrategy, address newStrategy);
    event MigrateFarmDispatcher(address oldFarmDispatcher, address newFarmDispatcher);

    // Groomable Manager Errors
    error GR_V1_MIGRATION_FEE_TOO_HIGH();
    error GR_V1_NOT_FLASH_LOAN_STRATEGY();
    error GR_V1_MIGRATION_OLD_SUPPLY_ERROR();
    error GR_V1_MIGRATION_OLD_BORROW_ERROR();
    error GR_V1_MIGRATION_NEW_SUPPLY_ERROR();
    error GR_V1_MIGRATION_NEW_BORROW_ERROR();
    error GR_V1_FARM_DISPATCHER_ALREADY_ACTIVE();
    error GR_V1_FARM_DISPATCHER_NOT_EMPTY();
    error GR_V1_LENDER_STRATEGY_ALREADY_ACTIVE();

    function migrateLender(address newStrategy) external;

    function migrateFarmDispatcher(address newFarmDispatcher) external;

    function rebalance() external;
}
IGroomableVault.sol 24 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "../../../../../libraries/types/VaultTypes.sol";
import "./IGroomableManager.sol";

/**
 * @author Altitude Protocol
 **/

interface IGroomableVaultV1 is IGroomableManager {
    // Groomable Vault Errors
    error GR_V1_MIGRATION_PERCENTAGE_OUT_OF_RANGE();

    function migrateLender(address newStrategy) external;

    function migrateFarmDispatcher(address newFarmDispatcher) external;

    function rebalance() external;

    function setGroomableConfig(VaultTypes.GroomableConfig memory) external;

    function getGroomableConfig() external view returns (address, address, uint256);
}
IHarvestableManager.sol 40 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

/**
 * @author Altitude Protocol
 **/

interface IHarvestableManager {
    event Harvested(
        uint256 harvestId,
        uint256 distributableProfit,
        uint256 vaultLoss,
        uint256 uncommittedLossPerc,
        uint256 claimableLossPerc
    );

    event ClaimedRewards(address indexed account, uint256 amountClaimed, uint256 debtRepayed);

    event InjectedBorrowAssets(uint256 amount);

    // Harvest Errors
    error HM_V1_BLOCK_ERROR();
    error HM_V1_INVALID_INJECT_AMOUNT();
    error HM_V1_HARVEST_ERROR();
    error HM_V1_PRICE_TOO_LOW();
    error HM_V1_INVALID_COMMIT();
    error HM_V1_CLAIM_REWARDS_ZERO();
    error HV_V1_HM_NO_ACTIVE_ASSETS();
    error HV_V1_RESERVE_FACTOR_OUT_OF_RANGE();
    error HM_V1_TOO_BIG_FARM_LOSS(uint256 farmRewardsLoss);
    error HM_V1_FARM_MODE_RESERVE_NOT_ENOUGH();

    function harvest(uint256 price) external;

    function withdrawReserve(address receiver, uint256 amount) external returns (uint256);

    function claimRewards(uint256 amountRequested) external returns (uint256);

    function injectBorrowAssets(uint256 amount) external;
}
IHarvestableVault.sol 26 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "./IHarvestableManager.sol";
import "../../../../../libraries/types/HarvestTypes.sol";

/**
 * @author Altitude Protocol
 **/

interface IHarvestableVaultV1 is IHarvestableManager {
    function claimableRewards(address account) external view returns (uint256);

    function reserveAmount() external view returns (uint256);

    function getHarvest(uint256 id) external view returns (HarvestTypes.HarvestData memory);

    function getHarvestsCount() external view returns (uint256);

    function getUserHarvest(address user) external view returns (HarvestTypes.UserHarvestData memory);

    function getHarvestData()
        external
        view
        returns (uint256 realClaimableEarnings, uint256 realUncommittedEarnings, uint256 vaultReserve);
}
ILiquidatableManager.sol 20 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

/**
 * @author Altitude Protocol
 **/

interface ILiquidatableManager {
    event LiquidateUser(address user, uint256 supplyTaken, uint256 borrowRepaid);

    event LiquidateUserDefault(address user, uint256 borrowRemaining);

    // Liquidation Errors
    error LQ_V1_MAX_BONUS_OUT_OF_RANGE();
    error LQ_V1_LIQUIDATION_CONSTRAINTS();
    error LQ_V1_INSUFFICIENT_REPAY_AMOUNT();
    error LQ_V1_MAX_POSITION_LIQUIDATION_OUT_OF_RANGE();

    function liquidateUsers(address[] calldata usersForLiquidation, uint256 repayAmountLimit) external;
}
ILiquidatableVault.sol 17 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "./ILiquidatableManager.sol";
import "../../../../../libraries/types/VaultTypes.sol";

/**
 * @author Altitude Protocol
 **/

interface ILiquidatableVaultV1 is ILiquidatableManager {
    function isUserForLiquidation(address userAddress) external view returns (bool isUserForLiquidator);

    function setLiquidationConfig(VaultTypes.LiquidatableConfig memory liqConfig) external;

    function getLiquidationConfig() external view returns (address, uint256, uint256);
}
ISnapshotableManager.sol 29 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "../harvestable/IHarvestableManager.sol";
import "../supply-loss/ISupplyLossManager.sol";

/**
 * @author Altitude Protocol
 **/

interface ISnapshotableManager is IHarvestableManager, ISupplyLossManager {
    event UserCommit(
        address account,
        uint256 supplyIndex,
        uint256 supplyBalance,
        uint256 borrowIndex,
        uint256 borrowBalance,
        uint256 userHarvestUncommittedEarnings
    );
    event InjectSupply(uint256 actualInjected, uint256 amountToInject);

    function updatePosition(address account) external payable returns (uint256);

    function updatePositionTo(address account, uint256 snapshotId) external returns (uint256);

    function updatePositions(address[] calldata accounts) external returns (uint256);

    function injectSupply(uint256 targetTotalSupply, uint256 atIndex, address funder) external;
}
ISnapshotableVault.sol 26 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "./ISnapshotableManager.sol";
import "../harvestable/IHarvestableVault.sol";
import "../supply-loss/ISupplyLossVault.sol";
import "../../../../../libraries/types/VaultTypes.sol";

/**
 * @author Altitude Protocol
 **/

interface ISnapshotableVaultV1 is ISnapshotableManager, IHarvestableVaultV1, ISupplyLossVaultV1 {
    function setSnapshotableConfig(VaultTypes.SnapshotableConfig memory config) external;

    function getSnapshotableConfig() external view returns (address, uint256);

    function calcCommitUser(
        address account,
        uint256 snapshotId
    ) external view returns (HarvestTypes.UserCommit memory commit);

    function totalSnapshots() external view returns (uint256);

    function getSnapshot(uint256 id) external view returns (CommonTypes.SnapshotType memory);
}
ISupplyLossManager.sol 18 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

/**
 * @author Altitude Protocol
 **/

interface ISupplyLossManager {
    event SupplyLossSnapshot(uint256 snapshotId);

    event WithdrawVaultBorrows(uint256 withdrawn, uint256 vaultBorrows);

    event InjectVaultWindfall(uint256 vaultWindfall, uint256 expectedAmountOut, uint256 amountOut, uint256 slippageFee);

    event RepayVaultRemaining(uint256 vaultBorrowsRemaining, uint256 maxBalance);

    function snapshotSupplyLoss() external;
}
ISupplyLossVault.sol 13 lines
// SPDX-License-Identifier: AGPL-3.0.
pragma solidity 0.8.28;

import "./ISupplyLossManager.sol";
import "../../../../../libraries/types/SupplyLossTypes.sol";

/**
 * @author Altitude Protocol
 **/

interface ISupplyLossVaultV1 is ISupplyLossManager {
    function getSupplyLossSnapshot(uint256 id) external view returns (SupplyLossTypes.SupplyLoss memory);
}
CommonTypes.sol 32 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

/**
 * @title CommonTypes
 * @dev Input parameters for not having "Stack too deep"
 * @author Altitude Labs
 **/

library CommonTypes {
    /// @notice struct for the supply and borrow position of a user
    struct UserPosition {
        uint256 supplyIndex; // supplyIndex for the user
        uint256 supplyBalance; // supplyBalance for the user
        uint256 borrowIndex; // borrowIndex for the user
        uint256 borrowBalance; // borrowBalance for the user
    }

    /// @notice defines the different types of snapshots
    enum SnapshotClass {
        Harvest,
        SupplyLoss
    }

    /// @notice struct for different commit types
    struct SnapshotType {
        uint256 id; // id of the snapshot
        uint256 kind; // kind of the snapshot, where 0 is harvest, 1 is supply loss
        uint256 supplyIndex; // supplyIndex for the snapshot
        uint256 borrowIndex; // borrowIndex for the snapshot
    }
}
HarvestTypes.sol 56 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import "./CommonTypes.sol";

/**
 * @title HarvestTypes
 * @notice Harvest storage types
 * @author Altitude Labs
 **/

library HarvestTypes {
    /// @notice track the committable data for a user, detailing incremental adjustements to the user position
    struct UserCommit {
        uint256 blockNumber; // block number of the commit
        uint256 harvestId; // harvest id of the commit
        uint256 userClaimableEarnings; // user claimable earnings
        uint256 userHarvestJoiningBlock; // user harvest joining block
        uint256 userHarvestUncommittedEarnings; // user harvest uncommitted earnings
        uint256 vaultReserveUncommitted; // vault reserve uncommitted
        CommonTypes.UserPosition position; // user position
    }

    /// @notice track data for a single harvest, used for commit calculations
    struct HarvestData {
        uint256 harvestId; // harvest id
        uint256 farmEarnings; // farm earnings in the harvest
        uint256 vaultLoss; // farm loss applicable for the vault balance
        uint256 uncommittedLossPerc; // farm loss applicable in percentage for the uncommitted earnings
        uint256 claimableLossPerc; // farm loss applicable in percentage  for the claimable rewards
        uint256 activeAssetsThreshold; // active assets threshold for the harvest
        uint256 divertEarningsThreshold; // divert earnings threshold for the harvest
        uint256 vaultActiveAssets; // vault active assets
        uint256 price; // price for the harvest
        uint256 blockNumber; // block number for the harvest
    }

    /// @notice track data for a user's harvest
    struct UserHarvestData {
        uint256 harvestId; // harvest id
        uint256 harvestJoiningBlock; // harvest joining block
        uint256 claimableEarnings; // claimable earnings
        uint256 uncommittedEarnings; // uncommitted earnings (needed for partial commits)
        uint256 vaultReserveUncommitted; // vault reserve uncommitted (needed for partial commits)
    }

    /// @notice harvest storage for multiple harvests, used for commit calculations
    struct HarvestStorage {
        uint256 vaultReserve; // amount allocated to the vault reserve
        uint256 realClaimableEarnings; // total amount of known claimable earnings (users have committed)
        uint256 realUncommittedEarnings; // total amount of uncommitted earnings (prior to type allocation during commit)
        HarvestData[] harvests; // array of all harvests
        uint256 reserveFactor; // percentage of earnings to be allocated to the reserve
        mapping(address => UserHarvestData) userHarvest; // user => userHarvestData - harvest data for users
    }
}
SupplyLossTypes.sol 26 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

/**
 * @title SupplyLossTypes
 * @dev Input parameters for not having "Stack too deep"
 * @author Altitude Labs
 **/

library SupplyLossTypes {
    // @notice track data for a supply loss
    struct SupplyLoss {
        uint256 supplyLossAtSnapshot; // total reduction of supply, less fees
        uint256 supplyLossProfit; // interest earned on supply (if any, zero if supplyLossAtSnapshot > 0)
        uint256 borrowLossAtSnapshot; // total reduction of borrows
        uint256 supplyBalanceAtSnapshot; // vault total supplied at snapshot
        uint256 borrowBalanceAtSnapshot; // vault total user borrows at snapshot
        uint256 fee; // combination of liquidation penalty, slippage and deposit fees
        uint256 withdrawShortage; // vault farm balance less farm withdrawal amount (if any)
    }

    // @notice supply loss storage
    struct SupplyLossStorage {
        SupplyLoss[] supplyLosses; // array of all supply loss snapshots
    }
}
VaultTypes.sol 76 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

/**
 * @title VaultTypes
 * @dev Input parameters for not having "Stack too deep"
 * @author Altitude Labs
 **/

library VaultTypes {
    /// @notice RegistryConfiguration parameters
    struct RegistryConfiguration {
        address registryAdmin; // global registry admin used to grant roles/access
        address tokensFactory; // tokens factory implementation
        address vaultInitImpl; // vault init implementation
        address proxyAdmin; // proxy admin implementation
    }

    /// @notice Vault BorrowLimit configuration parameters
    struct BorrowLimits {
        uint256 supplyThreshold; // loan-to-value up to which the user can borrow
        uint256 liquidationThreshold; // loan-to-value after which the user can be liquidated
        uint256 targetThreshold; // loan-to-value the vault targets to rebalance to
    }

    /// @notice Vault DefiProviders configuration parameters
    struct DefiProviders {
        address lending; // address of lending provider
        address farming; // address of farming provider
    }

    /// @notice Vault configuration parameters
    struct SnapshotableConfig {
        address snapshotableManager; // snapshotable manager implementation
        uint256 reserveFactor; // percentage of earnings to be allocated to the reserve
    }

    /// @notice Vault configuration parameters
    struct VaultConfig {
        address borrowVerifier; // borrow verifier implementation
        uint256 withdrawFeeFactor; // percentage of the withdraw fee
        uint256 withdrawFeePeriod; // number of blocks the withdraw fee is applied
        address configurableManager; // configurable manager implementation
        address swapStrategy; // swap strategy implementation
        address ingressControl; // ingress control implementation
    }

    /// @notice Vault Liquidation configuration parameters
    struct LiquidatableConfig {
        address liquidatableManager; // liquidatable manager implementation
        uint256 maxPositionLiquidation; // The maximum liquidation allowed by the contract, 18 decimals
        uint256 liquidationBonus; // The supply bonus that will be received by the liquidator, 18 decimals
    }

    /// @notice Vault Groomable configuration parameters
    struct GroomableConfig {
        address groomableManager; // groomable manager implementation
        address flashLoanStrategy; // flash loan strategy implementation
        uint256 maxMigrationFeePercentage; // a fixed percentage to check if the given flash loan strategy charges higher fees than we expect
    }

    /// @notice Vault Init configuration parameters
    struct VaultInit {
        VaultConfig vaultConfig; // vault configuration
        BorrowLimits borrowLimits; // borrow limits
        DefiProviders providers; // defi providers
    }

    /// @notice Vault Data parameters
    struct VaultData {
        VaultInit vaultInit; // vault init configuration
        LiquidatableConfig liquidatableConfig; // liquidatable configuration
        GroomableConfig groomableConfig; // groomable configuration
        SnapshotableConfig snapshotableConfig; // snapshotable configuration
    }
}
TransferHelper.sol 66 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title Transfer Helper Library
 * @dev Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false
 * @author Uniswap
 **/
library TransferHelper {
    // Tranfer helper library Errors
    error TH_SAFE_TRANSFER_FAILED();
    error TH_SAFE_TRANSFER_FROM_FAILED();
    error TH_SAFE_TRANSFER_NATIVE_FAILED();
    error TH_SAFE_APPROVE();
    error TH_SAFE_APPROVE_RESET();

    function safeTransfer(address token, address to, uint256 value) internal {
        bool toThrow = _call(token, abi.encodeWithSelector(IERC20.transfer.selector, to, value));
        if (toThrow) {
            revert TH_SAFE_TRANSFER_FAILED();
        }
    }

    function safeTransferFrom(address token, address from, address to, uint256 value) internal {
        bool toThrow = _call(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
        if (toThrow) {
            revert TH_SAFE_TRANSFER_FROM_FAILED();
        }
    }

    /// @notice Approves the stipulated contract to spend the given allowance in the given token
    /// @dev Errors with 'SA' if transfer fails
    /// @param token The contract address of the token to be approved
    /// @param to The target of the approval
    /// @param value The amount of the given token the target will be allowed to spend
    function safeApprove(address token, address to, uint256 value) internal {
        // Reset approval first
        bool toThrow = _call(token, abi.encodeWithSelector(IERC20.approve.selector, to, 0));
        if (toThrow) {
            revert TH_SAFE_APPROVE_RESET();
        }

        toThrow = _call(token, abi.encodeWithSelector(IERC20.approve.selector, to, value));
        if (toThrow) {
            revert TH_SAFE_APPROVE();
        }
    }

    function _call(address token, bytes memory data) internal returns (bool) {
        (bool success, bytes memory resultData) = token.call(data);
        if (!success || (resultData.length > 0 && !abi.decode(resultData, (bool)))) {
            return true;
        }

        return false;
    }

    function safeTransferNative(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        if (!success) {
            revert TH_SAFE_TRANSFER_NATIVE_FAILED();
        }
    }
}
SkimStrategy.sol 54 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../libraries/uniswap-v3/TransferHelper.sol";
import "../interfaces/internal/strategy/ISkimStrategy.sol";

/**
 * @title SkimStrategy
 * @dev Contract for skiming assets
 * @author Altitude Labs
 **/

contract SkimStrategy is Ownable, ISkimStrategy {
    /** @notice Assets that are not allowed for skim */
    mapping(address => bool) public nonSkimAssets;

    /// @param assets Assets that are not allowed for skim
    constructor(address[] memory assets) {
        uint256 assetsLength = assets.length;
        for (uint256 i; i < assetsLength; ) {
            nonSkimAssets[assets[i]] = true;
            unchecked {
                ++i;
            }
        }
    }

    /** @notice Transfer tokens out of the strategy
     * @dev Used to even out distributions when rewards accrue in batches
     * @param assets Token addresses
     * @param receiver Receiving account
     */
    function skim(address[] calldata assets, address receiver) public override onlyOwner {
        if (receiver == address(0)) {
            revert SK_INVALID_RECEIVER();
        }

        uint256 assetsLength = assets.length;
        for (uint256 i; i < assetsLength; ) {
            if (nonSkimAssets[assets[i]]) {
                revert SK_NON_SKIM_ASSET();
            }

            TransferHelper.safeTransfer(assets[i], receiver, IERC20(assets[i]).balanceOf(address(this)));

            unchecked {
                ++i;
            }
        }
    }
}
FarmDropStrategy.sol 164 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";

import "./FarmStrategy.sol";
import "../../../interfaces/internal/strategy/farming/IFarmDropStrategy.sol";

/**
 * @title FarmDropStrategy Contract
 * @dev Provides tools to monitor drops in the farm balance value
 * @author Altitude Labs
 **/

abstract contract FarmDropStrategy is Ownable, FarmStrategy, IFarmDropStrategy {
    uint256 public override expectedBalance;
    uint256 public override dropPercentage;
    uint256 public constant override DROP_UNITS = 1e18;

    /// @notice Default drop threshold to revert deposit/withdraw calls is 5%
    uint256 public override dropThreshold = (DROP_UNITS * 5) / 100;

    constructor(
        address farmAssetAddress,
        address farmDispatcherAddress,
        address rewardsAddress,
        address[] memory rewardAssets_,
        address swapStrategyAddress
    ) FarmStrategy(farmAssetAddress, farmDispatcherAddress, rewardsAddress, rewardAssets_, swapStrategyAddress) {}

    /// @notice Set the threshold for reverting deposit/withdraw
    /// @param dropThreshold_ The new threshold in percentage (of DROP_UNITS, 1e18)
    function setDropThreshold(uint256 dropThreshold_) external onlyOwner {
        if (dropThreshold_ > DROP_UNITS) {
            revert FDS_OUT_OF_BOUNDS();
        }

        dropThreshold = dropThreshold_;
    }

    /// @notice Deal with a farm drop during deposit
    /// If a farm drop is detected, the deposit will be reverted
    /// @param amount The amount to deposit
    function deposit(uint256 amount) public override(FarmStrategy, IFarmStrategy) {
        uint256 currentBalance = balance();
        _updateDropPercentage(currentBalance, 0);
        if (dropPercentage > dropThreshold) {
            revert FDS_DROP_EXCEEDED(dropPercentage, dropThreshold);
        }

        // deposit and check for any deposit fees
        expectedBalance = currentBalance;
        super.deposit(amount);
        currentBalance = balance();
        _updateDropPercentage(currentBalance, amount);
        expectedBalance = currentBalance;
    }

    /// @notice Track and handle any possible farm drop on withdraw
    /// If a farm drop is detected, the withdraw will be reverted
    /// @param amountRequested The amount to withdraw

    function withdraw(
        uint256 amountRequested
    ) public override(FarmStrategy, IFarmStrategy) returns (uint256 amountWithdrawn) {
        _updateDropPercentage(balance(), 0);
        if (dropPercentage > dropThreshold) {
            revert FDS_DROP_EXCEEDED(dropPercentage, dropThreshold);
        }
        amountWithdrawn = super.withdraw(amountRequested);
        expectedBalance = balance();
    }

    /// @notice Update the drop percentage on emergencyWithdraw
    function emergencyWithdraw() public override(FarmStrategy, IFarmStrategy) {
        _updateDropPercentage(balance(), 0);
        super.emergencyWithdraw();
        expectedBalance = balance();
    }

    /// @notice Update the drop percentage on emergencySwap
    /// @param assets The assets to swap
    function emergencySwap(
        address[] calldata assets
    ) public override(FarmStrategy, IFarmStrategy) returns (uint256 amountWithdrawn) {
        _updateDropPercentage(balance(), 0);
        amountWithdrawn = super.emergencySwap(assets);
        expectedBalance = balance();
    }

    /// @notice Account for increase/decrease in the farm drop percentage
    /// @param amount Expected balance increase
    function _updateDropPercentage(uint256 currentBalance, uint256 amount) internal {
        dropPercentage = _calculateDropPercentage(currentBalance, expectedBalance + amount, dropPercentage);
    }

    /// @notice Calculates the current drop in farm value as percentage
    /// @return percentage The total drop in farm value as a percentage
    function currentDropPercentage() public view override returns (uint256) {
        return _calculateDropPercentage(balance(), expectedBalance, dropPercentage);
    }

    /// @notice Decrease drop percentage with the rewards that are used to restore it
    function _recogniseRewardsInBase() internal virtual override {
        /// @dev It is assumed the balance to be bigger than the expected one
        // as the rewards have been recognised from the inherited contract
        // The drop percentage is to be decreased with the new rewards
        _updateDropPercentage(balance(), 0);
    }

    /// @notice Track and handle any possible farm drop on recogniseRewardsInBase
    /// @return rewards The amount of rewards recognised
    function recogniseRewardsInBase() public virtual override(FarmStrategy, IFarmStrategy) returns (uint256 rewards) {
        rewards = super.recogniseRewardsInBase();
        expectedBalance = balance();
    }

    /// @notice Calculates the drop in farm value as percentage
    /// @param currentBalance_ The current amount in farming
    /// @param expectedBalance_ The expected amount in farming
    /// @param accumulatedDrop_ The drop percentage accumulated so far
    /// @return amount The total drop in farm value as a percentage
    function _calculateDropPercentage(
        uint256 currentBalance_,
        uint256 expectedBalance_,
        uint256 accumulatedDrop_
    ) private pure returns (uint256) {
        if (expectedBalance_ == 0) {
            // If we expect the farm to be empty there can be no drop
            return 0;
        }

        if (currentBalance_ > expectedBalance_) {
            // Gained value
            uint256 percentage = ((currentBalance_ - expectedBalance_) * DROP_UNITS) / expectedBalance_;
            uint256 percentageAdjustment = (accumulatedDrop_ + ((accumulatedDrop_ * percentage) / DROP_UNITS));
            if (percentageAdjustment > percentage) {
                accumulatedDrop_ = percentageAdjustment - percentage;
            } else {
                // Farm is at net gain, new peak from where we will start tracking losses
                accumulatedDrop_ = 0;
            }
        } else {
            if (currentBalance_ == 0) {
                // Lost everything shortcut
                accumulatedDrop_ = DROP_UNITS;
            } else {
                // Lost some
                uint256 percentage = ((expectedBalance_ - currentBalance_) * DROP_UNITS) / expectedBalance_;
                accumulatedDrop_ = (accumulatedDrop_ - ((accumulatedDrop_ * percentage) / DROP_UNITS)) + percentage;
            }
        }

        return accumulatedDrop_;
    }

    /// @notice Reset drop percentage
    function reset() external override onlyOwner {
        dropPercentage = 0;
        expectedBalance = balance();

        emit ResetDropPercentage();
    }
}
FarmStrategy.sol 219 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../../swap/SwapStrategyConfiguration.sol";
import "../../../libraries/uniswap-v3/TransferHelper.sol";
import "../../../interfaces/internal/access/IIngress.sol";
import "../../../interfaces/internal/vault/IVaultCore.sol";
import "../../../interfaces/internal/strategy/farming/IFarmStrategy.sol";
import "../../../interfaces/internal/strategy/farming/IFarmDispatcher.sol";

/**
 * @title FarmStrategy Contract
 * @author Altitude Labs
 **/

abstract contract FarmStrategy is Ownable, SwapStrategyConfiguration, IFarmStrategy {
    address public override asset; // baseAsset of farmDispatcher
    address public override farmAsset; // asset of the farm
    address public override farmDispatcher; // farmDispatcher address
    address[] public override rewardAssets; // reward tokens to recognise
    address public override rewardsRecipient; // where to send rewards

    modifier onlyDispatcher() {
        if (msg.sender != farmDispatcher) {
            revert FS_ONLY_DISPATCHER();
        }
        _;
    }

    /// @param farmAssetAddress The address of the token we are farming with
    /// @param farmDispatcherAddress The manager of the strategy
    /// @param rewardsAddress Where to send any reward tokens
    /// @param rewardAssets_ Reward tokens to recognise
    /// @param swapStrategyAddress Swap strategy needed in case farmAsset != baseAsset
    constructor(
        address farmAssetAddress,
        address farmDispatcherAddress,
        address rewardsAddress,
        address[] memory rewardAssets_,
        address swapStrategyAddress
    ) SwapStrategyConfiguration(swapStrategyAddress) {
        farmAsset = farmAssetAddress;
        farmDispatcher = farmDispatcherAddress;
        rewardsRecipient = rewardsAddress;
        rewardAssets = rewardAssets_;
        asset = IFarmDispatcher(farmDispatcher).asset();
    }

    /// @notice Sets the reward tokens to be recognised
    /// @param rewardAssets_ Token addresses
    function setRewardAssets(address[] memory rewardAssets_) external onlyOwner {
        emit SetRewardAssets(rewardAssets, rewardAssets_);

        rewardAssets = rewardAssets_;
    }

    /// @notice Deposits own funds into the Farm Provider
    /// @param amount amount to deposit
    function deposit(uint256 amount) public virtual override onlyDispatcher {
        if (amount > 0) {
            // Transfer funds from dispatcher
            TransferHelper.safeTransferFrom(asset, msg.sender, address(this), amount);

            _deposit(IERC20(asset).balanceOf(address(this)));

            emit Deposit(amount);
        }
    }

    /// @notice Withdraws from the Farm Provider
    /// @param amountRequested The amount to withdraw
    /// @return amountWithdrawn The amount actually withdrawn
    function withdraw(
        uint256 amountRequested
    ) public virtual override onlyDispatcher returns (uint256 amountWithdrawn) {
        if (amountRequested > 0) {
            // When trying to withdraw all
            if (amountRequested == type(uint256).max) {
                // balanceAvailable() skips the swap slippage check, as that will happen in the actual withdraw
                amountRequested = balanceAvailable();
            }

            _withdraw(amountRequested);
            amountWithdrawn = IERC20(asset).balanceOf(address(this));

            if (amountWithdrawn > 0) {
                TransferHelper.safeTransfer(asset, msg.sender, amountWithdrawn);
            }

            emit Withdraw(amountWithdrawn);
        }
    }

    /// @notice Withdraw everything from the farm with minimal constraints
    /// @dev Should be invoked via protected rpc
    /// @dev We may want to perform intermediate actions, so this is step one of a two step process
    /// @dev Step two is emergencySwap()
    function emergencyWithdraw() public virtual onlyOwner {
        _emergencyWithdraw();

        emit EmergencyWithdraw();
    }

    /// @notice Swap specified tokens to asset
    /// @param assets The assets to swap
    /// @return amountWithdrawn The amount withdrawn after swap
    function emergencySwap(
        address[] calldata assets
    ) public virtual override onlyOwner returns (uint256 amountWithdrawn) {
        _emergencySwap(assets);

        amountWithdrawn = IERC20(asset).balanceOf(address(this));
        TransferHelper.safeTransfer(asset, farmDispatcher, amountWithdrawn);

        emit EmergencySwap();
    }

    /// @notice Claim and swap reward tokens to base asset. Then transfer to the dispatcher for compounding
    /// @return rewards An amount of rewards being recognised
    function recogniseRewardsInBase() public virtual override returns (uint256 rewards) {
        _recogniseRewardsInBase();

        rewards = IERC20(asset).balanceOf(address(this));
        TransferHelper.safeTransfer(asset, rewardsRecipient, rewards);

        emit RewardsRecognition(rewards);
    }

    /// @notice Return the balance in borrow asset excluding rewards (includes slippage validations)
    /// @dev Reverts if slippage is too high
    /// @return balance that can be withdrawn from the farm
    function balance() public view virtual returns (uint256) {
        // Get amount of tokens
        uint256 farmAssetAmount = _getFarmAssetAmount();
        (uint256 totalBalance, uint256 swapAmount) = _balance(farmAssetAmount);

        if (swapAmount > 0) {
            // Validate slippage
            uint256 minimumAssetAmount = swapStrategy.getMinimumAmountOut(farmAsset, asset, farmAssetAmount);

            if (swapAmount < minimumAssetAmount) {
                // Amount is no good since slippage is too high.
                // @dev harvest() earnings calculation relies on .balance() so it is important to revert on a bad value
                revert SSC_SWAP_AMOUNT(minimumAssetAmount, swapAmount);
            }
        }

        return totalBalance;
    }

    /// @notice Return the balance in borrow asset excluding rewards (no slippage validations)
    /// @dev Function will not revert on high slippage, should used with care in transactions
    /// @return availableBalance Balance that can be withdrawn from the farm
    function balanceAvailable() public view virtual returns (uint256 availableBalance) {
        // No slippage validations
        (availableBalance, ) = _balance(_getFarmAssetAmount());
    }

    /// @notice Return the max amount that can be withdrawn at the moment
    /// @param farmAssetAmount The amount from the farm provider
    /// @return totalBalance The amount available to be withdrawn (including amount swapped)
    /// @return swapAmount Amount of totalBalance that is subject to swapping
    function _balance(
        uint256 farmAssetAmount
    ) internal view virtual returns (uint256 totalBalance, uint256 swapAmount) {
        totalBalance = IERC20(asset).balanceOf(address(this));
        if (farmAssetAmount > 0) {
            if (farmAsset == asset) {
                totalBalance += farmAssetAmount;
            } else {
                // amount of borrow asset we'd get if we swap
                swapAmount = swapStrategy.getAmountOut(farmAsset, asset, farmAssetAmount);

                totalBalance += swapAmount;
            }
        }
    }

    function _getFarmAssetAmount() internal view virtual returns (uint256);

    function _deposit(uint256 amount) internal virtual;

    function _withdraw(uint256 amount) internal virtual;

    function _emergencyWithdraw() internal virtual;

    function _recogniseRewardsInBase() internal virtual;

    /// @notice Swap assets to borrow asset
    /// @param assets Array of assets to swap
    function _emergencySwap(address[] calldata assets) internal virtual {
        for (uint256 i; i < assets.length; ++i) {
            _swap(assets[i], asset, type(uint256).max);
        }
    }

    /// @notice Swap between different assets
    /// @param inputAsset Input asset address
    /// @param outputAsset Output asset address
    /// @param amount Amount to swap
    function _swap(address inputAsset, address outputAsset, uint256 amount) internal virtual returns (uint256) {
        if (inputAsset != outputAsset) {
            if (amount == type(uint256).max) {
                amount = IERC20(inputAsset).balanceOf(address(this));
            }

            if (amount > 0) {
                TransferHelper.safeApprove(inputAsset, address(swapStrategy), amount);

                amount = swapStrategy.swapInBase(inputAsset, outputAsset, amount);
            }
        }

        return amount;
    }
}
MorphoVault.sol 94 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/interfaces/IERC4626.sol";

import "../FarmDropStrategy.sol";
import "../../../SkimStrategy.sol";
import "../../../../libraries/uniswap-v3/TransferHelper.sol";
import "../../../../interfaces/internal/strategy/farming/IMorphoVault.sol";

/**
 * @title MorphoVault Contract
 * @dev Contract for interacting with MetaMorpho vaults
 * @author Altitude Labs
 **/

contract MorphoVault is FarmDropStrategy, SkimStrategy, IMorphoVault {
    IERC4626 public immutable morphoVault;

    constructor(
        address farmDispatcherAddress_,
        address rewardsAddress_,
        address swapStrategy_,
        IERC4626 morphoVault_,
        address[] memory rewardAssets_,
        address[] memory nonSkimAssets_
    )
        FarmDropStrategy(morphoVault_.asset(), farmDispatcherAddress_, rewardsAddress_, rewardAssets_, swapStrategy_)
        SkimStrategy(nonSkimAssets_)
    {
        morphoVault = morphoVault_;
    }

    /// @notice Deposit into Morpho
    /// @param amount Amount of asset to deposit
    function _deposit(uint256 amount) internal override {
        amount = _swap(asset, farmAsset, amount);
        TransferHelper.safeApprove(farmAsset, address(morphoVault), amount);
        morphoVault.deposit(amount, address(this));
    }

    /// @notice Withdraw from Morpho
    /// @param amountRequested Amount of asset to withdraw
    function _withdraw(uint256 amountRequested) internal override {
        uint256 amountToWithdraw = amountRequested;

        if (farmAsset != asset) {
            // If a conversion is happening, we substitute the requested sum with the
            // input amount we'd need to provide to get it in a swap
            amountToWithdraw = swapStrategy.getAmountIn(farmAsset, asset, amountToWithdraw);
        }

        uint256 farmBalance = morphoVault.maxWithdraw(address(this));

        if (amountToWithdraw > farmBalance) {
            // If requested amount is more than we have, recognise rewards and withdraw all
            if (farmBalance > 0) {
                morphoVault.withdraw(farmBalance, address(this), address(this));
            }
        } else {
            // Else withdraw the requested amount
            if (farmBalance > 0) {
                morphoVault.withdraw(amountToWithdraw, address(this), address(this));
            }
        }

        // Swap the farm asset to borrow asset (if required)
        _swap(farmAsset, asset, type(uint256).max);
    }

    /// @notice Withdraw as much as possible from Morpho
    function _emergencyWithdraw() internal override {
        morphoVault.redeem(morphoVault.maxRedeem(address(this)), address(this), address(this));
    }

    /// @notice Return farm asset ammount specific for the farm provider
    function _getFarmAssetAmount() internal view virtual override returns (uint256 farmAssetAmount) {
        uint256 shares = morphoVault.balanceOf(address(this));
        if (shares > 0) {
            farmAssetAmount = morphoVault.convertToAssets(shares);
        }
    }

    /// @notice Internal reusable function
    function _recogniseRewardsInBase() internal override {
        for (uint256 i; i < rewardAssets.length; ++i) {
            _swap(rewardAssets[i], asset, type(uint256).max);
        }

        // Update drop percentage
        super._recogniseRewardsInBase();
    }
}
SwapStrategyConfiguration.sol 29 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import "@openzeppelin/contracts/access/Ownable.sol";
import "../../interfaces/internal/strategy/swap/ISwapStrategyConfiguration.sol";

/**
 * @title SwapStrategyConfiguration Contract
 * @dev Base contract for swap strategy setup
 * @author Altitude Labs
 **/

contract SwapStrategyConfiguration is Ownable, ISwapStrategyConfiguration {
    /** @notice integration with swap provider */
    ISwapStrategy public override swapStrategy;

    constructor(address swapStrategy_) {
        swapStrategy = ISwapStrategy(swapStrategy_);
    }

    /// @notice Every lending/farming strategy has to deal with reward tokens
    /// @notice Because of this, it should use an exchange strategy to swap
    /// @notice the reward tokens for the base asset
    /// @param newSwapStrategy The exchange strategy
    function setSwapStrategy(address newSwapStrategy) external onlyOwner {
        swapStrategy = ISwapStrategy(newSwapStrategy);
        emit UpdateSwapStrategy(newSwapStrategy);
    }
}
IERC20Upgradeable.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}
IERC20MetadataUpgradeable.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
IAccessControl.sol 88 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
IERC4626.sol 240 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 *
 * _Available since v4.7._
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);
}
IERC20.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}
IERC20Metadata.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

Read Contract

DROP_UNITS 0xf3217f50 → uint256
asset 0x38d52e0f → address
balance 0xb69ef8a8 → uint256
balanceAvailable 0x7da2d1f4 → uint256
currentDropPercentage 0x39965dde → uint256
dropPercentage 0x39ddcb18 → uint256
dropThreshold 0xbe5ed586 → uint256
expectedBalance 0x329d0fa5 → uint256
farmAsset 0xd751d25f → address
farmDispatcher 0x193bc784 → address
morphoVault 0x5169379c → address
nonSkimAssets 0x842a69ae → bool
owner 0x8da5cb5b → address
rewardAssets 0xe110dfea → address
rewardsRecipient 0xff2a7d30 → address
swapStrategy 0x8088c318 → address

Write Contract 12 functions

These functions modify contract state and require a wallet transaction to execute.

deposit 0xb6b55f25
uint256 amount
emergencySwap 0x7766059d
address[] assets
returns: uint256
emergencyWithdraw 0xdb2e21bc
No parameters
recogniseRewardsInBase 0xc22223fd
No parameters
returns: uint256
renounceOwnership 0x715018a6
No parameters
reset 0xd826f88f
No parameters
setDropThreshold 0xcac3e147
uint256 dropThreshold_
setRewardAssets 0xc1f5a1bc
address[] rewardAssets_
setSwapStrategy 0x3e09a95f
address newSwapStrategy
skim 0x8e1ac2d2
address[] assets
address receiver
transferOwnership 0xf2fde38b
address newOwner
withdraw 0x2e1a7d4d
uint256 amountRequested
returns: uint256

Recent Transactions

This address has 1 on-chain transactions, but only 1.3% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →