IssueTokenSaleManager

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
"
    },
    "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
"
    },
    "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
      "content": "// 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);
}
"
    },
    "contracts/ExecutivePeerTreasury.sol": {
      "content": "// SPDX-License-Identifier: Unlicensed\r
// Copyright 2017-2025 US Fintech LLC\r
// \r
// Permission to use, copy, modify, or distribute this software is strictly prohibited\r
// without prior written consent from US Fintech LLC.\r
// \r
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\r
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\r
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,\r
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
// OFFICIAL DEL NORTE NETWORK COMPONENT\r
// ExecutivePeerTreasury is a contract that allows for the transfer of ETH and ERC20 tokens between different contracts.\r
// Designed by Ken Silverman as part of his ElasticTreasury (HUB and SPOKE), PeerTreasury and Controller-based eco-system.   \r
// Permission to change metadata stored on blockchain explorers exclusively reserved to US Fintech, LLC.\r
pragma solidity 0.8.30;\r
\r
import "../interfaces/IController.sol";\r
import "./TreasuryConstants.sol";\r
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";\r
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";\r
\r
// Earlier versions Compiled by Maleeha Naveed. May 5th, 2025\r
// ExecutivePeerTreasury is a contract that allows for the transfer of ETH and ERC20 tokens between different contracts.\r
/// @author Ken Silverman \r
// - first decentralized RWA management architect, est. 2017.\r
contract ExecutivePeerTreasury is TreasuryConstants {\r
\r
    using SafeERC20 for IERC20;\r
    \r
    event PeerTreasuryReceivedETH(address indexed sender, uint256 amount);\r
    event PeerTreasuryReceivedTokens(address indexed sender, address indexed token, uint256 amount);\r
    event PeerTreasuryTransferredETH(address indexed to, uint256 amount);\r
    event PeerTreasuryTransferredTokens(address indexed to, address indexed token, uint256 amount);\r
    event ExecutiveWithdrawalEvent(address indexed adminCaller, address indexed receiver, uint256 amt, string note, address tokenOrZero);\r
\r
\r
    struct TreasuryEntry {\r
        uint256 totalReceived;\r
        uint256 totalWithdrawn;\r
    }\r
\r
    struct Withdrawal{\r
        address to;\r
        address asset;\r
        uint256 amt;\r
        string note;\r
    }\r
\r
    address public immutable peerTreasuryController;\r
\r
    mapping(address => TreasuryEntry) private tokenPeerTreasury;\r
    address[] internal knownTokens; // <-- Needed for enumeration\r
\r
    TreasuryEntry private ethPeerTreasury;\r
    Withdrawal[] private withdrawals; // Track all executive withdrawals\r
\r
    modifier onlyTreasuryAdmin() {\r
        require(\r
            IController(peerTreasuryController).isOfficialDoubleEntityFast(KECCAK_TREASURY_ADMIN, msg.sender, KECCAK_SMART_CONTRACT, msg.sender, false),\r
            "Unauthorized"\r
        );\r
        _;\r
    }\r
\r
    constructor(address _controller, string memory _contractName) {\r
        peerTreasuryController = _controller;\r
        IController(_controller).init(address(this), _contractName);\r
    }\r
\r
    // EMERGENCY withdraw for admin controlled DIRECT withdrawals to a PERSON (not a smart contract)\r
    // this can ONLY happen for example, when the company wants to send some USDC or ETH from the Sales contract\r
    // to an EXECUTIVE of the company for the purposes of exchanging to USDC, as an example.\r
    // Such USDC can be spent on a capital expense or manually passed into the vesting pool.\r
    // ONLY HUBS can do this. For example, only contracts GENERATING or expected to RECEIVE\r
    // ETH or tokens from an external event (not from a HUB)  should be declared as HUBS.\r
    // EXCEPTIONS: send tokens to a launchpad or other noted entity can be a SC.\r
    function executiveWithdrawETH(address personAddress, uint256 amt, string memory note) external onlyTreasuryAdmin {\r
        require(address(this).balance >= amt, "Insufficient ETH balance");\r
        (bool success, ) = payable(personAddress).call{value: amt}("");\r
        require(success, "ETH transfer failed");\r
        withdrawals.push(Withdrawal(personAddress,address(0),amt,note));\r
        emit ExecutiveWithdrawalEvent(msg.sender, personAddress, amt, note, address(0));\r
    }\r
\r
    function executiveWithdrawTokens(address personAddress, uint256 amt, address tokenSCAddress, string calldata note) external onlyTreasuryAdmin {\r
        require(tokenSCAddress != address(0), "Invalid tok addr");\r
        IERC20 token = IERC20(tokenSCAddress);\r
        require(token.balanceOf(address(this)) >= amt, "Insuffic tok bal");\r
        IERC20(tokenSCAddress).safeTransfer(personAddress, amt);\r
        withdrawals.push(Withdrawal(personAddress,tokenSCAddress,amt,note));\r
        emit ExecutiveWithdrawalEvent(msg.sender, personAddress, amt, note, tokenSCAddress);\r
    }\r
\r
    /// @notice Receive ETH from any source and track it\r
    function peerTreasuryReceiveETH() virtual external payable {\r
        require(msg.value > 0, "No ETH sent");\r
        require(IController(peerTreasuryController).isOfficialEntity("SmartContract", msg.sender), "Sender is not an official SmartContract");\r
        ethPeerTreasury.totalReceived += msg.value;\r
        emit PeerTreasuryReceivedETH(msg.sender, msg.value);\r
    }\r
\r
    /// @notice Transfer ETH to another peer contract (OfficialEntity SmartContract)\r
    function peerTreasuryTransferETH(address payable to, uint256 amount) virtual public onlyTreasuryAdmin {\r
        require(address(this).balance >= amount, "Insufficient contract ETH balance");\r
        require(IController(peerTreasuryController).isOfficialEntity("SmartContract", to), "Recipient not an official SmartContract");\r
\r
        ethPeerTreasury.totalWithdrawn += amount;\r
        ExecutivePeerTreasury(to).peerTreasuryReceiveETH{value: amount}();\r
        emit PeerTreasuryTransferredETH(to, amount);\r
    }\r
\r
    /// @notice Receive ERC20 tokens and track source\r
    function peerTreasuryReceiveTokens(address token, uint256 amount) virtual external {\r
        require(amount > 0, "Zero amount");\r
        require(IController(peerTreasuryController).isOfficialEntity("SmartContract", msg.sender), "Sender not official SC");\r
        IERC20(token).safeTransferFrom(msg.sender, address(this), amount);\r
\r
        // Track the token if it's the first time we're seeing it\r
        if (tokenPeerTreasury[token].totalReceived == 0 && tokenPeerTreasury[token].totalWithdrawn == 0) {\r
            knownTokens.push(token);\r
        }\r
\r
        tokenPeerTreasury[token].totalReceived += amount;\r
        emit PeerTreasuryReceivedTokens(msg.sender, token, amount);\r
    }\r
\r
    /// @notice Transfer ERC20 tokens to another peer contract (OfficialEntity SmartContract)\r
    function peerTreasuryTransferTokens(address token, address to, uint256 amount) virtual public onlyTreasuryAdmin {\r
        require(IERC20(token).balanceOf(address(this)) >= amount, "Insuffic token bal");\r
        require(IController(peerTreasuryController).isOfficialEntity("SmartContract", to), "recipient not official SC");\r
        tokenPeerTreasury[token].totalWithdrawn += amount;\r
         // Get current allowance and reset to zero using decrease\r
        uint256 currentAllowance = IERC20(token).allowance(address(this), to);\r
        if (currentAllowance > 0) {\r
            IERC20(token).safeDecreaseAllowance(to, currentAllowance);\r
        }\r
        // Set new allowance using increase\r
        IERC20(token).safeIncreaseAllowance(to, amount);\r
        ExecutivePeerTreasury(to).peerTreasuryReceiveTokens(token, amount);\r
        emit PeerTreasuryTransferredTokens(to, token, amount);\r
    }\r
\r
    /// @notice Check ETH available (total received - total withdrawn)\r
    /// @return The signed balance (can be negative if more was withdrawn than received - which is OK!)\r
    /// obviously the first lateral movement will be a withdrawal from a peerTreasury source\r
    function peerETHAvailable() external view returns (int256) {\r
        // Convert to int256 before subtraction to allow negative results\r
        return int256(ethPeerTreasury.totalReceived) - int256(ethPeerTreasury.totalWithdrawn);\r
    }\r
\r
    /// @notice Check token available (total received - total withdrawn)\r
    /// @return The signed balance (can be negative if more was withdrawn than received)\r
    /// obviously the first lateral movement will be a withdrawal from a peerTreasury source\r
    function peerTokenAvailable(address token) external view returns (int256) {\r
        TreasuryEntry storage e = tokenPeerTreasury[token];\r
        // Convert to int256 before subtraction to allow negative results\r
        return int256(e.totalReceived) - int256(e.totalWithdrawn);\r
    }\r
\r
    struct TreasurySnapshot {\r
        address token;\r
        uint256 totalReceived;\r
        uint256 totalWithdrawn;\r
    }\r
\r
\r
    /// @notice Return all known peer treasury token entries\r
   function getPeerTreasuries() external view returns (TreasurySnapshot[] memory) {\r
        uint256 len = knownTokens.length;\r
        TreasurySnapshot[] memory result = new TreasurySnapshot[](len);\r
        for (uint256 i = 0; i < len; i++) {\r
            address token = knownTokens[i];\r
            TreasuryEntry storage entry = tokenPeerTreasury[token];\r
\r
            result[i] = TreasurySnapshot({\r
                token: token,\r
                totalReceived: entry.totalReceived,\r
                totalWithdrawn: entry.totalWithdrawn\r
            });\r
        }\r
        return result;\r
    }\r
\r
    /// @notice Return individual peer treasury entry for a token\r
    function getPeerTreasury(address token) external view returns (uint256 received, uint256 withdrawn) {\r
        TreasuryEntry storage entry = tokenPeerTreasury[token];\r
        return (entry.totalReceived, entry.totalWithdrawn);\r
    }\r
\r
    /// @notice Return ETH treasury data\r
    function getPeerTreasuryETH() external view returns (uint256 received, uint256 withdrawn) {\r
        return (ethPeerTreasury.totalReceived, ethPeerTreasury.totalWithdrawn);\r
    }\r
\r
    function getAllWithdrawals() external view returns(Withdrawal[] memory){\r
        return withdrawals;\r
    }\r
\r
    \r
    \r
    // SAFE DIRECT METHODS\r
    /// @notice DIRECT TOKEN TRANSFER - Transfer tokens directly without approve/transferFrom pattern\r
    /// @param token Token contract address\r
    /// @param to Recipient PeerTreasury contract\r
    /// @param amount Amount to transfer\r
    function peerTreasuryTransferTokensDirect(address token, address to, uint256 amount) virtual public onlyTreasuryAdmin {\r
        require(IERC20(token).balanceOf(address(this)) >= amount, "Low token bal");\r
        require(IController(peerTreasuryController).isOfficialEntity("SmartContract", to), "rcpnt !ofcl SC");\r
        // Update withdrawal tracking\r
        tokenPeerTreasury[token].totalWithdrawn += amount;\r
        // Direct transfer - handles USDT properly\r
        IERC20(token).safeTransfer(to, amount);\r
        // Notify receiver for tracking (no actual transfer needed)\r
        ExecutivePeerTreasury(to).peerTreasuryNotifyReceived(token, amount);\r
        emit PeerTreasuryTransferredTokens(to, token, amount);\r
    }\r
\r
    /// @notice NOTIFICATION METHOD - Update counters when tokens received via direct transfer\r
    /// @param token Token contract address  \r
    /// @param amount Amount received\r
    function peerTreasuryNotifyReceived(address token, uint256 amount) virtual public {\r
        require(IController(peerTreasuryController).isOfficialEntity("SmartContract", msg.sender), "sender !ofcl SC");\r
        // Update received tracking (same as peerTreasuryReceiveTokens but no transferFrom)\r
        tokenPeerTreasury[token].totalReceived += amount;\r
        // tokenPeerTreasury[token].currentBalance += amount;\r
        emit PeerTreasuryReceivedTokens(msg.sender, token, amount);\r
    }\r
\r
    \r
}\r
"
    },
    "contracts/TreasuryConstants.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED\r
