xDAI Price: $0.999836 (-0.02%)
Gas: 1 GWei

Contract

0xc4969183c6C2cAC80628cBBCA7d929a77818AbE0

Overview

xDAI Balance

Gnosis Chain LogoGnosis Chain LogoGnosis Chain Logo0 xDAI

xDAI Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Threshold Wi...391672812025-03-22 14:13:4532 days ago1742652825IN
0xc4969183...77818AbE0
0 xDAI0.000066691.0000001
Set Threshold Wi...372691082024-11-29 12:04:30145 days ago1732881870IN
0xc4969183...77818AbE0
0 xDAI0.000100031.5
Set Threshold Wi...364828062024-10-13 10:35:05192 days ago1728815705IN
0xc4969183...77818AbE0
0 xDAI0.000133382
Set Threshold Wi...362518272024-09-29 16:39:45205 days ago1727627985IN
0xc4969183...77818AbE0
0 xDAI0.000080031.20000001
Set Threshold Wi...362288292024-09-28 7:47:50207 days ago1727509670IN
0xc4969183...77818AbE0
0 xDAI0.000100551.2
Set Threshold Wi...362249032024-09-28 2:11:30207 days ago1727489490IN
0xc4969183...77818AbE0
0 xDAI0.000124852.02
Set Threshold Wi...362130822024-09-27 9:13:55208 days ago1727428435IN
0xc4969183...77818AbE0
0 xDAI0.000217891.50000001

Latest 1 internal transaction

Parent Transaction Hash Block From To
362073782024-09-27 0:53:20208 days ago1727398400  Contract Creation0 xDAI
Loading...
Loading

Minimal Proxy Contract for 0x1a2dee43601372abb499046ab531cae3f187fb6d

Contract Name:
Coordinator

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 7 : Coordinator.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Initializable} from "@openzeppelin/proxy/utils/Initializable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IAccount} from "../interfaces/IAccount.sol";
import {ICoordinator} from "../interfaces/ICoordinator.sol";
import {SafeTransfer} from "./SafeTransfer.sol";

library CoordinatorError {
    error OnlyOwner();
    error ZeroAddress();
    error InvalidOwner();
    error InvalidRecipientToken();
    error MaxAccountsReached();
    error NoAccounts();
    error AccountNotFound();
    error IncorrectAccountCount();
    error InvalidWithdrawArguments();
}

interface IAccountFactory {
    function deployContract(address _investToken, address _coordinator, uint256 _dustAmount)
        external
        returns (address accountProxyAddress);
}

