Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x58907ad5c7eD1EaB5FdCc0Cc347F25bF5BC0e7da
Balance 0 ETH
Nonce 1
Code Size 6857 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

6857 bytes
0x60806040526004361061013d575f3560e01c80621a55971461014857806301a4c2f81461016957806306fdde03146101885780631aac030c146101a95780631c2ca2fd146101ca5780631f660123146101f35780632e1a7d4d1461020757806333ba095714610226578063461632991461026657806352efea6e146102855780635f6550d5146102995780636c23ab4c146102b8578063715018a6146102cc5780637654f7ab146102e057806379ba5097146102f45780638da5cb5b146103085780639d80349b1461031c578063a4ba0b101461034f578063b4027ea81461036e578063ba7b67031461038d578063d0e30db0146103a2578063d4532dfd146103aa578063dd07d288146103dd578063e30c3978146103f2578063f2fde38b14610406578063f77c479114610425578063fd92bff214610207575f80fd5b3661014457005b5f80fd5b348015610153575f80fd5b505f5b6040519081526020015b60405180910390f35b348015610174575f80fd5b50610156610183366004611677565b610458565b348015610193575f80fd5b5061019c610646565b60405161016091906116b0565b3480156101b4575f80fd5b506101c86101c3366004611677565b6106d2565b005b3480156101d5575f80fd5b506101de6106df565b60408051928352602083019190915201610160565b3480156101fe575f80fd5b50610156610892565b348015610212575f80fd5b50610156610221366004611677565b610a75565b348015610231575f80fd5b506102597f000000000000000000000000a2e3356610840701bdf5611a53974510ae27e2e181565b60405161016091906116e2565b348015610271575f80fd5b506101c8610280366004611677565b610acd565b348015610290575f80fd5b50610156610b4d565b3480156102a4575f80fd5b506101566102b336600461170a565b610bcf565b3480156102c3575f80fd5b50610156610dbe565b3480156102d7575f80fd5b506101c8610dfd565b3480156102eb575f80fd5b50610156610e10565b3480156102ff575f80fd5b506101c8610e1e565b348015610313575f80fd5b50610259610e9c565b348015610327575f80fd5b506102597f00000000000000000000000079973d557cd9dd87eb61e250cc2572c990e2019681565b34801561035a575f80fd5b50610156610369366004611677565b610eaa565b348015610379575f80fd5b50610156610388366004611677565b61105a565b348015610398575f80fd5b5061015660035481565b6101c8611288565b3480156103b5575f80fd5b506102597f000000000000000000000000422f5accc812c396600010f224b320a743695f8581565b3480156103e8575f80fd5b5061015660025481565b3480156103fd575f80fd5b50610259611348565b348015610411575f80fd5b506101c8610420366004611738565b611357565b348015610430575f80fd5b506102597f000000000000000000000000396abf9ff46e21694f4ef01ca77c6d7893a017b281565b5f6104616113bd565b7f000000000000000000000000a2e3356610840701bdf5611a53974510ae27e2e15f8390036104ab5760405162461bcd60e51b81526004016104a290611753565b60405180910390fd5b6040516370a0823160e01b81526001600160a01b038216906370a08231906104d79030906004016116e2565b602060405180830381865afa1580156104f2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105169190611771565b8311156105355760405162461bcd60e51b81526004016104a290611788565b60405163a090728360e01b8152600481018490526001600160a01b0382169063a0907283906024015f604051808303815f87803b158015610574575f80fd5b505af1158015610586573d5f803e3d5ffd5b505050507f0000000000000000000000000000000000000000000000000de0b6b3a76400007f000000000000000000000000a2e3356610840701bdf5611a53974510ae27e2e16001600160a01b0316633ba0b9a96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610607573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061062b9190611771565b61063590856117c4565b61063f91906117db565b9392505050565b60048054610653906117fa565b80601f016020809104026020016040519081016040528092919081815260200182805461067f906117fa565b80156106ca5780601f106106a1576101008083540402835291602001916106ca565b820191905f5260205f20905b8154815290600101906020018083116106ad57829003601f168201915b505050505081565b6106da6113bd565b600355565b60405163781364bd60e01b81525f9081907f00000000000000000000000079973d557cd9dd87eb61e250cc2572c990e201969082906001600160a01b0383169063781364bd906107339030906004016116e2565b5f60405180830381865afa15801561074d573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261077491908101906118ad565b80519091505f81900361078d57505f9485945092505050565b5f836001600160a01b0316630d6680876040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107ca573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107ee9190611771565b90505f5b82811015610889575f84828151811061080d5761080d6119a8565b6020026020010151905080608001515f146108285750610877565b82816060015161083891906119bc565b421015801561084857508060a001515b1561086357604081015161085c90896119bc565b9750610875565b604081015161087290886119bc565b96505b505b80610881816119cf565b9150506107f2565b50505050509091565b5f807f000000000000000000000000a2e3356610840701bdf5611a53974510ae27e2e16001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016108e091906116e2565b602060405180830381865afa1580156108fb573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061091f9190611771565b90505f7f000000000000000000000000422f5accc812c396600010f224b320a743695f856001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161096e91906116e2565b602060405180830381865afa158015610989573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109ad9190611771565b90507f0000000000000000000000000000000000000000000000000de0b6b3a76400007f000000000000000000000000a2e3356610840701bdf5611a53974510ae27e2e16001600160a01b0316633ba0b9a96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a2c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a509190611771565b610a5a83856119bc565b610a6491906117c4565b610a6e91906117db565b9250505090565b5f7f000000000000000000000000396abf9ff46e21694f4ef01ca77c6d7893a017b26001600160a01b03163314610abe5760405162461bcd60e51b81526004016104a2906119e7565b610ac78261141c565b92915050565b610ad56113bd565b60405163b13acedd60e01b8152600481018290527f00000000000000000000000079973d557cd9dd87eb61e250cc2572c990e201966001600160a01b03169063b13acedd906024015f604051808303815f87803b158015610b34575f80fd5b505af1158015610b46573d5f803e3d5ffd5b5050505050565b5f7f000000000000000000000000396abf9ff46e21694f4ef01ca77c6d7893a017b26001600160a01b03163314610b965760405162461bcd60e51b81526004016104a2906119e7565b478015610bcb57610bc77f000000000000000000000000396abf9ff46e21694f4ef01ca77c6d7893a017b28261146d565b8091505b5090565b5f610bd86113bd565b825f03610bf75760405162461bcd60e51b81526004016104a290611753565b47831115610c175760405162461bcd60e51b81526004016104a290611788565b6040516370a0823160e01b81527f000000000000000000000000a2e3356610840701bdf5611a53974510ae27e2e1905f906001600160a01b038316906370a0823190610c679030906004016116e2565b602060405180830381865afa158015610c82573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ca69190611771565b9050816001600160a01b031663f340fa0186866040518363ffffffff1660e01b8152600401610cd591906116e2565b5f604051808303818588803b158015610cec575f80fd5b505af1158015610cfe573d5f803e3d5ffd5b50506040516370a0823160e01b81528493506001600160a01b03861692506370a082319150610d319030906004016116e2565b602060405180830381865afa158015610d4c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d709190611771565b610d7a9190611a0f565b60408051878152602081018390529194507f29c78d6f3bc4be22a7d3b080c2ee4dd05e0cabf415da6e105dce069930d4062b910160405180910390a1505092915050565b5f478180610dca6106df565b91509150610dd6610892565b81610de184866119bc565b610deb91906119bc565b610df591906119bc565b935050505090565b610e056113bd565b610e0e5f611511565b565b5f610e19610dbe565b905090565b3380610e28611348565b6001600160a01b031614610e905760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b60648201526084016104a2565b610e9981611511565b50565b5f546001600160a01b031690565b5f610eb36113bd565b815f03610ed25760405162461bcd60e51b81526004016104a290611753565b610f1d7f000000000000000000000000a2e3356610840701bdf5611a53974510ae27e2e17f000000000000000000000000422f5accc812c396600010f224b320a743695f858461152a565b6040516311f9fbc960e21b81526001600160a01b037f000000000000000000000000422f5accc812c396600010f224b320a743695f8516906347e7ef2490610f6b9030908690600401611a22565b6020604051808303815f875af1158015610f87573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fab9190611771565b9050805f03610fee5760405162461bcd60e51b815260206004820152600f60248201526e6d696e74207a65726f20736861726560881b60448201526064016104a2565b604080518381526020810183905230917f000000000000000000000000422f5accc812c396600010f224b320a743695f856001600160a01b0316917f3b6ddb74a836538977a46805c3f29a3ea63f9caabc0a0ad0ffae36ec611ea35691015b60405180910390a3919050565b5f6110636113bd565b815f036110825760405162461bcd60e51b81526004016104a290611753565b6040516370a0823160e01b81526001600160a01b037f000000000000000000000000a2e3356610840701bdf5611a53974510ae27e2e116906370a08231906110ce9030906004016116e2565b602060405180830381865afa1580156110e9573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061110d9190611771565b60405163f3fef3a360e01b81529091506001600160a01b037f000000000000000000000000422f5accc812c396600010f224b320a743695f85169063f3fef3a39061115e9030908690600401611a22565b5f604051808303815f87803b158015611175575f80fd5b505af1158015611187573d5f803e3d5ffd5b50506040516370a0823160e01b81528392506001600160a01b037f000000000000000000000000a2e3356610840701bdf5611a53974510ae27e2e11691506370a08231906111d99030906004016116e2565b602060405180830381865afa1580156111f4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112189190611771565b6112229190611a0f565b604080518481526020810183905291925030917f000000000000000000000000422f5accc812c396600010f224b320a743695f856001600160a01b0316917f77e35408ff044a9c6ce57c6d1f0795af280d3ca2e043065997f5f92c0af57463910161104d565b7f000000000000000000000000396abf9ff46e21694f4ef01ca77c6d7893a017b26001600160a01b031633146112d05760405162461bcd60e51b81526004016104a2906119e7565b426003546002546112e191906119bc565b11156113235760405162461bcd60e51b81526020600482015260116024820152706174207468652073616d6520626c6f636b60781b60448201526064016104a2565b345f036113425760405162461bcd60e51b81526004016104a290611a3b565b42600255565b6001546001600160a01b031690565b61135f6113bd565b600180546001600160a01b0319166001600160a01b038316908117909155611385610e9c565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b336113c6610e9c565b6001600160a01b031614610e0e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104a2565b5f815f0361143c5760405162461bcd60e51b81526004016104a290611a3b565b50806114687f000000000000000000000000396abf9ff46e21694f4ef01ca77c6d7893a017b28261146d565b919050565b604080515f808252602082019092526001600160a01b0384169083906040516114969190611a5f565b5f6040518083038185875af1925050503d805f81146114d0576040519150601f19603f3d011682016040523d82523d5f602084013e6114d5565b606091505b505090508061150c5760405162461bcd60e51b815260206004820152600360248201526253544560e81b60448201526064016104a2565b505050565b600180546001600160a01b0319169055610e9981611628565b5f80846001600160a01b031663095ea7b360e01b8585604051602401611551929190611a22565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161158f9190611a5f565b5f604051808303815f865af19150503d805f81146115c8576040519150601f19603f3d011682016040523d82523d5f602084013e6115cd565b606091505b50915091508180156115f75750805115806115f75750808060200190518101906115f79190611a7a565b610b465760405162461bcd60e51b8152602060048201526002602482015261534160f01b60448201526064016104a2565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f60208284031215611687575f80fd5b5035919050565b5f5b838110156116a8578181015183820152602001611690565b50505f910152565b602081525f82518060208401526116ce81604085016020870161168e565b601f01601f19169190910160400192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0381168114610e99575f80fd5b5f806040838503121561171b575f80fd5b82359150602083013561172d816116f6565b809150509250929050565b5f60208284031215611748575f80fd5b813561063f816116f6565b6020808252600490820152637a65726f60e01b604082015260600190565b5f60208284031215611781575f80fd5b5051919050565b6020808252600e908201526d6578636565642062616c616e636560901b604082015260600190565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610ac757610ac76117b0565b5f826117f557634e487b7160e01b5f52601260045260245ffd5b500490565b600181811c9082168061180e57607f821691505b60208210810361182c57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52604160045260245ffd5b60405160c081016001600160401b038111828210171561186857611868611832565b60405290565b604051601f8201601f191681016001600160401b038111828210171561189657611896611832565b604052919050565b80518015158114611468575f80fd5b5f60208083850312156118be575f80fd5b82516001600160401b03808211156118d4575f80fd5b818501915085601f8301126118e7575f80fd5b8151818111156118f9576118f9611832565b611907848260051b0161186e565b818152848101925060c0918202840185019188831115611925575f80fd5b938501935b8285101561199c5780858a031215611941575f8081fd5b611949611846565b8551611954816116f6565b8152858701518782015260408087015190820152606080870151908201526080808701519082015260a061198981880161189e565b908201528452938401939285019261192a565b50979650505050505050565b634e487b7160e01b5f52603260045260245ffd5b80820180821115610ac757610ac76117b0565b5f600182016119e0576119e06117b0565b5060010190565b6020808252600e908201526d3737ba1031b7b73a3937b63632b960911b604082015260600190565b81810381811115610ac757610ac76117b0565b6001600160a01b03929092168252602082015260400190565b6020808252600a90820152697a65726f2076616c756560b01b604082015260600190565b5f8251611a7081846020870161168e565b9190910192915050565b5f60208284031215611a8a575f80fd5b61063f8261189e56fea264697066735822122049366cc25e4f6913a1167a9fbaab3b5807cff3925b4b9dffdddf386dae32703d64736f6c63430008150033