// Copyright 2025 US Fintech LLC\r
// \r
// Permission to use, copy, modify, or distribute this software is strictly prohibited\r
// without prior written consent from either copyright holder.\r
// \r
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\r
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\r
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\r
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,\r
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
\r
pragma solidity 0.8.30;\r
\r
/// @title TreasuryConstants\r
/// @notice Shared constants for treasury contracts to avoid diamond inheritance conflicts\r
/// @dev This abstract contract provides keccak256 hashes for entity type strings used across treasury contracts\r
/// @author Ken Silverman\r
abstract contract TreasuryConstants {\r
    /// @notice Pre-computed keccak256 hash of "TreasuryAdmin" for gas-optimized entity checks\r
    bytes32 public constant KECCAK_TREASURY_ADMIN = keccak256(bytes("TreasuryAdmin"));\r
    \r
    /// @notice Pre-computed keccak256 hash of "SmartContract" for gas-optimized entity checks\r
    bytes32 public constant KECCAK_SMART_CONTRACT = keccak256(bytes("SmartContract"));\r
    \r
    /// @notice Pre-computed keccak256 hash of "TokenAdmin" for gas-optimized entity checks\r
    bytes32 public constant KECCAK_TOKEN_ADMIN = keccak256(bytes("TokenAdmin"));\r
\r
    /// @notice Pre-computed keccak256 hash of "TokenAdmin" for gas-optimized entity checks\r
    bytes32 public constant KECCAK_REGISTRAR = keccak256(bytes("Registrar"));\r
    \r
    /// @notice Pre-computed keccak256 hash of "SwappableToken" for gas-optimized entity checks\r
    bytes32 public constant KECCAK_SWAPPABLE_TOKEN = keccak256(bytes("SwappableToken"));\r
\r
    /// @notice Pre-computed keccak256 hash of "PLATFORM_CHAIN_TREASURER" for gas-optimized entity checks\r
    bytes32 public constant KECCAK_PLATFORM_CHAIN_TREASURER = keccak256(bytes("PLATFORM_CHAIN_TREASURER"));\r
}\r
\r
"
    },
    "interfaces/IController.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