contract Coordinator is ReentrancyGuard, Initializable, ICoordinator {
    // ################### Constants and Storage ###################

    /// @inheritdoc ICoordinator
    uint256 public constant MAX_ACCOUNTS = 8;

    /// @inheritdoc ICoordinator
    uint256 public accountCount;

    /// @inheritdoc ICoordinator
    uint256 public threshold;

    /// @notice List of accounts in this coordinator
    /// @dev The list includes the number of both rebalance and no rebalance accounts
    address[MAX_ACCOUNTS] internal accounts;

    /// @inheritdoc ICoordinator
    mapping(address => bool) public isRebalanceAccount;

    /// @inheritdoc ICoordinator
    address public recipientToken;

    /// @inheritdoc ICoordinator
    address public recipient;

    /// @inheritdoc ICoordinator
    address public owner;

    /// @inheritdoc ICoordinator
    address public immutable accountFactory;

    modifier onlyOwner() {
        if (msg.sender != owner) revert CoordinatorError.OnlyOwner();
        _;
        /// Continue executing the function code here
    }

    // ################### Events ###################
    event CoordinatorInitialized(address owner, uint256 threshold, address recipient, address recipientToken);
    event ThresholdUpdated(uint256 newThreshold);
    event AccountRemoved(address account, uint256 accountCount);
    event AccountStatusUpdated(address account, bool status);
    event AccountOrderUpdated(address[] accounts);
    event NewRecipient(address recipient, address recipientToken);

    // ################### Constructor ###################

    constructor(address _accountFactory) {
        accountFactory = _accountFactory;
    }

    // ################### Initializers ###################

    /// @inheritdoc ICoordinator
    function initialize(
        address _owner,
        uint256 _threshold,
        address _recipient,
        address _recipientToken,
        address _investmentToken,
        uint256 _dustAmount,
        bool _isRebalanceAccount
    ) external initializer {
        _initialize(_owner, _threshold, _recipient, _recipientToken);
        address accountProxyAddress = _deployAccount(_investmentToken, _dustAmount);
        if (_isRebalanceAccount) {
            _setAccountStatus(accountProxyAddress, true);
        }
    }

    function _initialize(address _owner, uint256 _threshold, address _recipient, address _recipientToken) internal {
        if (_owner == address(0)) {
            revert CoordinatorError.InvalidOwner();
        }
        owner = _owner;

        if (_recipient != address(0)) {
            if (_recipientToken == address(0)) {
                revert CoordinatorError.InvalidRecipientToken();
            }
            recipient = _recipient;
            recipientToken = _recipientToken;
        }

        if (_threshold > 0) {
            threshold = _threshold;
        }

        emit CoordinatorInitialized(_owner, _threshold, _recipient, _recipientToken);
    }

    // ################### View/Getter functions ###################

    /// @inheritdoc ICoordinator
    function getAccounts() external view returns (address[] memory _accounts) {
        _accounts = new address[](accountCount);
        for (uint256 i = 0; i < accountCount; i++) {
            _accounts[i] = accounts[i];
        }
    }

    /// @inheritdoc ICoordinator
    function getAccountInfo() external view returns (AccountInfo[] memory) {
        address[MAX_ACCOUNTS] memory _accounts = accounts;
        AccountInfo[] memory accountInfo = new AccountInfo[](accountCount);

        for (uint256 i; i < accountCount; i++) {
            accountInfo[i] = AccountInfo({
                account: _accounts[i],
                status: isRebalanceAccount[_accounts[i]],
                asset: _getAccountToken(_accounts[i]),
                balance: _getAccountAssets(_accounts[i])
            });
        }

        return accountInfo;
    }

    /// @inheritdoc ICoordinator
    function getAccountTokens() public view returns (address[] memory) {
        address[MAX_ACCOUNTS] memory _accounts = accounts;
        address[] memory assets = new address[](accountCount);

        for (uint256 i; i < accountCount; i++) {
            assets[i] = _getAccountToken(_accounts[i]);
        }

        return assets;
    }

    function _getAccountToken(address _account) internal view returns (address) {
        return IAccount(_account).investmentToken();
    }

    /// @inheritdoc ICoordinator
    function getAccountAssets() public view returns (uint256[] memory) {
        address[MAX_ACCOUNTS] memory _accounts = accounts;
        uint256[] memory assets = new uint256[](accountCount);

        for (uint256 i; i < accountCount; i++) {
            assets[i] = _getAccountAssets(_accounts[i]);
        }

        return assets;
    }

    function _getAccountAssets(address _account) internal view returns (uint256) {
        return IAccount(_account).investmentBalance();
    }

    /// @inheritdoc ICoordinator
    function amountToRebalance() public view returns (uint256) {
        uint256 _threshold = threshold;
        uint256 _currentBalance = IERC20(recipientToken).balanceOf(recipient);

        return (_threshold > _currentBalance) ? _threshold - _currentBalance : 0;
    }

    /// @inheritdoc ICoordinator
    function rebalanceTrigger() public view returns (address) {
        address _recipient = recipient;

        if (_recipient == address(0)) {
            return address(0);
        }

        if (IERC20(recipientToken).balanceOf(_recipient) >= threshold) {
            return address(0);
        }

        for (uint256 i; i < accountCount; i++) {
            address _account = accounts[i];
            if (!isRebalanceAccount[_account]) {
                continue;
            }
            if (IAccount(_account).rebalanceTrigger()) {
                return _account;
            }
        }
        return address(0);
    }

    // ################### Setter functions ###################

    /// @inheritdoc ICoordinator
    function setRecipient(address _recipient, address _recipientToken) public onlyOwner {
        if (recipient == _recipient && recipientToken == _recipientToken) {
            return;
        }
        if (_recipient == address(0)) {
            if (_recipientToken != address(0)) {
                revert CoordinatorError.InvalidRecipientToken();
            }
        } else {
            if (_recipientToken == address(0)) {
                revert CoordinatorError.InvalidRecipientToken();
            }
        }
        recipient = _recipient;
        recipientToken = _recipientToken;

        emit NewRecipient(_recipient, _recipientToken);
    }

    /// @inheritdoc ICoordinator
    function setThreshold(uint256 _threshold) public onlyOwner {
        threshold = _threshold;
        emit ThresholdUpdated(_threshold);
    }

    /// @inheritdoc ICoordinator
    function setAccountOrder(address[] memory _accounts) public onlyOwner {
        if (_accounts.length != accountCount) {
            revert CoordinatorError.IncorrectAccountCount();
        }
        _checkAccounts(_accounts);

        for (uint256 i = 0; i < _accounts.length; i++) {
            accounts[i] = _accounts[i];
        }

        emit AccountOrderUpdated(_accounts);
    }

    function _buildQueue(address[] memory _queue) internal view returns (address[] memory) {
        uint256 _accountCount = accountCount;
        if (_queue.length == 0) {
            return new address[](0);
        } else if (_queue.length == _accountCount) {
            return _queue;
        }
        address[MAX_ACCOUNTS] memory _accounts = accounts;
        address[] memory newQueue = new address[](_accountCount);
        for (uint256 i = 0; i < _queue.length; i++) {
            newQueue[i] = _queue[i];
        }
        uint256 queueLength = _queue.length;
        for (uint256 i = 0; i < _accountCount; i++) {
            for (uint256 j = 0; j < _queue.length; j++) {
                if (_accounts[i] == _queue[j]) {
                    break;
                } else if (j == _queue.length - 1) {
                    newQueue[queueLength] = _accounts[i];
                    queueLength += 1;
                }
            }
        }
        return newQueue;
    }

    function _checkAccounts(address[] memory _accountsToCheck) internal view {
        address[MAX_ACCOUNTS] memory _accounts = accounts;
        uint256 _accountCount = accountCount;
        for (uint256 i = 0; i < _accountsToCheck.length; i++) {
            if (_accountsToCheck[i] == address(0)) {
                revert CoordinatorError.ZeroAddress();
            }
            for (uint256 j = 0; j < _accountCount; j++) {
                if (_accountsToCheck[i] == _accounts[j]) {
                    break;
                } else if (j == _accountCount - 1) {
                    revert CoordinatorError.AccountNotFound();
                }
            }
        }
    }

    // ################### Account Logic ###################

    /// @inheritdoc ICoordinator
    function addAccount(address _investmentToken, uint256 _dustAmount)
        external
        onlyOwner
        returns (address newAccount)
    {
        if (accountCount >= MAX_ACCOUNTS) {
            revert CoordinatorError.MaxAccountsReached();
        }
        newAccount = _deployAccount(_investmentToken, _dustAmount);
    }

    function _deployAccount(address _investmentToken, uint256 _dustAmount)
        internal
        returns (address accountProxyAddress)
    {
        uint256 _accountCount = accountCount;
        if (_investmentToken == address(0)) {
            revert CoordinatorError.ZeroAddress();
        }

        accountProxyAddress =
            IAccountFactory(accountFactory).deployContract(_investmentToken, address(this), _dustAmount);
        accounts[_accountCount] = accountProxyAddress;
        accountCount = _accountCount + 1;
    }

    /// @inheritdoc ICoordinator
    function removeAccount(address _account) external onlyOwner {
        uint256 _accountCount = accountCount;

        if (_accountCount == 0) {
            revert CoordinatorError.NoAccounts();
        }

        if (_account == address(0)) {
            revert CoordinatorError.ZeroAddress();
        }

        address[MAX_ACCOUNTS] memory _accounts = accounts;
        bool _found = false;

        for (uint256 i = 0; i < _accountCount; i++) {
            if (_accounts[i] == _account) {
                _found = true;

                if (i == MAX_ACCOUNTS - 1) {
                    _accounts[i] = address(0);
                } else {
                    _accounts[i] = _accounts[i + 1];
                }

                for (uint256 j = i + 1; j < _accountCount; j++) {
                    if (accounts[j] == address(0)) {
                        break;
                    }
                    _accounts[j] = _accounts[j + 1];
                }
                accounts = _accounts;
                break;
            }
        }
        if (!_found) {
            revert CoordinatorError.AccountNotFound();
        }

        _setAccountStatus(_account, false);
        accountCount -= 1;

        emit AccountRemoved(_account, accountCount);
    }

    /// @inheritdoc ICoordinator
    function setAccountStatus(address[] memory _accounts, bool[] memory _status) public onlyOwner {
        if (_accounts.length != _status.length || _accounts.length == 0) {
            revert CoordinatorError.IncorrectAccountCount();
        }
        if (_accounts.length == accountCount) {
            setAccountOrder(_accounts);
        } else {
            setAccountOrder(_buildQueue(_accounts));
        }
        for (uint256 i = 0; i < _accounts.length; i++) {
            _setAccountStatus(_accounts[i], _status[i]);
        }
    }

    function _setAccountStatus(address _account, bool _status) internal {
        isRebalanceAccount[_account] = _status;
        emit AccountStatusUpdated(_account, _status);
    }

    // ################### Withdraw functions ###################

    /// @inheritdoc ICoordinator
    function withdraw(address[] memory _accounts, uint256[] memory _amounts) external onlyOwner {
        if (_accounts.length != _amounts.length) {
            revert CoordinatorError.InvalidWithdrawArguments();
        }

        _checkAccounts(_accounts);
        for (uint256 i = 0; i < _accounts.length; i++) {
            IAccount(_accounts[i]).withdrawInvestment(_amounts[i]);
        }
    }

    /// @inheritdoc ICoordinator
    function sweep(address[] calldata _tokens, uint256[] calldata _amounts) external onlyOwner {
        address _owner = owner;

        if (_tokens.length != _amounts.length) {
            revert CoordinatorError.InvalidWithdrawArguments();
        }

        for (uint256 i = 0; i < _tokens.length; i++) {
            if (_tokens[i] == address(0)) {
                SafeTransfer._safeTransferETH(_owner, _amounts[i]);
            } else {
                SafeTransfer._safeTransfer(_tokens[i], _owner, _amounts[i]);
            }
        }
    }

    // ################### Helper functions ###################

    /// @inheritdoc ICoordinator
    function setThresholdWithRecipient(
        address _recipient,
        address _recipientToken,
        address[] memory _rebalancers,
        uint256 _threshold
    ) external onlyOwner {
        setRecipient(_recipient, _recipientToken);
        setThreshold(_threshold);
        address[] memory newQueue = _buildQueue(_rebalancers);
        bool[] memory status = new bool[](newQueue.length);
        for (uint256 i = 0; i < newQueue.length; i++) {
            if (i < _rebalancers.length) {
                status[i] = true;
            } else {
                status[i] = false;
            }
        }
        setAccountStatus(newQueue, status);
    }
}