Verified Source Code Full Match

Compiler: v0.8.21+commit.d9974bed EVM: shanghai Optimization: Yes (10 runs)
AssetsVault.sol 44 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";

contract AssetsVault {
    address public stoneVault;
    address public strategyController;

    modifier onlyPermit() {
        require(
            stoneVault == msg.sender || strategyController == msg.sender,
            "not permit"
        );
        _;
    }

    constructor(address _stoneVault, address _strategyController) {
        require(
            _stoneVault != address(0) && _strategyController != address(0),
            "ZERO ADDRESS"
        );
        stoneVault = _stoneVault;
        strategyController = _strategyController;
    }

    function deposit() external payable {
        require(msg.value != 0, "too small");
    }

    function withdraw(address _to, uint256 _amount) external onlyPermit {
        TransferHelper.safeTransferETH(_to, _amount);
    }

    function setNewVault(address _vault) external onlyPermit {
        stoneVault = _vault;
    }

    function getBalance() external view returns (uint256 amount) {
        amount = address(this).balance;
    }

    receive() external payable {}
}
IWBETH.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IWBETH {
    function deposit(address referral) external payable;

    function exchangeRate() external view returns (uint256 _exchangeRate);

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

    function requestWithdrawEth(uint256 wbethAmount) external;
}
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;
    }
}
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling 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);
    }
}
Strategy.sol 92 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {StrategyController} from "../strategies/StrategyController.sol";