// Copyright 2025 US Fintech LLC and DelNorte Holdings.
// 
// Permission to use, copy, modify, or distribute this software is strictly prohibited
// without prior written consent from both copyright holders.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// OFFICIAL DEL NORTE NETWORK COMPONENT 
// Provides immediate membership access to platform at different levels. 
// Required Non US or accredited US registration to swap for DTV token. Registration available within 180 days per terms.delnorte.io . 
// Minimally tesed Conroller Tree for world-wide government administration of, well, anything, including property ownership.
// Designed by Ken Silverman as part of his ElasticTreasury (HUB and SPOKE), PeerTreasury and Controller model. 
// @author Ken Silverman
// This deployment is for Del Norte Holdings, Delaware and US Fintech, LLC NY.  
// Permission to change metadata stored on blockchain explorers and elsewhere granted to:
// Del Norte Holdings, DE only and/or US Fintech, LLC NY independently
pragma solidity 0.8.30;

interface IController {
    struct OfficialEntityStruct {
        string fullNameOfEntityOrLabel;
        string nationalIdOfEntity;
        address pubAddress;
        uint256 blockNumber;
        uint256 blockTimestamp;
        bool active;
        uint256 value;  // Associated value (0-1,000,000 for absolute, or basis points for %, type(uint256).max = no value)
    }