File 2 of 7 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

File 3 of 7 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

File 4 of 7 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 5 of 7 : IAccount.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.25;

/// @title Account
/// @notice A contract for managing an investment token position from an account coordinator
/// (another SC). The coordinator deploys and keeps track of underlying accounts that leverage
/// cowswap to swap account's investment token (some yield baring asset, e.g. sDai) into a
/// specified token (e.g., Monero's EURe), typically used by the gnosis card safe account (GCSA).
/// Not all accounts need to be designated as `rebalance` accounts, but only `rebalance`
/// accounts are used to top up the recipient account, which is assumed to be a GCSA, when the
/// balance falls below a certain threshold.
/// @dev Mostly built with smart contract wallet (SCW) and ERC-4337 in mind.
interface IAccount {
    /// @notice Retrieves the address of the investment token managed by this account.
    /// @dev sDAI or other similar yield-bearing token, for example. The contract does not
    /// restrict the token the user may want to use as investment token.
    function investmentToken() external view returns (address);

    /// @notice Retrieves the address of the account owner, which is also the coordinator owner.
    function owner() external view returns (address);

    /// @notice Retrieves the coordinator address.
    function coordinator() external view returns (address);

    /// @notice Retrieves the account factory address.
    function factory() external view returns (address);

    /// @notice Retrieves the recipient target for rebalancing.
    /// @dev Typically, this is the Gnosis Card Safe Account.
    function recipient() external view returns (address);