abstract contract Strategy {
    address payable public immutable controller;

    address public governance;

    uint256 public latestUpdateTime;
    uint256 public bufferTime = 12;

    string public name;

    modifier onlyGovernance() {
        require(governance == msg.sender, "not governace");
        _;
    }

    modifier notAtSameBlock() {
        require(
            latestUpdateTime + bufferTime <= block.timestamp,
            "at the same block"
        );
        _;
    }

    event TransferGovernance(address oldOwner, address newOwner);

    constructor(address payable _controller, string memory _name) {
        require(_controller != address(0), "ZERO ADDRESS");

        governance = msg.sender;
        controller = _controller;
        name = _name;
    }

    modifier onlyController() {
        require(controller == msg.sender, "not controller");
        _;
    }

    function deposit() public payable virtual onlyController notAtSameBlock {}

    function withdraw(
        uint256 _amount
    )
        public
        virtual
        onlyController
        notAtSameBlock
        returns (uint256 actualAmount)
    {}

    function instantWithdraw(
        uint256 _amount
    )
        public
        virtual
        onlyController
        notAtSameBlock
        returns (uint256 actualAmount)
    {}

    function clear() public virtual onlyController returns (uint256 amount) {}

    function execPendingRequest(
        uint256 _amount
    ) public virtual returns (uint256 amount) {}

    function getAllValue() public virtual returns (uint256 value) {}

    function getPendingValue() public virtual returns (uint256 value) {}

    function getInvestedValue() public virtual returns (uint256 value) {}

    function checkPendingStatus()
        public
        virtual
        returns (uint256 pending, uint256 executable)
    {}

    function setGovernance(address governance_) external onlyGovernance {
        emit TransferGovernance(governance, governance_);
        governance = governance_;
    }

    function setBufferTime(uint256 _time) external onlyGovernance {
        bufferTime = _time;
    }
}
StrategyV2.sol 68 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {StrategyController} from "../strategies/StrategyController.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";