    struct ChainedEntityStruct {
        address entityAddress;
        address parent;
        uint8 type1;     // 0 = 'V' (value/absolute), 1 = 'P' (percentage in basis points)
        uint256 val1;    // Value for type1 (absolute amount or percentage in basis points)
        uint8 type2;     // 0 = 'V' (value/absolute), 1 = 'P' (percentage in basis points)
        uint256 val2;    // Value for type2 (absolute amount or percentage in basis points)
        bool active;
        uint256 blockNumber;
        uint256 blockTimestamp;
        string entityName;  // Human-readable name
        string entityID;    // Additional identifier (tax ID, registration #, etc)
    }

    struct CalculatedEntityAmount {
        address entityAddress;
        uint256 type1Amount;  // Calculated amount for type1 (e.g., transfer fee)
        uint256 type2Amount;  // Calculated amount for type2 (e.g., activation fee)
    }

    // Official Entity Functions
    function addOfficialEntity(string memory, address, string memory, string memory) external returns (bool);
    function addOfficialEntityWithValue(string memory, address, string memory, string memory, uint256) external returns (bool);
    function removeOfficialEntity(string memory, address) external returns (bool);
    function isOfficialEntity(string memory, address) external view returns (bool);
    function isOfficialEntityFast(bytes32, address) external view returns (bool);
    function isOfficialDoubleEntityFast(bytes32, address, bytes32, address, bool) external view returns (bool);
    function isOfficialTripleEntityFast(bytes32, address, bytes32, address, bytes32, address, bool) external view returns (bool);
    function isOfficialQuadrupleEntityFast(bytes32, address,  bytes32, address, bytes32, address, bytes32, address, bool) external view returns (bool);
    function getOfficialEntity(string calldata, address) external view returns (OfficialEntityStruct memory);
    function getAllOfficialEntities(string calldata, bool) external view returns (OfficialEntityStruct[] memory);
    function init(address, string calldata) external;
    