    /// @notice Retrieves the address of the card account token.
    /// @dev It's the same as the coordinator's `recipientToken`.
    function cardToken() external view returns (address);

    /// @notice Retrieves the rebalance status of the account.
    /// @dev True if the investment token balance is above the dust threshold.
    /// Used by the coordinator to determine what investment account should be used
    /// in the top up process.
    function rebalanceTrigger() external view returns (bool);

    /// @notice Retrieves the balance of the investment token in the account.
    function investmentBalance() external view returns (uint256);

    /// @notice Retrieves the dust amount threshold.
    /// @dev Denominated in investment token. Relevant to rebalance accounts only. At the time
    /// of rebalancing, Cowswap's competitive batch auction process makes it possible to potentially
    /// spend less investment token to top up the card token. It's then unlikely that the investment
    /// account balance will ever be 0 during a top up. `dustAmount` helps you set the balance
    /// threshold at which you want the rebalance process to look at the next investment account
    /// in the queue that has been flagged for rebalance.
    function dustAmount() external view returns (uint256);

    /// @notice Set the dust amount threshold.
    /// @param _dustAmount The dust threshold denominated in investment token.
    function setDustAmount(uint256 _dustAmount) external;

    /// @notice Sends the specified investment token balance to the account owner.
    /// @dev This function can only be called by the account coordinator contract.
    function withdrawInvestment(uint256) external;