abstract contract StrategyV2 is Ownable2Step {
    address payable public immutable controller;

    uint256 public latestUpdateTime;
    uint256 public bufferTime;

    string public name;

    modifier notAtSameBlock() {
        require(
            latestUpdateTime + bufferTime <= block.timestamp,
            "at the same block"
        );
        _;
    }

    constructor(address payable _controller, string memory _name) {
        require(_controller != address(0), "ZERO ADDRESS");

        controller = _controller;
        name = _name;
    }

    modifier onlyController() {
        require(controller == msg.sender, "not controller");
        _;
    }

    function deposit() public payable virtual onlyController notAtSameBlock {}

    function withdraw(
        uint256 _amount
    )
        public
        virtual
        onlyController
        notAtSameBlock
        returns (uint256 actualAmount)
    {}

    function instantWithdraw(
        uint256 _amount
    )
        public
        virtual
        onlyController
        notAtSameBlock
        returns (uint256 actualAmount)
    {}

    function clear() public virtual onlyController returns (uint256 amount) {}

    function getAllValue() public virtual returns (uint256 value) {}

    function getPendingValue() public virtual returns (uint256 value) {}

    function getInvestedValue() public virtual returns (uint256 value) {}

    function setBufferTime(uint256 _time) external onlyOwner {
        bufferTime = _time;
    }
}
ICollateral.sol 11 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface ICollateral {
    function deposit(
        address recipient,
        uint256 amount
    ) external returns (uint256);

    function withdraw(address recipient, uint256 amount) external;
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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);
}
Ownable2Step.sol 57 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./Ownable.sol";