    // Chained Entity Functions
    function addChainedEntity(string memory, address, address, uint8, uint256, uint8, uint256, string memory, string memory) external returns (bool);
    function removeChainedEntity(string calldata, address, bool) external returns (bool);
    function isChainedEntity(string calldata, address) external view returns (bool);
    function isChainedEntityFast(bytes32, address) external view returns (bool);
    function getChainedEntity(string calldata, address) external view returns (ChainedEntityStruct memory);
    function getActiveChainedEntities(string calldata) external view returns (ChainedEntityStruct[] memory);
    function calculateChainedAmounts(string calldata, uint256, bool, uint256, uint8) external view returns (CalculatedEntityAmount[] memory, uint256, uint256);
    
    // Constants
    function BASIS_POINTS_DIVISOR() external view returns (uint256);
}
"
    },
    "contracts/IssueTokenSaleManager.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
// Copyright 2025 US Fintech LLC and DelNorte Holdings.
// 
// Permission to use, copy, modify, or distribute this software is strictly prohibited
// without prior written consent from either copyright holder.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// OFFICIAL DEL NORTE NETWORK COMPONENT 
// Issue Token Sale Manager handles sales of IssueToken (treasury shares) with automatic fee distribution
// to chained entities. Fees can be absolute (USDC) or percentage-based (paid in purchase token).
// This component is minimally tested. Use at your own risk.   
// Designed by Ken Silverman as part of his PeerTreasury and Controller model.  
// @author Ken Silverman
pragma solidity 0.8.30;