    /// @notice Rebalance order pre-hook that ensures that only one order is settled in a block.
    /// @dev Transactions with a counter greater than 1 will revert. The function can only be called
    /// by cowswap's trampoline contract, which is in charge of executing hooks
    function increaseOrderCount() external;

    /// @notice Validates whether a rebalance order is authorized to settle on behalf of the account.
    /// @param _orderHash The hash of the `GPv2Order.Data` struct.
    /// @param _signature The encoded `GPv2Order.Data` struct.
    /// @return The bytes4 magic value 0x1626ba7e when the signature is valid.
    function isValidSignature(
        bytes32 _orderHash,
        bytes calldata _signature
    ) external view returns (bytes4);

    /// @notice Retrieves the hash of the metadata used in rebalancing trades.
    /// @dev The metadata, among other things, include the pre-hooks used during
    /// trade execution that guarantees that only one relance order is settled per block.
    /// AppData is unique to each account smart contract.
    function appData() external view returns (bytes32);

    /// @notice Initializes the contract
    /// @param _investmentToken Address of investment token
    /// @param _coordinator Address of the coordinator
    /// @param _dustAmount Dust amount threshold denominated in investment token
    /// @param _appData Hash of the appData struct used by cowswap
    function initialize(
        address _investmentToken,
        address _coordinator,
        uint256 _dustAmount,
        bytes32 _appData
    ) external;

    /// @notice Sweeps specified amounts of tokens to the owner
    /// @param _tokens Array of token addresses to sweep
    /// @param _amounts Array of amounts to sweep for each token
    function sweep(
        address[] calldata _tokens,
        uint256[] calldata _amounts
    ) external;
}

File 6 of 7 : ICoordinator.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.25;