/**
 * @dev Contract module which provides 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} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }
}
IUnwrapTokenV1ETH.sol 40 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IUnwrapTokenV1ETH {
    struct WithdrawRequest {
        address recipient; // user who withdraw
        uint256 wbethAmount; //WBETH
        uint256 ethAmount; //ETH
        uint256 triggerTime; //user trigger time
        uint256 claimTime; //user claim time
        bool allocated; //is it allocated
    }

    function lockTime() external view returns (uint256);

    function getUserWithdrawRequests(
        address _recipient
    ) external view returns (WithdrawRequest[] memory);

    function getWithdrawRequests(
        uint256 _startIndex
    ) external view returns (WithdrawRequest[] memory);

    function claimWithdraw(uint256 _index) external;

    // testing
    function getNeedRechargeEthAmount() external view returns (uint256);

    function allocate(uint256 _maxAllocateNum) external returns (uint256);

    function rechargeFromRechargeAddress() external payable;

    function startAllocatedEthIndex() external view returns (uint256);

    function nextIndex() external view returns (uint256);

    function rechargeAddress() external view returns (address);

    function operatorAddress() external view returns (address);
}
StrategyController.sol 329 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";

import {Strategy} from "./Strategy.sol";
import {AssetsVault} from "../AssetsVault.sol";

contract StrategyController {
    using EnumerableSet for EnumerableSet.AddressSet;

    uint256 internal constant ONE_HUNDRED_PERCENT = 1e6;

    address public stoneVault;
    address payable public immutable assetsVault;

    EnumerableSet.AddressSet private strategies;

    mapping(address => uint256) public ratios;

    struct StrategyDiff {
        address strategy;
        bool isDeposit;
        uint256 amount;
    }

    modifier onlyVault() {
        require(stoneVault == msg.sender, "not vault");
        _;
    }

    constructor(
        address payable _assetsVault,
        address[] memory _strategies,
        uint256[] memory _ratios
    ) {
        require(_assetsVault != address(0), "ZERO ADDRESS");

        uint256 length = _strategies.length;
        for (uint256 i; i < length; i++) {
            require(_strategies[i] != address(0), "ZERO ADDRESS");
        }

        stoneVault = msg.sender;
        assetsVault = _assetsVault;

        _initStrategies(_strategies, _ratios);
    }

    function forceWithdraw(
        uint256 _amount
    ) external onlyVault returns (uint256 actualAmount) {
        uint256 balanceBeforeRepay = address(this).balance;

        if (balanceBeforeRepay >= _amount) {
            _repayToVault();

            actualAmount = _amount;
        } else {
            actualAmount =
                _forceWithdraw(_amount - balanceBeforeRepay) +
                balanceBeforeRepay;
        }
    }

    function setStrategies(
        address[] memory _strategies,
        uint256[] memory _ratios
    ) external onlyVault {
        _setStrategies(_strategies, _ratios);
    }

    function addStrategy(address _strategy) external onlyVault {
        require(!strategies.contains(_strategy), "already exist");

        strategies.add(_strategy);
    }

    function rebaseStrategies(
        uint256 _in,
        uint256 _out
    ) external payable onlyVault {
        _rebase(_in, _out);
    }

    function destroyStrategy(address _strategy) external onlyVault {
        _destoryStrategy(_strategy);
    }

    function _rebase(uint256 _in, uint256 _out) internal {
        require(_in == 0 || _out == 0, "only deposit or withdraw");

        if (_in != 0) {
            AssetsVault(assetsVault).withdraw(address(this), _in);
        }
        uint256 total = getAllStrategyValidValue();
        if (total < _out) {
            total = 0;
        } else {
            total = total + _in - _out;
        }

        uint256 length = strategies.length();
        StrategyDiff[] memory diffs = new StrategyDiff[](length);
        uint256 head;
        uint256 tail = length - 1;
        for (uint i; i < length; i++) {
            address strategy = strategies.at(i);
            if (ratios[strategy] == 0) {
                _clearStrategy(strategy, true);
                continue;
            }
            uint256 newPosition = (total * ratios[strategy]) /
                ONE_HUNDRED_PERCENT;
            uint256 position = getStrategyValidValue(strategy);

            if (newPosition < position) {
                diffs[head] = StrategyDiff(
                    strategy,
                    false,
                    position - newPosition
                );
                head++;
            } else if (newPosition > position) {
                diffs[tail] = StrategyDiff(
                    strategy,
                    true,
                    newPosition - position
                );
                if (tail != 0) {
                    tail--;
                }
            }
        }

        length = diffs.length;
        for (uint256 i; i < length; i++) {
            StrategyDiff memory diff = diffs[i];

            if (diff.amount == 0) {
                continue;
            }

            if (diff.isDeposit) {
                if (address(this).balance < diff.amount) {
                    diff.amount = address(this).balance;
                }
                _depositToStrategy(diff.strategy, diff.amount);
            } else {
                _withdrawFromStrategy(diff.strategy, diff.amount);
            }
        }

        _repayToVault();
    }

    function _repayToVault() internal {
        if (address(this).balance != 0) {
            TransferHelper.safeTransferETH(assetsVault, address(this).balance);
        }
    }

    function _depositToStrategy(address _strategy, uint256 _amount) internal {
        Strategy(_strategy).deposit{value: _amount}();
    }

    function _withdrawFromStrategy(
        address _strategy,
        uint256 _amount
    ) internal {
        Strategy(_strategy).withdraw(_amount);
    }

    function _forceWithdraw(
        uint256 _amount
    ) internal returns (uint256 actualAmount) {
        uint256 length = strategies.length();

        uint256 allRatios;
        for (uint i; i < length; i++) {
            address strategy = strategies.at(i);
            allRatios += ratios[strategy];
        }

        for (uint i; i < length; i++) {
            address strategy = strategies.at(i);

            uint256 withAmount = (_amount * ratios[strategy]) / allRatios;

            if (withAmount != 0) {
                actualAmount =
                    Strategy(strategy).instantWithdraw(withAmount) +
                    actualAmount;
            }
        }

        _repayToVault();
    }

    function getStrategyValue(
        address _strategy
    ) public returns (uint256 _value) {
        return Strategy(_strategy).getAllValue();
    }

    function getStrategyValidValue(
        address _strategy
    ) public returns (uint256 _value) {
        return Strategy(_strategy).getInvestedValue();
    }

    function getStrategyPendingValue(
        address _strategy
    ) public returns (uint256 _value) {
        return Strategy(_strategy).getPendingValue();
    }

    function getAllStrategiesValue() public returns (uint256 _value) {
        uint256 length = strategies.length();
        for (uint i; i < length; i++) {
            _value = _value + getStrategyValue(strategies.at(i));
        }
    }

    function getAllStrategyValidValue() public returns (uint256 _value) {
        uint256 length = strategies.length();
        for (uint i; i < length; i++) {
            _value = _value + getStrategyValidValue(strategies.at(i));
        }
    }

    function getAllStrategyPendingValue() public returns (uint256 _value) {
        uint256 length = strategies.length();
        for (uint i; i < length; i++) {
            _value = _value + getStrategyPendingValue(strategies.at(i));
        }
    }

    function getStrategies()
        public
        view
        returns (address[] memory addrs, uint256[] memory portions)
    {
        uint256 length = strategies.length();

        addrs = new address[](length);
        portions = new uint256[](length);

        for (uint256 i; i < length; i++) {
            address addr = strategies.at(i);
            addrs[i] = addr;
            portions[i] = ratios[addr];
        }
    }

    function _initStrategies(
        address[] memory _strategies,
        uint256[] memory _ratios
    ) internal {
        require(_strategies.length == _ratios.length, "invalid length");

        uint256 totalRatio;
        uint256 length = _strategies.length;
        for (uint i; i < length; i++) {
            strategies.add(_strategies[i]);
            ratios[_strategies[i]] = _ratios[i];
            totalRatio = totalRatio + _ratios[i];
        }
        require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
    }

    function _setStrategies(
        address[] memory _strategies,
        uint256[] memory _ratios
    ) internal {
        uint256 length = _strategies.length;
        require(length == _ratios.length, "invalid length");

        uint256 oldLength = strategies.length();
        for (uint i; i < oldLength; i++) {
            ratios[strategies.at(i)] = 0;
        }
        uint256 totalRatio;
        for (uint i; i < length; i++) {
            require(
                Strategy(_strategies[i]).controller() == address(this),
                "controller mismatch"
            );
            strategies.add(_strategies[i]);
            ratios[_strategies[i]] = _ratios[i];
            totalRatio = totalRatio + _ratios[i];
        }
        require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
    }

    function clearStrategy(address _strategy) public onlyVault {
        _clearStrategy(_strategy, false);
    }

    function _clearStrategy(address _strategy, bool _isRebase) internal {
        Strategy(_strategy).clear();

        if (!_isRebase) {
            _repayToVault();
        }
    }

    function _destoryStrategy(address _strategy) internal {
        require(_couldDestroyStrategy(_strategy), "still active");

        strategies.remove(_strategy);

        _repayToVault();
    }

    function _couldDestroyStrategy(
        address _strategy
    ) internal returns (bool status) {
        return
            ratios[_strategy] == 0 && Strategy(_strategy).getAllValue() < 1e4;
    }

    function setNewVault(address _vault) external onlyVault {
        stoneVault = _vault;
    }

    receive() external payable {}
}
EnumerableSet.sol 378 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}
TransferHelper.sol 60 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;

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

library TransferHelper {
    /// @notice Transfers tokens from the targeted address to the given destination
    /// @notice Errors with 'STF' if transfer fails
    /// @param token The contract address of the token to be transferred
    /// @param from The originating address from which the tokens will be transferred
    /// @param to The destination address of the transfer
    /// @param value The amount to be transferred
    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
    }

    /// @notice Transfers tokens from msg.sender to a recipient
    /// @dev Errors with ST if transfer fails
    /// @param token The contract address of the token which will be transferred
    /// @param to The recipient of the transfer
    /// @param value The value of the transfer
    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
    }

    /// @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 {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
    }

    /// @notice Transfers ETH to the recipient address
    /// @dev Fails with `STE`
    /// @param to The destination of the transfer
    /// @param value The value to be transferred
    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, 'STE');
    }
}
SymbioticDepositWBETHStrategy.sol 216 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {StrategyV2} from "../strategies/StrategyV2.sol";
import {StrategyController} from "../strategies/StrategyController.sol";
import {IWBETH} from "../interfaces/IWBETH.sol";
import {IUnwrapTokenV1ETH} from "../interfaces/IUnwrapTokenV1ETH.sol";
import {ICollateral} from "../interfaces/ICollateral.sol";

contract SymbioticDepositWBETHStrategy is StrategyV2 {
    address public immutable wbETHAddr;
    address public immutable unwrapTokenV1ETHAddr;
    address public immutable collateralAddr;

    uint256 internal immutable MULTIPLIER = 1e18;

    event WrapToWBETH(uint256 etherAmount, uint256 wbETHAmount);
    event DepositIntoSymbiotic(
        address indexed collateral,
        address indexed recipient,
        uint256 amount,
        uint256 share
    );
    event WithdrawFromSymbiotic(
        address indexed collateral,
        address indexed recipient,
        uint256 share,
        uint256 amount
    );

    constructor(
        address payable _controller,
        address _wbETHAddr,
        address _unwrapTokenV1ETHAddr,
        address _collateralAddr,
        string memory _name
    ) StrategyV2(_controller, _name) {
        wbETHAddr = _wbETHAddr;
        unwrapTokenV1ETHAddr = _unwrapTokenV1ETHAddr;
        collateralAddr = _collateralAddr;
    }

    function deposit() public payable override onlyController notAtSameBlock {
        require(msg.value != 0, "zero value");

        latestUpdateTime = block.timestamp;
    }

    function withdraw(
        uint256 _amount
    ) public override onlyController returns (uint256 actualAmount) {
        actualAmount = _withdraw(_amount);
    }

    function instantWithdraw(
        uint256 _amount
    ) public override onlyController returns (uint256 actualAmount) {
        actualAmount = _withdraw(_amount);
    }

    function clear() public override onlyController returns (uint256 amount) {
        uint256 balance = address(this).balance;

        if (balance != 0) {
            TransferHelper.safeTransferETH(controller, balance);
            amount = balance;
        }
    }

    function _withdraw(
        uint256 _amount
    ) internal returns (uint256 actualAmount) {
        require(_amount != 0, "zero value");

        actualAmount = _amount;

        TransferHelper.safeTransferETH(controller, actualAmount);
    }

    function getAllValue() public override returns (uint256 value) {
        value = getInvestedValue();
    }

    function getInvestedValue() public override returns (uint256 value) {
        uint256 etherValue = address(this).balance;
        (uint256 claimableValue, uint256 pendingValue) = checkPendingAssets();

        value = etherValue + claimableValue + pendingValue + getWBETHValue();
    }

    function getWBETHValue() public view returns (uint256 value) {
        uint256 wbBalance = IERC20(wbETHAddr).balanceOf(address(this));
        uint256 depositValue = IERC20(collateralAddr).balanceOf(address(this));

        value =
            ((wbBalance + depositValue) * IWBETH(wbETHAddr).exchangeRate()) /
            MULTIPLIER;
    }

    function wrapToWBETH(
        uint256 _ethAmount,
        address _referral
    ) external onlyOwner returns (uint256 amount) {
        require(_ethAmount != 0, "zero");
        require(_ethAmount <= address(this).balance, "exceed balance");

        IWBETH wbETH = IWBETH(wbETHAddr);

        uint256 balanceBefore = wbETH.balanceOf(address(this));

        wbETH.deposit{value: _ethAmount}(_referral);

        amount = wbETH.balanceOf(address(this)) - balanceBefore;

        emit WrapToWBETH(_ethAmount, amount);
    }

    function depositIntoSymbiotic(
        uint256 _wbETHAmount
    ) external onlyOwner returns (uint256 shares) {
        require(_wbETHAmount != 0, "zero");

        TransferHelper.safeApprove(wbETHAddr, collateralAddr, _wbETHAmount);

        shares = ICollateral(collateralAddr).deposit(
            address(this),
            _wbETHAmount
        );

        require(shares != 0, "mint zero share");

        emit DepositIntoSymbiotic(
            collateralAddr,
            address(this),
            _wbETHAmount,
            shares
        );
    }

    function withdrawFromSymbiotic(
        uint256 _share
    ) external onlyOwner returns (uint256 wbETHAmount) {
        require(_share != 0, "zero");

        wbETHAmount = IWBETH(wbETHAddr).balanceOf(address(this));

        ICollateral(collateralAddr).withdraw(address(this), _share);

        wbETHAmount = IWBETH(wbETHAddr).balanceOf(address(this)) - wbETHAmount;

        emit WithdrawFromSymbiotic(
            collateralAddr,
            address(this),
            _share,
            wbETHAmount
        );
    }

    function requestToEther(
        uint256 _amount
    ) external onlyOwner returns (uint256 etherAmount) {
        IWBETH wbETH = IWBETH(wbETHAddr);

        require(_amount != 0, "zero");
        require(_amount <= wbETH.balanceOf(address(this)), "exceed balance");

        wbETH.requestWithdrawEth(_amount);

        etherAmount = (_amount * IWBETH(wbETHAddr).exchangeRate()) / MULTIPLIER;
    }

    function claimPendingAssets(uint256 _index) external onlyOwner {
        IUnwrapTokenV1ETH(unwrapTokenV1ETHAddr).claimWithdraw(_index);
    }

    function checkPendingAssets()
        public
        view
        returns (uint256 totalClaimable, uint256 totalPending)
    {
        IUnwrapTokenV1ETH unwrapTokenV1ETH = IUnwrapTokenV1ETH(
            unwrapTokenV1ETHAddr
        );

        IUnwrapTokenV1ETH.WithdrawRequest[]
            memory allRequests = unwrapTokenV1ETH.getUserWithdrawRequests(
                address(this)
            );

        uint256 length = allRequests.length;
        if (length == 0) {
            return (0, 0);
        }

        uint256 lockTime = unwrapTokenV1ETH.lockTime();
        for (uint256 i; i < length; i++) {
            IUnwrapTokenV1ETH.WithdrawRequest memory request = allRequests[i];
            if (request.claimTime != 0) {
                continue;
            }
            if (
                block.timestamp >= request.triggerTime + lockTime &&
                request.allocated
            ) {
                totalClaimable = totalClaimable + request.ethAmount;
            } else {
                totalPending = totalPending + request.ethAmount;
            }
        }
    }

    receive() external payable {}
}

Read Contract

bufferTime 0xba7b6703 → uint256
checkPendingAssets 0x1c2ca2fd → uint256, uint256
collateralAddr 0xd4532dfd → address
controller 0xf77c4791 → address
getWBETHValue 0x1f660123 → uint256
latestUpdateTime 0xdd07d288 → uint256
name 0x06fdde03 → string
owner 0x8da5cb5b → address
pendingOwner 0xe30c3978 → address
unwrapTokenV1ETHAddr 0x9d80349b → address
wbETHAddr 0x33ba0957 → address

Write Contract 16 functions

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

acceptOwnership 0x79ba5097
No parameters
claimPendingAssets 0x46163299
uint256 _index
clear 0x52efea6e
No parameters
returns: uint256
deposit 0xd0e30db0
No parameters
depositIntoSymbiotic 0xa4ba0b10
uint256 _wbETHAmount
returns: uint256
getAllValue 0x7654f7ab
No parameters
returns: uint256
getInvestedValue 0x6c23ab4c
No parameters
returns: uint256
getPendingValue 0x001a5597
No parameters
returns: uint256
instantWithdraw 0xfd92bff2
uint256 _amount
returns: uint256
renounceOwnership 0x715018a6
No parameters
requestToEther 0x01a4c2f8
uint256 _amount
returns: uint256
setBufferTime 0x1aac030c
uint256 _time
transferOwnership 0xf2fde38b
address newOwner
withdraw 0x2e1a7d4d
uint256 _amount
returns: uint256
withdrawFromSymbiotic 0xb4027ea8
uint256 _share
returns: uint256
wrapToWBETH 0x5f6550d5
uint256 _ethAmount
address _referral
returns: uint256

Recent Transactions

No transactions found for this address