import "./ExecutivePeerTreasury.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/* ========== INTERFACES ========== */
interface ITokenDecimals {
    function decimals() external view returns (uint8);
    function symbol() external view returns (string memory);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

interface IIssueToken {
    function transfer(address recipient, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
    function getUserRegistrationStatus(address user, bool notGatedOverride) external view returns (uint8);
    function isActivated() external view returns (bool);
}

interface AggregatorV3Interface {
    function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

/**
 * @title IssueTokenSaleManager
 * @dev This contract handles IssueToken sales (like treasury shares) with automatic fee distribution
 * @author Ken Silverman
 */
contract IssueTokenSaleManager is ExecutivePeerTreasury {
    using SafeERC20 for IERC20;
    
    /* ========== CONSTANTS ========== */
    uint256 public constant MAXIMUM_ACCEPTABLE_DELAY = 1 hours;
    uint256 public constant BASIS_POINTS_DIVISOR = 10000; // 100% = 10000 basis points
    uint256 public constant MAX_SINGLE_FEE_BPS = 1000; // Maximum 10% per fee
    string public constant FEE_CHAIN_TYPE = "SALES_FEE_TREASURER"; // Fixed chain type for sales fees
    
    // Contract state variables
    address public immutable issueTokenAddress;
    string public issueTokenSymbol;
    address public immutable usdcAddress;
    address public immutable usdtAddress;
    AggregatorV3Interface public ethUsdPriceFeed;
    AggregatorV3Interface public usdtUsdPriceFeed;  // For USDT/USD conversion
    uint8 public usdcDecimals;
    uint8 public usdtDecimals;
    bool public saleActive;
    uint256 public startSaleBlockNum = 0;
    uint256 public purchasePriceInUSD;  // Price per token in USD (18 decimals)
    uint256 public totalTokensSold;
    uint256 public minDollarValue = 300e18;
    
    // Mappings
    mapping(address => uint256) public tokensSoldByPaymentToken;
    mapping(address => uint256) public userPurchases;  // Track purchases by user
    
    /* ========== EVENTS ========== */
    event TokenSold(address indexed buyer, address indexed paymentToken, uint256 paymentAmount, uint256 tokenAmount, uint256 totalFees);
    event FeeDistributed(address indexed recipient, address indexed paymentToken, uint256 amount, bool isPct);
    event SaleStatusChanged(address indexed caller, bool newStatus);
    event StartSaleBlockNumChanged(uint256 newStartSaleBlockNum);
    event PurchasePriceChanged(uint256 newPrice);
    event MinDollarValueChanged(uint256 newMinDollarValue);

    modifier onlyTokenAdmin() {
        require(IController(peerTreasuryController).isOfficialEntity("TokenAdmin", msg.sender), "Not TokenAdmin");
        _;
    }
    
    /* ========== CONSTRUCTOR ========== */
    constructor(
        address _controller,
        address _issueTokenAddress,
        string memory _issueTokenSymbol,
        address _ethUsdPriceFeed,
        address _usdtUsdPriceFeed,
        address _usdcAddress,
        address _usdtAddress
    )
        ExecutivePeerTreasury(_controller, "IssueTokenSaleManager")
    {
        require(keccak256(bytes(ITokenDecimals(_issueTokenAddress).symbol())) == keccak256(bytes(_issueTokenSymbol)), "Incorrect token address; symbol mismatch");
        issueTokenAddress = _issueTokenAddress;
        issueTokenSymbol = _issueTokenSymbol;
        IController(_controller).addOfficialEntity("SalesTargetToken", _issueTokenAddress, "", "");
        ethUsdPriceFeed = AggregatorV3Interface(_ethUsdPriceFeed);
        usdtUsdPriceFeed = AggregatorV3Interface(_usdtUsdPriceFeed);
        usdcAddress = _usdcAddress;
        usdtAddress = _usdtAddress;
        usdcDecimals = ITokenDecimals(_usdcAddress).decimals();
        usdtDecimals = ITokenDecimals(_usdtAddress).decimals();
        purchasePriceInUSD = 1e18; // Default: $1.00 per token
        saleActive = false;
    }

    function setMinDollarValue(uint256 _minDollarValue) external onlyTokenAdmin {
        minDollarValue = _minDollarValue * 1e18;
        emit MinDollarValueChanged(_minDollarValue);
    }
    
    /* ========== ADMIN FUNCTIONS ========== */
    function setSaleActive(bool _active) external onlyTokenAdmin {
        saleActive = _active;
        emit SaleStatusChanged(msg.sender, _active);
    }
    
    function isSaleActive() public view returns (bool) {
        return saleActive && startSaleBlockNum > 0 && block.number >= startSaleBlockNum;
    }
    
    function setStartSaleBlockOffset(uint256 offset) external onlyTokenAdmin {
        if (offset < 1) {
            saleActive = false;
            startSaleBlockNum = 0;
        } else {
            startSaleBlockNum = block.number + offset;
        }
        emit StartSaleBlockNumChanged(startSaleBlockNum);
    }
    
    function getStartSaleTimeInBlocks() external view returns (uint256) {
        return startSaleBlockNum > block.number ? startSaleBlockNum - block.number : 0;
    }
    
    function setPurchasePriceInUSD(uint256 newPrice) external {
        require(IController(peerTreasuryController).isOfficialEntity("TreasuryAdmin", msg.sender), "Only TreasuryAdmin can change price");
        purchasePriceInUSD = newPrice;
        emit PurchasePriceChanged(newPrice);
    }
    
    /* ========== CORE SALE FUNCTIONS ========== */
    
    /**
     * @notice Buy issue tokens with USDC or USDT, fees distributed automatically
     * @param amount Amount of stablecoin to spend
     * @param isUSDC true for USDC, false for USDT
     */
    function buyWithStableCoin(uint256 amount, bool isUSDC) external {
        require(isSaleActive(), "Sale not active");
        require(IIssueToken(issueTokenAddress).isActivated(), "Token not activated");
        uint8 registrationStatus = IIssueToken(issueTokenAddress).getUserRegistrationStatus(msg.sender, false);
        require(registrationStatus > 0, "Caller not registered");
        
        address paymentToken = isUSDC ? usdcAddress : usdtAddress;
        uint8 tokenDecimals = isUSDC ? usdcDecimals : usdtDecimals;
        
        // Convert to 18 decimals
        uint256 amount18 = amount * (10 ** (18 - tokenDecimals));
        require(amount18 >= minDollarValue, "Under minimum purchase");
        
        // Calculate token amount
        uint256 tokenAmount = (amount18 * 1e18) / purchasePriceInUSD;
        
        // Transfer payment from buyer
        IERC20(paymentToken).safeTransferFrom(msg.sender, address(this), amount);
        
        // Distribute fees
        uint256 totalFees = _distributeFees(paymentToken, amount18, amount, tokenDecimals);
        
        // Transfer tokens to buyer
        require(IIssueToken(issueTokenAddress).transfer(msg.sender, tokenAmount), "Token transfer failed");
        
        // Update tracking
        totalTokensSold += tokenAmount;
        tokensSoldByPaymentToken[paymentToken] += tokenAmount;
        userPurchases[msg.sender] += tokenAmount;
        
        emit TokenSold(msg.sender, paymentToken, amount, tokenAmount, totalFees);
    }
    
    /**
     * @notice Buy tokens with USDC using permit (no separate approve needed)
     */
    function buyWithUSDCPermit(uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external {
        require(isSaleActive(), "Sale not active");
        require(IIssueToken(issueTokenAddress).isActivated(), "Token not activated");
        uint8 registrationStatus = IIssueToken(issueTokenAddress).getUserRegistrationStatus(msg.sender, true);
        require(registrationStatus > 0, "Caller not registered");
        
        // Permit
        IERC20Permit(usdcAddress).permit(msg.sender, address(this), amount, deadline, v, r, s);
        
        // Convert to 18 decimals
        uint256 amount18 = amount * (10 ** (18 - usdcDecimals));
        require(amount18 >= minDollarValue, "Under minimum purchase");
        
        // Calculate token amount
        uint256 tokenAmount = (amount18 * 1e18) / purchasePriceInUSD;
        
        // Transfer payment from buyer
        IERC20(usdcAddress).safeTransferFrom(msg.sender, address(this), amount);
        
        // Distribute fees
        uint256 totalFees = _distributeFees(usdcAddress, amount18, amount, usdcDecimals);
        
        // Transfer tokens to buyer
        require(IIssueToken(issueTokenAddress).transfer(msg.sender, tokenAmount), "Token transfer failed");
        
        // Update tracking
        totalTokensSold += tokenAmount;
        tokensSoldByPaymentToken[usdcAddress] += tokenAmount;
        userPurchases[msg.sender] += tokenAmount;
        
        emit TokenSold(msg.sender, usdcAddress, amount, tokenAmount, totalFees);
    }
    
    /**
     * @notice Purchase tokens with ETH
     */
    function buyWithETH() external payable {
        require(isSaleActive(), "Sale not active");
        require(IIssueToken(issueTokenAddress).isActivated(), "Token not activated");
        uint8 registrationStatus = IIssueToken(issueTokenAddress).getUserRegistrationStatus(msg.sender, false);
        require(registrationStatus > 0, "Caller not registered");
        
        // Get ETH price from oracle
        (uint80 roundId, int256 price, , uint256 updatedAt, uint80 answeredInRound) = ethUsdPriceFeed.latestRoundData();
        require(price > 0, "Invalid price from oracle");
        require(answeredInRound >= roundId, "Stale price");
        require(block.timestamp - updatedAt < MAXIMUM_ACCEPTABLE_DELAY, "Price data too old");
        
        // Calculate USD value (price has 8 decimals, multiply by 1e10 to get 18 decimals)
        uint256 usdValue = (msg.value * uint256(price) * 1e10) / 1e18;
        require(usdValue >= minDollarValue, "Under minimum purchase");
        
        // Calculate token amount
        uint256 tokenAmount = (usdValue * 1e18) / purchasePriceInUSD;
        
        // Distribute fees in ETH
        uint256 totalFees = _distributeFees(address(0), usdValue, msg.value, 18);
        
        // Transfer tokens to buyer
        require(IIssueToken(issueTokenAddress).transfer(msg.sender, tokenAmount), "Token transfer failed");
        
        // Update tracking
        totalTokensSold += tokenAmount;
        tokensSoldByPaymentToken[address(0)] += tokenAmount;
        userPurchases[msg.sender] += tokenAmount;
        
        emit TokenSold(msg.sender, address(0), msg.value, tokenAmount, totalFees);
    }
    
    /* ========== FEE DISTRIBUTION ========== */
    
    /**
     * @notice Distribute fees to all active chained entities
     * @param paymentToken Address of payment token (address(0) for ETH)
     * @param paymentAmount Actual payment amount in token's native decimals
     * @return totalFees Total fees distributed (in payment token's native decimals)
     */
    function _distributeFees(
        address paymentToken,
        uint256 /* usdValue18 */,
        uint256 paymentAmount,
        uint8 /* paymentDecimals */
    ) internal returns (uint256 totalFees) {
        // Get all active chained entities
        IController.ChainedEntityStruct[] memory feeEntities = 
            IController(peerTreasuryController).getActiveChainedEntities(FEE_CHAIN_TYPE);
        
        if (feeEntities.length == 0) {
            return 0; // No fees to distribute
        }
        
        for (uint256 i = 0; i < feeEntities.length; i++) {
            uint256 feeAmount;
            
            // val2 = Percentage-based fee (paid in payment token, no conversion needed)
            if (feeEntities[i].val2 > 0) {
                require(feeEntities[i].val2 <= MAX_SINGLE_FEE_BPS, "Percentage fee exceeds 10% limit");
                feeAmount = (paymentAmount * feeEntities[i].val2) / BASIS_POINTS_DIVISOR;
                
                // Send percentage fee in payment token
                if (paymentToken == address(0)) {
                    // ETH
                    (bool success, ) = feeEntities[i].entityAddress.call{value: feeAmount}("");
                    require(success, "ETH percentage fee transfer failed");
                } else {
                    // ERC20
                    IERC20(paymentToken).safeTransfer(feeEntities[i].entityAddress, feeAmount);
                }
                
                totalFees += feeAmount;
                emit FeeDistributed(feeEntities[i].entityAddress, paymentToken, feeAmount, true);
            }
            
            // val1 = Absolute fee in USDC terms (needs conversion to payment token)
            if (feeEntities[i].val1 > 0) {
                uint256 absoluteFeeInPaymentToken;
                
                if (paymentToken == usdcAddress) {
                    // Paying in USDC - no conversion needed
                    absoluteFeeInPaymentToken = feeEntities[i].val1 * (10 ** usdcDecimals) / 1e18;
                    IERC20(usdcAddress).safeTransfer(feeEntities[i].entityAddress, absoluteFeeInPaymentToken);
                    
                } else if (paymentToken == usdtAddress) {
                    // Paying in USDT - convert USDC to USDT using oracle
                    (uint80 roundId, int256 usdtPrice, , uint256 updatedAt, uint80 answeredInRound) = usdtUsdPriceFeed.latestRoundData();
                    require(usdtPrice > 0, "Invalid USDT price from oracle");
                    require(answeredInRound >= roundId, "Stale USDT price");
                    require(block.timestamp - updatedAt < MAXIMUM_ACCEPTABLE_DELAY, "USDT price data too old");
                    
                    // Convert: USDC amount -> USD value -> USDT amount
                    // usdtPrice has 8 decimals (e.g., 100000000 = $1.00)
                    // Formula: (usdcAmount * 1e8) / usdtPrice = usdtAmount
                    uint256 usdcAmount18 = feeEntities[i].val1; // Already in 18 decimals
                    absoluteFeeInPaymentToken = (usdcAmount18 * 1e8 * (10 ** usdtDecimals)) / (uint256(usdtPrice) * 1e18);
                    IERC20(usdtAddress).safeTransfer(feeEntities[i].entityAddress, absoluteFeeInPaymentToken);
                    
                } else if (paymentToken == address(0)) {
                    // Paying in ETH - convert USDC to ETH using oracle
                    (uint80 roundId, int256 ethPrice, , uint256 updatedAt, uint80 answeredInRound) = ethUsdPriceFeed.latestRoundData();
                    require(ethPrice > 0, "Invalid ETH price from oracle");
                    require(answeredInRound >= roundId, "Stale ETH price");
                    require(block.timestamp - updatedAt < MAXIMUM_ACCEPTABLE_DELAY, "ETH price data too old");
                    
                    // Convert: USDC amount -> USD value -> ETH amount
                    // ethPrice has 8 decimals (e.g., 200000000000 = $2000)
                    // Formula: (usdValue * 1e18) / ethPrice = ethAmount
                    uint256 usdValue = feeEntities[i].val1; // Already in 18 decimals
                    absoluteFeeInPaymentToken = (usdValue * 1e18 * 1e8) / (uint256(ethPrice) * 1e18);
                    
                    (bool success, ) = feeEntities[i].entityAddress.call{value: absoluteFeeInPaymentToken}("");
                    require(success, "ETH absolute fee transfer failed");
                } else {
                    // Unsupported payment token for absolute fees
                    revert("Cannot convert absolute fee to payment token");
                }
                
                totalFees += absoluteFeeInPaymentToken;
                emit FeeDistributed(feeEntities[i].entityAddress, paymentToken, absoluteFeeInPaymentToken, false);
            }
            
            // val3 (activation fee) is NOT used in sales, only in IssueToken activation
        }
        
        return totalFees;
    }
    
    /* ========== TREASURY WITHDRAWAL ========== */
    
    /**
     * @notice Withdraw unsold issue tokens (admin only)
     * @dev For ETH/ERC20 withdrawals, use inherited executiveWithdrawETH() and executiveWithdrawTokens()
     */
    function withdrawUnsoldTokens(address recipient, uint256 amount) external {
        require(IController(peerTreasuryController).isOfficialEntity("TreasuryAdmin", msg.sender), "Not TreasuryAdmin");
        require(recipient != address(0), "Invalid recipient");
        require(IIssueToken(issueTokenAddress).transfer(recipient, amount), "Token withdrawal failed");
    }
    
    // Allow contract to receive ETH
    receive() external payable {}
}

"
    },
    "@openzeppelin/contracts/utils/Address.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}
"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 10000
    },
    "viaIR": true,
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    }
  }
}}

Tags:
ERC20, Multisig, Swap, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xd71522c91499d6e960f41b0bc6eae287cb14cd9a|verified:true|block:23723198|tx:0x56b123356f3f452eb5ab38f7bff385bb3ba18f763574d88d6e6432bc7067a0dc|first_check:1762251033

Submitted on: 2025-11-04 11:10:10

Comments

Log in to comment.

No comments yet.