/// @title Coordinator
/// @notice A contract for managing multiple investment accounts and rebalancing funds to a
/// gnosis card safe account (GCSA). The account coordinator deploys and keeps track of underlying
/// accounts that leverage cowswap to swap specified investment tokens (some yield baring
/// asset, e.g. sDai) into a specified token used by the GCSA (e.g., Monero's EURe).
/// Not all underlying accounts need to be designated as `rebalance` accounts, but only `rebalance`
/// accounts are used to top up the recipient account, which is assumed to be a GCSA, when the
/// balance falls below a certain threshold. The account coordinator can also be used to withdraw
/// funds from the underlying accounts.
/// @dev Mostly built with smart contract wallet (SCW) and ERC-4337 in mind.
interface ICoordinator {
    struct AccountInfo {
        address account;
        bool status;
        address asset;
        uint256 balance;
    }

    /// @notice Initializes the contract and deploys an account
    /// @param _owner Address of the account owner
    /// @param _threshold Rebalancing threshold
    /// @param _recipient Address to receive rebalanced funds
    /// @param _recipientToken Token to be rebalanced
    /// @param _investmentToken Token for the deployed account
    /// @param _dustAmount The dust threshold denominated in investment token.
    /// @param _isRebalanceAccount Whether the deployed account should be a rebalance account
    function initialize(
        address _owner,
        uint256 _threshold,
        address _recipient,
        address _recipientToken,
        address _investmentToken,
        uint256 _dustAmount,
        bool _isRebalanceAccount
    ) external;

    /// @notice Retrieves account factory address
    function accountFactory() external view returns (address);

    /// @notice Retrieves the recipient target for rebalancing
    /// @dev Typically, this is the Gnosis Card Safe Account
    function recipient() external view returns (address);

    /// @notice Retrieves the target token for rebalancing
    function recipientToken() external view returns (address);

    /// @notice Retrieves the account owner
    function owner() external view returns (address);

    /// @notice Determines if an account is registered as a rebalance account
    function isRebalanceAccount(address _account) external view returns (bool);

    /// @notice Retrieves all registered accounts
    /// @return An array of account addresses
    function getAccounts() external view returns (address[] memory);

    /// @notice Retrieves detailed information about all accounts
    /// @return An array of AccountInfo structs, which includes the address, the
    /// rebalance status, the token held and its balance
    function getAccountInfo() external view returns (AccountInfo[] memory);

    /// @notice Retrieves a list of all investment tokens managed by the coordinator
    function getAccountTokens() external view returns (address[] memory);

    /// @notice Retrieves the investment token balance held in each account
    function getAccountAssets() external view returns (uint256[] memory);

    /// @notice Retrieves the maximum number of accounts allowed in this coordinator
    /// @dev This number cannot be updated
    function MAX_ACCOUNTS() external view returns (uint256);

    /// @notice Retrieves the number of accounts managed by this coordinator
    /// @dev This includes the number of both rebalance and no rebalance accounts
    function accountCount() external view returns (uint256);

    /// @notice Retrieves the threshold used for triggering a rebalance
    /// @dev The threshold is denominated in `recipientToken` units
    function threshold() external view returns (uint256);

    /// @notice Calculates the amount needed to rebalance
    /// @dev The amount to rebalance is the difference between the GCSA's balance and the
    /// specified threshold
    /// @return The amount to rebalance
    function amountToRebalance() external view returns (uint256);

    /// @notice Checks if rebalancing is needed and returns the triggering account
    /// @dev
    /// @return Address of the account triggering the rebalance, or zero address if not needed
    function rebalanceTrigger() external view returns (address);

    /// @notice Sets a new recipient and token for rebalancing
    /// @param _recipient New recipient address
    /// @param _recipientToken New recipient token address
    function setRecipient(address _recipient, address _recipientToken) external;

    /// @notice Sets the order of the accounts
    /// @param _accounts Array of account addresses
    /// @dev This is only relevant for the order of rebalancing accounts,
    /// the order determines the sequence in which the accounts have
    /// their funds pulled during the top up.
    function setAccountOrder(address[] memory _accounts) external;

    /// @notice Updates the rebalancing threshold
    /// @dev The threshold should be denominated in `recipientToken` units
    /// @param _threshold New threshold value
    function setThreshold(uint256 _threshold) external;

    /// @notice Adds a new account
    /// @dev At the time of rebalancing, Cowswap's competitive batch auction process makes it
    /// possible to potentially spend less investment tokent to top up the card token. It's
    /// then unlikely that the investment account balance will ever be 0 during a top up.
    /// `_dustAmount` helps you set the balance threshold (denominated in investment token)
    /// at which you want the rebalance process to look at the next investment account in the
    /// queue that has been flagged for rebalance (see `setAccountStatus()`)
    /// @param _investmentToken Token for the new account
    /// @return newAccount Address of the new account
    function addAccount(
        address _investmentToken,
        uint256 _dustAmount
    ) external returns (address newAccount);

    /// @notice Removes a account
    /// @param _account Address of the account to remove
    function removeAccount(address _account) external;

    /// @notice Sets the rebalance status of an account
    /// @dev If True, the account will be used during the top up process following the order
    /// of accounts specified in `getAccounts()`
    /// @param _account Address of the account
    /// @param _status New rebalance status (true for active, false for inactive)
    function setAccountStatus(
        address[] calldata _account,
        bool[] calldata _status
    ) external;

    /// @notice Withdraws funds from the underlying accounts
    /// @param _accounts List of accounts to withdraw from
    /// @param _amounts Amounts to be withdrawn
    /// @dev Account coordinator calls the withdrawInvestment function on the underlying
    /// accounts. The target investment token is sent to the owner
    function withdraw(
        address[] calldata _accounts,
        uint256[] calldata _amounts
    ) external;

    /// @notice Sweeps specified amounts of tokens to the owner
    /// @param _tokens Array of token addresses to sweep
    /// @param _amounts Array of amounts to sweep for each token
    function sweep(
        address[] calldata _tokens,
        uint256[] calldata _amounts
    ) external;

    /// @notice Sets threshold, recipient, and rebalancer accounts in one transaction
    /// @param _recipient New recipient address
    /// @param _recipientToken New recipient token address
    /// @param _rebalancers Array of rebalancing accounts
    /// @param _threshold New threshold value
    function setThresholdWithRecipient(
        address _recipient,
        address _recipientToken,
        address[] memory _rebalancers,
        uint256 _threshold
    ) external;
}

File 7 of 7 : SafeTransfer.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

library SafeTransfer {
    error TransferFailed();

    /// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
    /// also when the token returns `false`.
    function _safeTransfer(address _token, address _to, uint256 _value) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let freeMemoryPointer := mload(0x40)
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(_to, 0xffffffffffffffffffffffffffffffffffffffff))
            mstore(add(freeMemoryPointer, 36), _value)

            if iszero(call(gas(), _token, 0, freeMemoryPointer, 68, 0, 0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }

        if (!getLastTransferResult(_token)) {
            revert TransferFailed();
        }
    }

    /// @dev Verifies that the last return was a successful `transfer*` call.
    /// This is done by checking that the return data is either empty, or
    /// is a valid ABI encoded boolean.
    function getLastTransferResult(address _token) private view returns (bool success) {
        // NOTE: Inspecting previous return data requires assembly. Note that
        // we write the return data to memory 0 in the case where the return
        // data size is 32, this is OK since the first 64 bytes of memory are
        // reserved by Solidy as a scratch space that can be used within
        // assembly blocks.
        // <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
        // solhint-disable-next-line no-inline-assembly
        assembly {
            /// @dev Revert with an ABI encoded Solidity error with a message
            /// that fits into 32-bytes.
            ///
            /// An ABI encoded Solidity error has the following memory layout:
            ///
            /// ------------+----------------------------------
            ///  byte range | value
            /// ------------+----------------------------------
            ///  0x00..0x04 |        selector("Error(string)")
            ///  0x04..0x24 |      string offset (always 0x20)
            ///  0x24..0x44 |                    string length
            ///  0x44..0x64 | string value, padded to 32-bytes
            function revertWithMessage(length, message) {
                mstore(0x00, "\x08\xc3\x79\xa0")
                mstore(0x04, 0x20)
                mstore(0x24, length)
                mstore(0x44, message)
                revert(0x00, 0x64)
            }

            switch returndatasize()
            // Non-standard ERC20 transfer without return.
            case 0 {
                // NOTE: When the return data size is 0, verify that there
                // is code at the address. This is done in order to maintain
                // compatibility with Solidity calling conventions.
                // <https://docs.soliditylang.org/en/v0.7.6/control-structures.html#external-function-calls>
                if iszero(extcodesize(_token)) { revertWithMessage(20, "!contract") }

                success := 1
            }
            // Standard ERC20 transfer returning boolean success value.
            case 32 {
                returndatacopy(0, 0, returndatasize())

                // NOTE: For ABI encoding v1, any non-zero value is accepted
                // as `true` for a boolean. In order to stay compatible with
                // OpenZeppelin's `SafeERC20` library which is known to work
                // with the existing ERC20 implementation we care about,
                // make sure we return success for any non-zero return value
                // from the `transfer*` call.
                success := iszero(iszero(mload(0)))
            }
            default { revertWithMessage(31, "malformed") } // malformed transfer result
        }
    }

    function _safeTransferETH(address _to, uint256 _amount) internal {
        bool success;

        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), _to, _amount, 0, 0, 0, 0)
        }

        if (!success) {
            revert TransferFailed();
        }
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "@deployer/=lib/safe-singleton-deployer-sol/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "safe-singleton-deployer-sol/=lib/safe-singleton-deployer-sol/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_accountFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountNotFound","type":"error"},{"inputs":[],"name":"IncorrectAccountCount","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidOwner","type":"error"},{"inputs":[],"name":"InvalidRecipientToken","type":"error"},{"inputs":[],"name":"InvalidWithdrawArguments","type":"error"},{"inputs":[],"name":"MaxAccountsReached","type":"error"},{"inputs":[],"name":"NoAccounts","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"AccountOrderUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"accountCount","type":"uint256"}],"name":"AccountRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AccountStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"recipientToken","type":"address"}],"name":"CoordinatorInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"recipientToken","type":"address"}],"name":"NewRecipient","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newThreshold","type":"uint256"}],"name":"ThresholdUpdated","type":"event"},{"inputs":[],"name":"MAX_ACCOUNTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accountCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accountFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_investmentToken","type":"address"},{"internalType":"uint256","name":"_dustAmount","type":"uint256"}],"name":"addAccount","outputs":[{"internalType":"address","name":"newAccount","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"amountToRebalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAccountAssets","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAccountInfo","outputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"status","type":"bool"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ICoordinator.AccountInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAccountTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAccounts","outputs":[{"internalType":"address[]","name":"_accounts","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_threshold","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_recipientToken","type":"address"},{"internalType":"address","name":"_investmentToken","type":"address"},{"internalType":"uint256","name":"_dustAmount","type":"uint256"},{"internalType":"bool","name":"_isRebalanceAccount","type":"bool"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isRebalanceAccount","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebalanceTrigger","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recipientToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"removeAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_accounts","type":"address[]"}],"name":"setAccountOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_accounts","type":"address[]"},{"internalType":"bool[]","name":"_status","type":"bool[]"}],"name":"setAccountStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_recipientToken","type":"address"}],"name":"setRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_threshold","type":"uint256"}],"name":"setThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_recipientToken","type":"address"},{"internalType":"address[]","name":"_rebalancers","type":"address[]"},{"internalType":"uint256","name":"_threshold","type":"uint256"}],"name":"setThresholdWithRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"threshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_accounts","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Block Transaction Gas Used Reward
view all blocks validated

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.