Controller

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/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../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 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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @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.isContract(address(token));
    }
}
"
    },
    "contracts/Recoverable.sol": {
      "content": "// SPDX-License-Identifier: CLOSED LICENSE COPYRIGHT 2025\r
// OFFICAL DEL NORTE NETWORK COMPONENT\r
// Designed By Ken Silverman for Del Norte.  Implementation help from Tony Sparks\r
// Compilation help from Maleeha Naveed. May 5th, 2025\r
\r
pragma solidity 0.8.30;\r
\r
\r
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";\r
import "../interfaces/IController.sol";\r
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";\r
\r
\r
/// @title ReversibleRecoveryBase\r
/// @notice Base contract tracking / reversing accidental ETH/ERC20 transfers with admin authorization\r
abstract contract Recoverable {\r
\r
    using SafeERC20 for IERC20;\r
\r
    // STRUCTS\r
\r
\r
    struct ReversibleUserBalance {\r
        uint256 totalReceivedThatIsReversible;\r
        uint256 totalReversed;\r
        uint256 totalReversals;\r
    }\r
\r
\r
    // -----------------------------\r
    // STORAGE\r
    // -----------------------------\r
\r
\r
    address public controller;  // Address of the Controller contract (must implement IController)\r
    uint256 public ADMIN_FEE_FIXED = 10 ** 17; // 0.1 ETH\r
    uint256 public totalAdminFeesCollected;\r
\r
\r
    mapping(address => ReversibleUserBalance) public reversibleEthBalances;\r
    mapping(address => mapping(address => ReversibleUserBalance)) public reversibleTokenBalances;\r
\r
\r
    // -----------------------------\r
    // EVENTS\r
    // -----------------------------\r
    event TransferReversed(address indexed user, uint256 refundAmount, address tokenSC, uint256 adminFee);\r
    event AdminFeeUpdated(uint256 newFee);\r
    event ControllerChanged(address newController);\r
\r
\r
     modifier onlyBTExecutives() {\r
        bool temp = IController(controller).isOfficialEntity("TreasuryAdmin", msg.sender) ||\r
                    IController(controller).isOfficialEntity("SmartContract", msg.sender);\r
        require(temp, "Unauthorized access");\r
        _;  // run the code block referencing this modifier\r
    }\r
\r
\r
    // -----------------------------\r
    // CONSTRUCTOR\r
    // -----------------------------\r
    constructor(address _controller) {\r
        require(_controller != address(0), "Controller address cannot be zero");\r
        controller = _controller;\r
    }\r
\r
\r
    // -----------------------------\r
    // EXTERNAL METHODS\r
    // -----------------------------\r
    function manualUpdateReversibleBalanceETH(address userAddress, uint256 amount)\r
        external onlyBTExecutives  {\r
        reversibleEthBalances[userAddress].totalReceivedThatIsReversible += amount;\r
    }\r
\r
\r
\r
\r
    function manualUpdateReversibleBalanceERC20(address userAddress, uint256 amount, address tokenSC)\r
        external onlyBTExecutives {\r
        reversibleTokenBalances[tokenSC][userAddress].totalReceivedThatIsReversible += amount;\r
    }\r
\r
\r
    function reverseAccidentalETH() external payable  {\r
        require(msg.value >= ADMIN_FEE_FIXED, "Insufficient admin fee");\r
        require(!IController(controller).isOfficialEntity("Registrar", msg.sender),\r
            "Registrars/launchpads may not be allowed to reverse any amounts they send.");\r
        ReversibleUserBalance storage balance = reversibleEthBalances[msg.sender];\r
        uint256 refundAmount = balance.totalReceivedThatIsReversible - balance.totalReversed;\r
        require(refundAmount > 0, "Nothing to refund");\r
        // Update state before external call\r
        balance.totalReversed += refundAmount;\r
        balance.totalReversals += 1;\r
        totalAdminFeesCollected += msg.value;\r
        // Perform the external call\r
        (bool success, ) = msg.sender.call{value: refundAmount}("");\r
        require(success, "Ether transfer failed");\r
        emit TransferReversed(msg.sender, refundAmount, address(0), msg.value);\r
    }\r
\r
\r
\r
    function reverseAccidentalERC20(address tokenSC) external payable {\r
        require(msg.value >= ADMIN_FEE_FIXED, "Insufficient admin fee");\r
        require(!IController(controller).isOfficialEntity("Registrar", msg.sender), "Registrars/launchpads may not reverse any amounts they send.");\r
        ReversibleUserBalance storage balance = reversibleTokenBalances[tokenSC][msg.sender];\r
        uint256 refundAmount = balance.totalReceivedThatIsReversible - balance.totalReversed;\r
        require(refundAmount > 0, "Nothing to refund");\r
        // Update state before external call\r
        balance.totalReversed += refundAmount;\r
        balance.totalReversals += 1;\r
        totalAdminFeesCollected += msg.value;\r
        // Perform the external call\r
        IERC20(tokenSC).safeTransfer(msg.sender, refundAmount);\r
        emit TransferReversed(msg.sender, refundAmount, tokenSC, msg.value);\r
  }\r
\r
\r
    function changeAdminFee(uint256 newFee) external onlyBTExecutives {\r
        ADMIN_FEE_FIXED = newFee;\r
        emit AdminFeeUpdated(newFee);\r
    }\r
\r
\r
   function changeController(address remote) internal {\r
        controller = remote;\r
        emit ControllerChanged(remote);\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/Controller.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 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 
// This component is minimally tested. Use at your own risk.   
// Designed by Ken Silverman as part of his ExecutivePeerTreasury, Controller and IssueToken model.  
// Complete Asset Management System for all assets, including real estate, stocks, bonds, etc.
// This deployment is for the Data Cloud OS Desktop enterprise CRM and management system owned by US Fintech, LLC. 
// Earlier Deployments for Trueviewchain Inc. a Panama entity and Del Norte El Salvador S.A  a subsidiary of Del Norte Holdings, Delaware USA.  
// Permission to change metadata stored on blockchain explorers and elsewhere granted to US Fintech, LLC NY Del Norte Holdings, DE only.
// Early Compilation help from Tony Sparks and Maleeha Naveed. 
// Latest Version: Deployed by Maleeha Naveed on behalf of US Fintech. October 14th, 2025
pragma solidity 0.8.30;
import "./Recoverable.sol";

interface IERC20SymbolAndName {
        function symbol() external view returns (string memory);
        function name() external view returns (string memory);
}

/// @title Controller
/// @author Ken Silverman
/// @notice This contract is used to manage the official entities of all smart contract in the network.
contract Controller is Recoverable {


    event TreasuryCreated(address treasuryAddress, string label);
    event UpdateOfficials(
        address indexed entityAddress,
        address indexed updater,
        string entityType,              // "SC,SystemUser,SystemAdmin,BTADmin, etc ..."
        string action,                  // Add,Remove  (nothing is actually removed, only active boolean changed)
        string fullNameOfEntityOrLabel, // person's full name or "SC"
        string nationalIdOfEntity,       // registered to entity or SC address if entity is SC
        uint256 indexed blockTimeStamp,             // block number of the event
        bool remainsActive
    );

    event UpdateOfficialsWithValue(
        address indexed entityAddress,
        address indexed updater,
        string entityType,
        string action,
        string fullNameOfEntityOrLabel,
        string nationalIdOfEntity,
        uint256 indexed blockTimeStamp,
        bool remainsActive,
        uint256 value  // The associated value
    );

    event ChainedEntityUpdate(
        address indexed entityAddress,
        address indexed parent,
        address indexed updater,
        string chainType,
        string action,  // "Add", "Remove", "Reactivate"
        uint8 type1,    // 0 = 'V', 1 = 'P'
        uint256 val1,
        uint8 type2,    // 0 = 'V', 1 = 'P'
        uint256 val2,
        bool active,
        uint256 blockTimeStamp
    );

    // Constant for chained entity calculations (basis points)
    uint256 public constant BASIS_POINTS_DIVISOR = 10000; // 100% = 10000 basis points

    struct OfficialEntityStruct {
        string fullNameOfEntityOrLabel;
        string nationalIdOfEntity; // (if entity is a smart contract, leave empty or provide some other detail here.)
        address pubAddress;   // registered to entity or SC address if entity is SC
        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 OfficialEntityGroup {
        mapping(address => OfficialEntityStruct) entitiesByAddress; // Maps entity address to its details
        address[] entityList; // List of entity addresses for iteration
    }


    struct AdminTypeRegistry {
       mapping(string  => bool)  isRegistered;     // internally a hash key but requires string for lookup
       mapping(bytes32 => bool)  isHashRegistered; // slightly faster pre-hahsed lookup
       string[] registeredAdminTypes;
       mapping(bytes32 => string) nameByHash;       // Reverse lookup from hash to original string
    }

    // ChainedEntity structures for hierarchical fee/permission management
    struct ChainedEntityStruct {
        address entityAddress;
        address parent;  // address(0) for root nodes
        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)
    }

    struct ChainedEntityGroup {
        mapping(address => ChainedEntityStruct) entitiesByAddress;
        mapping(address => address[]) childrenByParent;  // Track children for recursive operations
        address[] rootNodes;  // Top nodes in each tree of the forest
    }


    mapping(string => OfficialEntityGroup) private officialEntityGroupsByType;
    mapping(string => ChainedEntityGroup) private chainedEntityGroupsByType;


    // NEW structure to SUPPORT validateAdminType


     AdminTypeRegistry private adminTypeRegistry;


    // address public remoteController; // Hub address for admin verification - ALWAYS "this" for now
    string public platformName;
    string public projectName;  // Project name (fixed at deployment)
    string public projectId;    // Project ID (fixed at deployment)
    bytes32 public constant KECCAK_TREASURY_ADMIN = keccak256(bytes("TreasuryAdmin"));
    bytes32 public constant KECCAK_SMART_CONTRACT = keccak256(bytes("SmartContract"));
    bytes32 public constant KECCAK_SUPER_ADMIN = keccak256(bytes("Super"));
    uint256 public constant NO_VALUE = type(uint256).max;  // Sentinel value indicating "no value set"
    uint256 public constant MAX_VALUE = 1000000;  // Maximum allowed value

    modifier onlySuper() {
        require(isOfficialEntityFast(KECCAK_SUPER_ADMIN, tx.origin), "caller not Super");
        _;
    }


    // Warning! Any official "Super" can call addEntity directly. This is NOT a first line of defense against that.
    //  The only defense there is to make sure your official SCs have their own calls to this controller’s
    //  isOfficialEntity(“Super”) for example BEFORE calling addEntity() or removeEntity()
    //  init() LOCKS this Controller to only allowing TreasAdmins to adding official SCs to begin with.
    //  REMOTE case now allows a smartContract caller unless tx.origin is used
    //  use case of checking that the original human sender is authorized (even through non-official contract calls) 
    //  may be one of the few legitimate uses of tx.origin
    // ✓ To register as official smartContract → You need to be called by an official entity
    // ✗ because you can't be an official entity smartContract → Until that smatcontract calls init() via its own constructor 
    //  therefore we must USE tx.origin for initial registration of smartContracts as a bootstrap
    modifier onlySuperOrSCExecutives() {
        bool temp = isOfficialEntityFast(KECCAK_SUPER_ADMIN, tx.origin) ||
                    isOfficialEntityFast(KECCAK_SMART_CONTRACT, msg.sender);
        require(temp, "Unauthorized access");
        _;  // run the code block referencing this modifier
    }


    // to meet the 24K limit, BaseTreasury is now instantiated FIRST - however its concept remains the same - as a BASE class
    // for ElasticTreasuryHUB and ElasticTreasurySPOKE  MasterHub is now an optional, later passed "controller" that must be
    // a separate instantiatedController. This will NOT be used for our needs yet.  In other words, controller is ALWAYS
    // this for now.  initialEntitys are project super entity.  NOT the same as the platform chained entity.
    constructor(
    address projectSuperAddress,
    string memory _platformName,
    address platformTreasuryAddress,
    string memory projectSuperName,
    string memory projectSuperId,
    string memory _projectName,
    string memory _projectId
) Recoverable(address(this)) {
    platformName = _platformName;
    projectName = _projectName;  // Store immutable
    projectId = _projectId;      // Store immutable
    // remoteController = address(this);  // formerly masterHub, controller is ALWAYS this for now. (see remotelyManage)
    // **Reject if projectSuperAddress is 0x0000**
    if (projectSuperAddress == address(0)) {
        revert("Controller: needs at least one Super Entity");
    }
    // Register default admin types
    string[3] memory defaultTypes = ["TreasuryAdmin", "SmartContract","Super"];
    for (uint256 i = 0; i < defaultTypes.length; i++) {
        addAdminType(defaultTypes[i]);
    }
    // Use passed entity name/ID or defaults if empty
    string memory superName = bytes(projectSuperName).length > 0 ? projectSuperName : "Presumed Multi-Sig project officer";
    string memory superID = bytes(projectSuperId).length > 0 ? projectSuperId : "Initial Execs";
    addOfficialEntityNow("Super", projectSuperAddress, superName, superID, NO_VALUE);
    addOfficialEntityNow("TreasuryAdmin", projectSuperAddress, superName, superID, NO_VALUE);
    // Add platform treasury as root chained entity if provided
    if (platformTreasuryAddress != address(0)) {
        addChainedEntity("PLATFORM_CHAIN_TREASURY", address(0), platformTreasuryAddress, 0, 20, 0, 1000, platformName, "Platform Treasury");
    }
    emit TreasuryCreated(address(this), "Controller");
}


    //  caller MUST be a HUB or SPOKE because Controller is now deployed separately.
    //  HUB and spoke are no longer Controllers themselves.  addedBTAdmin is optional, HUB and SPOKE pass address(0)
    function init(address officialSmartContractAddress, string calldata contractName) external onlySuperOrSCExecutives {
        addOfficialEntityNow("SmartContract",officialSmartContractAddress, contractName,"", NO_VALUE);  // may exist, if token, symbol will be added.
    }




    // do not use except for use case as of yet not imagined would need to be called immediately via child constructor
    /* 
    function remotelyManage(address _controller) external onlySuperOrSCExecutives {
        bool isRemotelyManaged = (_controller != address(0) && _controller != address(this));
        if (isRemotelyManaged) {
            remoteController = _controller;
        }
        else {
            remoteController = address(this);  // not used if isRemotelyManaged is false
        }
        changeController(remoteController);
    } */


    // if master is self, it is the same as passing false, because SELF (local) WILL manage in that case.
    // Any controller that is not self must be another instance of BaseTreasury
    // just a formality, if not here,  controller can always be cast to BaseTreasury, even if self.
    //  Ex:  BaseTreasury(controller).someFunction … will always work whether self or another BaseTreasury instance.
    //function doesRemoteControllerManageAllOfficials() internal view returns (bool) {
    //    return controller != address(this);
    //}



    ///
    //   OFFICIAL ENTITIES
    //
    //

        

        // Dynamically validate an admin type, and optionally register new ones
        function validateAdminType(string memory adminType) internal view {
            if (!adminTypeRegistry.isRegistered[adminType]) {
                revert("Invalid admin type");
            }
        }


        function addAdminType(string memory adminType) internal returns (bool) {
            if (!adminTypeRegistry.isRegistered[adminType]) {
                // to support remote case, let a TreasuryAdmin add too, since “SmartContract” is not sensed for remote
                // chained transaction caller:  EOA ⇒ contract A == > contract B (here) ⇒ remote controller (A not sensed)
                // require(isOfficialEntity(“SmartContract”,msg.sender),”Only official SC can add new Officl type”);
                bytes32 hash = keccak256(bytes(adminType));
                adminTypeRegistry.isRegistered[adminType] = true;
                adminTypeRegistry.isHashRegistered[hash] = true;
                adminTypeRegistry.nameByHash[hash] = adminType;
                adminTypeRegistry.registeredAdminTypes.push(adminType);
                return true;
            }
            return false;
        }


        // Retrieve all registered admin types
        function getRegisteredAdminTypes() external view returns (string[] memory) {
            return adminTypeRegistry.registeredAdminTypes;
        }

         // "TreasuryAdmin", initialControllerAdmin, "Presumed Multi-Sig platform exec", "Initial Execs"
       function addOfficialEntity(string memory adminType, address entityAddr, string memory label,string memory nationalIdOrSC) public onlySuper  returns (bool) {
            return addOfficialEntityNow(adminType, entityAddr, label, nationalIdOrSC, NO_VALUE);
        }

        // Add official entity with an associated value
        function addOfficialEntityWithValue(string memory adminType, address entityAddr, string memory label, string memory nationalIdOrSC, uint256 value) public onlySuper returns (bool) {
            require(value <= MAX_VALUE, "Value exceeds maximum");
            return addOfficialEntityNow(adminType, entityAddr, label, nationalIdOrSC, value);
        }

       
       function addOfficialEntityNow(string memory adminType, address entityAddr, string memory label, string memory nationalIdOrSCSymbol, uint256 value) internal returns (bool) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //    // For remote controller, route based on whether value is set
            //    if (value == NO_VALUE) {
            //        return Controller(controller).addOfficialEntity(adminType, entityAddr, label, nationalIdOrSCSymbol);
            //    } else {
            //        return Controller(controller).addOfficialEntityWithValue(adminType, entityAddr, label, nationalIdOrSCSymbol, value);
            //    }
            //}
            require(entityAddr != address(0), "Invalid entity address");
            if (bytes(label).length == 0) {
                if (entityAddr.code.length > 0) {
                    try IERC20SymbolAndName(entityAddr).name() returns (string memory s) {
                        label = bytes(s).length > 0 ? s : "SC not token";
                    } catch {
                        label = "SC not token";
                    }
                }
                else {
                    label = "SC not token";
                }
            }
            if (bytes(nationalIdOrSCSymbol).length == 0) {
                if (entityAddr.code.length > 0) {
                    try IERC20SymbolAndName(entityAddr).symbol() returns (string memory s) {
                        nationalIdOrSCSymbol = bytes(s).length > 0 ? s : "SC not token";
                    } catch {
                        nationalIdOrSCSymbol = "SC not token";
                    }
                } else {
                    nationalIdOrSCSymbol = "no label and SC not contract";
                }
            }
            addAdminType(adminType);
            OfficialEntityGroup storage group = officialEntityGroupsByType[adminType];
            
            // If entity is active, check if we're trying to update the value
            if (group.entitiesByAddress[entityAddr].active) {
                uint256 existingValue = group.entitiesByAddress[entityAddr].value;
                
                // Prevent changing from value to NO_VALUE or vice versa
                // This is a semantic change that requires removal and re-addition
                if ((existingValue == NO_VALUE && value != NO_VALUE) || 
                    (existingValue != NO_VALUE && value == NO_VALUE)) {
                    revert("Cannot change from value to noVal or vice versa. Must remove entity first");
                }
                
                // Allow updating to a different value (both must be real values, not NO_VALUE)
                if (value != NO_VALUE && existingValue != value) {
                    // Value is changing, allow update
                    group.entitiesByAddress[entityAddr].value = value;
                    emit UpdateOfficialsWithValue(entityAddr, msg.sender, adminType, "UpdateValue", label, nationalIdOrSCSymbol, block.timestamp, true, value);
                    return true;
                }
                
                return false;  // Already exists with same value or no value change
            }
            
            group.entitiesByAddress[entityAddr] = OfficialEntityStruct(label, nationalIdOrSCSymbol, entityAddr, block.number, block.timestamp, true, value);
            group.entityList.push(entityAddr);
            
            // Emit appropriate event based on whether value is set
            if (value == NO_VALUE) {
                emit UpdateOfficials(entityAddr, msg.sender, adminType, "Add", label, nationalIdOrSCSymbol, block.timestamp, true);
            } else {
                emit UpdateOfficialsWithValue(entityAddr, msg.sender, adminType, "Add", label, nationalIdOrSCSymbol, block.timestamp, true, value);
            }
            return true;
        }

        function removeOfficialEntity(string calldata adminType, address entityAddr) external onlySuper returns (bool) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //     return Controller(controller).removeOfficialEntity(adminType, entityAddr);
            //}
            require(entityAddr != address(0), "Invalid entity address");
            validateAdminType(adminType);  // ???? Ensure valid type
            OfficialEntityGroup storage group = officialEntityGroupsByType[adminType];
            if (!group.entitiesByAddress[entityAddr].active) {
                return false;
            }
            // Soft-delete by marking inactive, but retain in list for history
            group.entitiesByAddress[entityAddr].active = false;
            emit UpdateOfficials(entityAddr, msg.sender, adminType, "Remove",group.entitiesByAddress[entityAddr].fullNameOfEntityOrLabel,group.entitiesByAddress[entityAddr].nationalIdOfEntity,block.timestamp,false);
            return true;
        }

        function isOfficialEntityFast(bytes32 hashedAdminType, address entityAddr) public view returns (bool) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //     return Controller(controller).isOfficialEntityFast(hashedAdminType, entityAddr);
            //}
            require(entityAddr != address(0), "Invalid entity address");
             string memory adminType = adminTypeRegistry.nameByHash[hashedAdminType];
             if (!adminTypeRegistry.isHashRegistered[hashedAdminType]) {
                revert("Invalid admin type");
            }
            return officialEntityGroupsByType[adminType].entitiesByAddress[entityAddr].active;
        }

        function isOfficialEntity(string memory adminType, address entityAddr) public view returns (bool) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //     return Controller(controller).isOfficialEntity(adminType, entityAddr);
            //}
            require(entityAddr != address(0), "Invalid entity address");
            validateAdminType(adminType);  // ???? Ensure valid type
            return officialEntityGroupsByType[adminType].entitiesByAddress[entityAddr].active;
        }

        function getOfficialEntity(string calldata adminType, address entityAddr) external view returns (OfficialEntityStruct memory) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //     return Controller(controller).getOfficialEntity(adminType, entityAddr);
            //}
            require(entityAddr != address(0), "Invalid entity addrss");
            validateAdminType(adminType);  // ???? Ensure valid type
            OfficialEntityStruct storage entity = officialEntityGroupsByType[adminType].entitiesByAddress[entityAddr];
            require(entity.active, "No Ent or inactve");
            return entity;
        }

        function getAllOfficialEntities(string calldata adminType, bool includeInactive) external view returns (OfficialEntityStruct[] memory) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //    return Controller(controller).getAllOfficialEntities(adminType, includeInactive);
            //}
            validateAdminType(adminType);  // ???? Ensure valid type
            OfficialEntityGroup storage group = officialEntityGroupsByType[adminType];
            // First count entities based on the includeInactive parameter
            uint256 entityCount = 0;
            for (uint256 i = 0; i < group.entityList.length; i++) {
                address entityAddr = group.entityList[i];
                if (includeInactive || group.entitiesByAddress[entityAddr].active) {
                    entityCount++;
                }
            }
            // Then create array of exactly the right size and fill it
            OfficialEntityStruct[] memory filteredEntities = new OfficialEntityStruct[](entityCount);
            uint256 counter = 0;
            for (uint256 i = 0; i < group.entityList.length; i++) {
                address entityAddr = group.entityList[i];
                if (includeInactive || group.entitiesByAddress[entityAddr].active) {
                    filteredEntities[counter] = group.entitiesByAddress[entityAddr];
                    counter++;
                }
            }
            return filteredEntities;
        }


        function isOfficialDoubleEntityFast(bytes32 adminType1, address entityAddr1,bytes32 adminType2, address entityAddr2, bool isAnd) external view returns (bool) {
            if (isAnd) {
                return isOfficialEntityFast(adminType1, entityAddr1) && isOfficialEntityFast(adminType2, entityAddr2);
            } else {
                return isOfficialEntityFast(adminType1, entityAddr1) || isOfficialEntityFast(adminType2, entityAddr2);
            }
        }

        // Fast triple entity check with bytes32
        function isOfficialTripleEntityFast(bytes32 adminType1, address entityAddr1, bytes32 adminType2, address entityAddr2,
            bytes32 adminType3, address entityAddr3, bool isAnd) external view returns (bool) {
            if (isAnd) {
                return isOfficialEntityFast(adminType1, entityAddr1) 
                    && isOfficialEntityFast(adminType2, entityAddr2) 
                    && isOfficialEntityFast(adminType3, entityAddr3);
            } else {
                return isOfficialEntityFast(adminType1, entityAddr1) 
                    || isOfficialEntityFast(adminType2, entityAddr2) 
                    || isOfficialEntityFast(adminType3, entityAddr3);
            }
        }

      function isOfficialQuadrupleEntityFast(bytes32 adminType1, address entityAddr1,bytes32 adminType2, address entityAddr2,bytes32 adminType3, address entityAddr3,
            bytes32 adminType4, address entityAddr4,bool isAnd) external view returns (bool) {
            if (isAnd) {
                return isOfficialEntityFast(adminType1, entityAddr1) 
                    && isOfficialEntityFast(adminType2, entityAddr2)
                    && isOfficialEntityFast(adminType3, entityAddr3) 
                    && isOfficialEntityFast(adminType4, entityAddr4);
            } else {
                return isOfficialEntityFast(adminType1, entityAddr1)
                    || isOfficialEntityFast(adminType2, entityAddr2)
                    || isOfficialEntityFast(adminType3, entityAddr3)
                    || isOfficialEntityFast(adminType4, entityAddr4);
            }
        }

    //
    // END OFFICIAL ENTITIES
    //

    //
    // CHAINED ENTITIES - Hierarchical tree/forest structure for fee management
    //

        /// @notice Add a chained entity to the forest. Only SUPER can add NEW root nodes. 
        ///         Root nodes can update their own value or re-add themselves if previously removed.
        ///         Entities already in the forest can add children under themselves.
        /// @param chainType The type of chain (e.g., "FeeAdmin")
        /// @param parent The parent address (address(0) for root nodes)
        /// @param newEntity The address to add
        /// @param type1 Type of value 1: 0 = 'V' (absolute), 1 = 'P' (percentage in basis points)
        /// @param val1 Value 1 amount (absolute or percentage depending on type1)
        /// @param type2 Type of value 2: 0 = 'V' (absolute), 1 = 'P' (percentage in basis points)
        /// @param val2 Value 2 amount (absolute or percentage depending on type2)
        /// @param entityName Human-readable name of the entity
        /// @param entityID Additional identifier (tax ID, registration #, etc)
        function addChainedEntity(string memory chainType, address parent, address newEntity, uint8 type1, uint256 val1, uint8 type2, uint256 val2, string memory entityName, string memory entityID) public returns (bool) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //    return Controller(controller).addChainedEntity(chainType, parent, newEntity, type1, val1, type2, val2, entityName, entityID);
            //}
            require(newEntity != address(0), "Invalid entity address");
            require(type1 <= 1, "Invalid type1 (must be 0 or 1)");
            require(type2 <= 1, "Invalid type2 (must be 0 or 1)");
            require(val1 <= MAX_VALUE, "Val1 exceeds maximum");
            require(val2 <= MAX_VALUE, "Val2 exceeds maximum");
            
            addAdminType(chainType);
            ChainedEntityGroup storage group = chainedEntityGroupsByType[chainType];
            
            // Determine if caller has permission
            bool isRootNode = (parent == address(0));
            bool isSuperOrSC = isOfficialEntityFast(KECCAK_SUPER_ADMIN, tx.origin) || 
                               isOfficialEntityFast(KECCAK_SMART_CONTRACT, msg.sender);
            bool isCallerInChain = group.entitiesByAddress[msg.sender].active;
            
            // Check if entity already exists
            if (group.entitiesByAddress[newEntity].entityAddress != address(0)) {
                ChainedEntityStruct storage existing = group.entitiesByAddress[newEntity];
                bool isExistingRoot = (existing.parent == address(0));
                
                // Special case: Root node updating own value or reactivating self
                if (isExistingRoot && newEntity == msg.sender && parent == address(0)) {
                    // Root node can always update own value or reactivate self
                    if (existing.active) {
                        // Update value while active
                        existing.type1 = type1;
                        existing.val1 = val1;
                        existing.type2 = type2;
                        existing.val2 = val2;
                        existing.entityName = entityName;
                        existing.entityID = entityID;
                        existing.blockTimestamp = block.timestamp;
                        emit ChainedEntityUpdate(newEntity, parent, msg.sender, chainType, "UpdateValue", type1, val1, type2, val2, true, block.timestamp);
                        return true;
                    } else {
                        // Reactivate self as root
                        existing.active = true;
                        existing.type1 = type1;
                        existing.val1 = val1;
                        existing.type2 = type2;
                        existing.val2 = val2;
                        existing.entityName = entityName;
                        existing.entityID = entityID;
                        existing.blockTimestamp = block.timestamp;
                        emit ChainedEntityUpdate(newEntity, parent, msg.sender, chainType, "ReactivateSelf", type1, val1, type2, val2, true, block.timestamp);
                        return true;
                    }
                }
                
                // Regular reactivation: same parent, entity inactive
                if (existing.parent == parent && !existing.active) {
                    // If non-root trying to re-add with same parent (requires parent or SUPER)
                    require(msg.sender == existing.parent || isSuperOrSC, "Only parent or SUPER can reactivate");
                    
                    existing.active = true;
                    existing.type1 = type1;
                    existing.val1 = val1;
                    existing.type2 = type2;
                    existing.val2 = val2;
                    existing.entityName = entityName;
                    existing.entityID = entityID;
                    existing.blockTimestamp = block.timestamp;
                    emit ChainedEntityUpdate(newEntity, parent, msg.sender, chainType, "Reactivate", type1, val1, type2, val2, true, block.timestamp);
                    return true;
                }
                
                // Already exists and active, or trying to change parent
                return false;
            }
            
            // Adding NEW entity - check permissions
            if (isRootNode) {
                // Only SUPER can add NEW root nodes (not self-updates, those handled above)
                require(isOfficialEntityFast(KECCAK_SUPER_ADMIN, tx.origin), "Only SUPER can add new root nodes");
            } else {
                // Must be: SUPER, official SmartContract, OR already an active entity in the chain
                require(isSuperOrSC || isCallerInChain, "Must be SUPER, SC executive, or active in chain");
                
                // If caller is in chain, they can only add children under themselves
                if (!isSuperOrSC && isCallerInChain) {
                    require(parent == msg.sender, "Non-SUPER entities can only add children under themselves");
                }
                
                // Parent must exist and be active in the chain
                require(group.entitiesByAddress[parent].active, "Parent not in active chain");
            }
            
            // Adding new entity
            if (isRootNode) {
                group.rootNodes.push(newEntity);
            } else {
                // Add to parent's children list
                group.childrenByParent[parent].push(newEntity);
            }
            
            // Create the new entity
            group.entitiesByAddress[newEntity] = ChainedEntityStruct({
                entityAddress: newEntity,
                parent: parent,
                type1: type1,
                val1: val1,
                type2: type2,
                val2: val2,
                active: true,
                blockNumber: block.number,
                blockTimestamp: block.timestamp,
                entityName: entityName,
                entityID: entityID
            });
            
            emit ChainedEntityUpdate(newEntity, parent, msg.sender, chainType, "Add", type1, val1, type2, val2, true, block.timestamp);
            return true;
        }

        /// @notice Remove a chained entity. Only parent can remove children, only SELF can remove root nodes.
        /// @dev Recursively deactivates descendants. If includeSelf=false, only children are removed.
        /// @param chainType The chain type (e.g., "FeeAdmin")
        /// @param entity The entity to process
        /// @param includeSelf If true, removes entity and descendants. If false, only removes descendants.
        function removeChainedEntity(string calldata chainType, address entity, bool includeSelf) external returns (bool) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //    return Controller(controller).removeChainedEntity(chainType, entity, includeSelf);
            //}
            require(entity != address(0), "Invalid entity address");
            validateAdminType(chainType);
            
            ChainedEntityGroup storage group = chainedEntityGroupsByType[chainType];
            ChainedEntityStruct storage entityStruct = group.entitiesByAddress[entity];
            
            require(entityStruct.active, "Entity not active");
            
            // Check permissions - must be SELF or parent
            bool isRootNode = (entityStruct.parent == address(0));
            if (isRootNode) {
                // Only SELF can remove root node or its children
                require(msg.sender == entity, "Only SELF can remove root node");
            } else {
                // Only parent can remove child (or entity itself can remove self/children)
                require(msg.sender == entityStruct.parent || msg.sender == entity, "Only parent or SELF can remove");
            }
            
            // Deactivate entity and/or descendants
            _deactivateChainedEntity(chainType, entity, includeSelf);
            
            return true;
        }

        /// @notice Internal function to recursively deactivate entity and/or descendants
        /// @param chainType The chain type
        /// @param entity The entity to process
        /// @param deactivateSelf If true, deactivates this entity. Always deactivates all descendants.
        function _deactivateChainedEntity(string memory chainType, address entity, bool deactivateSelf) internal {
            ChainedEntityGroup storage group = chainedEntityGroupsByType[chainType];
            ChainedEntityStruct storage entityStruct = group.entitiesByAddress[entity];
            
            if (!entityStruct.active) {
                return; // Already inactive
            }
            
            // Deactivate this entity if requested
            if (deactivateSelf) {
                entityStruct.active = false;
                emit ChainedEntityUpdate(entity, entityStruct.parent, msg.sender, chainType, "Remove", entityStruct.type1, entityStruct.val1, entityStruct.type2, entityStruct.val2, false, block.timestamp);
            }
            
            // Recursively deactivate all children (always)
            address[] storage children = group.childrenByParent[entity];
            for (uint256 i = 0; i < children.length; i++) {
                _deactivateChainedEntity(chainType, children[i], true); // Always deactivate descendants
            }
        }

        /// @notice Check if an address is an active chained entity
        function isChainedEntity(string calldata chainType, address entity) external view returns (bool) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //    return Controller(controller).isChainedEntity(chainType, entity);
            //}
            validateAdminType(chainType);
            return chainedEntityGroupsByType[chainType].entitiesByAddress[entity].active;
        }

        /// @notice Check if an address is an active chained entity (fast version with hash)
        function isChainedEntityFast(bytes32 hashedChainType, address entity) external view returns (bool) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //    return Controller(controller).isChainedEntityFast(hashedChainType, entity);
            //}
            string memory chainType = adminTypeRegistry.nameByHash[hashedChainType];
            require(adminTypeRegistry.isHashRegistered[hashedChainType], "Invalid chain type");
            return chainedEntityGroupsByType[chainType].entitiesByAddress[entity].active;
        }

        /// @notice Get chained entity details including parent and value
        function getChainedEntity(string calldata chainType, address entity) external view returns (ChainedEntityStruct memory) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //    return Controller(controller).getChainedEntity(chainType, entity);
            //}
            validateAdminType(chainType);
            ChainedEntityStruct storage entityStruct = chainedEntityGroupsByType[chainType].entitiesByAddress[entity];
            require(entityStruct.active, "Entity not active");
            return entityStruct;
        }

        /// @notice Get all active chained entities in a forest (for fee distribution)
        /// @return Array of ChainedEntityStruct containing all active entities
        function getActiveChainedEntities(string calldata chainType) external view returns (ChainedEntityStruct[] memory) {
            //if (doesRemoteControllerManageAllOfficials()) {
            //    return Controller(controller).getActiveChainedEntities(chainType);
            //}
            validateAdminType(chainType);
            
            ChainedEntityGroup storage group = chainedEntityGroupsByType[chainType];
            
            // First, count active entities by traversing from root nodes
            uint256 count = 0;
            for (uint256 i = 0; i < group.rootNodes.length; i++) {
                count += _countActiveDescendants(chainType, group.rootNodes[i]);
            }
            
            // Allocate array
            ChainedEntityStruct[] memory entities = new ChainedEntityStruct[](count);
            
            // Fill array
            uint256 index = 0;
            for (uint256 i = 0; i < group.rootNodes.length; i++) {
                index = _collectActiveDescendants(chainType, group.rootNodes[i], entities, index);
            }
            
            return entities;
        }

        /// @notice Internal function to count active descendants recursively
        function _countActiveDescendants(string memory chainType, address entity) internal view returns (uint256) {
            ChainedEntityGroup storage group = chainedEntityGroupsByType[chainType];
            ChainedEntityStruct storage entityStruct = group.entitiesByAddress[entity];
            
            if (!entityStruct.active) {
                return 0;
            }
            
            uint256 count = 1; // Count self
            
            // Count children
            address[] storage children = group.childrenByParent[entity];
            for (uint256 i = 0; i < children.length; i++) {
                count += _countActiveDescendants(chainType, children[i]);
            }
            
            return count;
        }

        /// @notice Internal function to collect active descendants recursively
        function _collectActiveDescendants(string memory chainType, address entity, ChainedEntityStruct[] memory entities, uint256 index) internal view returns (uint256) {
            ChainedEntityGroup storage group = chainedEntityGroupsByType[chainType];
            ChainedEntityStruct storage entityStruct = group.entitiesByAddress[entity];
            
            if (!entityStruct.active) {
                return index;
            }
            
            // Add self (copy to memory)
            entities[index] = ChainedEntityStruct({
                entityAddress: entityStruct.entityAddress,
                parent: entityStruct.parent,
                type1: entityStruct.type1,
                val1: entityStruct.val1,
                type2: entityStruct.type2,
                val2: entityStruct.val2,
                active: entityStruct.active,
                blockNumber: entityStruct.blockNumber,
                blockTimestamp: entityStruct.blockTimestamp,
                entityName: entityStruct.entityName,
                entityID: entityStruct.entityID
            });
            index++;
            
            // Add children
            address[] storage children = group.childrenByParent[entity];
            for (uint256 i = 0; i < children.length; i++) {
                index = _collectActiveDescendants(chainType, children[i], entities, index);
            }
            
            return index;
        }

        /**
         * @notice Calculate chained amounts for hierarchical entities (fees, royalties, etc.)
         * @param chainType The chain type (e.g., "PLATFORM_CHAIN_TREASURER")
         * @param totalVal Base value (0 = NO_VALUE, meaning only absolute values apply)
         * @param isPctRelative If true, percentages are multiplicative (20% of 10% = 2%). If false, additive (20% + 10% = 30%)
         * @param maxPct Maximum cumulative percentage allowed in basis points (typically 10000 = 100%)
         * @param calculateType Which type to calculate: 0 = type1 only, 1 = type2 only, 2 = both (gas optimization)
         * @return calculatedAmounts Array of calculated amounts per entity (address + type1Amount + type2Amount)
         * @return totalType1 Sum of all type1 amounts
         * @return totalType2 Sum of all type2 amounts
         */
        function calculateChainedAmounts(
            string calldata chainType,
            uint256 totalVal,
            bool isPctRelative,
            uint256 maxPct,
            uint8 calculateType
        ) external view returns (
            CalculatedEntityAmount[] memory calculatedAmounts,
            uint256 totalType1,
            uint256 totalType2
        ) {
            // Get active entities (ordered parent-first by depth-first traversal)
            ChainedEntityStruct[] memory chainedEntities = this.getActiveChainedEntities(chainType);
            
            uint256[] memory type1Amounts = new uint256[](chainedEntities.length);
            uint256[] memory type2Amounts = new uint256[](chainedEntities.length);
            
            // Track parent values for percentage calculations (memory arrays)
            uint256[] memory type1ParentVals = new uint256[](chainedEntities.length);
            uint256[] memory type2ParentVals = new uint256[](chainedEntities.length);
            
            uint256 cumulativePct1 = 0;
            uint256 cumulativePct2 = 0;
            
            totalType1 = 0;
            totalType2 = 0;
            
            // Process each entity
            for (uint256 i = 0; i < chainedEntities.length; i++) {
                ChainedEntityStruct memory entity = chainedEntities[i];
                
                // === CALCULATE TYPE1 (if calculateType == 0 or 2) ===
                if (calculateType == 0 || calculateType == 2) {
                    uint256 type1Amt = 0;
                    if (entity.type1 == 0) {
                        // Type 'V' = absolute value
                        type1Amt = entity.val1;
                    } else if (entity.type1 == 1) {
                        // Type 'P' = percentage
                        if (entity.parent == address(0)) {
                            // Root node: percentage of totalVal
                            if (totalVal > 0) {
                                type1Amt = (totalVal * entity.val1) / BASIS_POINTS_DIVISOR;
                            }
                            // If totalVal == 0, type1Amt = 0
                        } else {
                            // Child n

Tags:
ERC20, Multisig, Upgradeable, Multi-Signature, Factory|addr:0xffacfdf52034ae8386741f763858b651e2f4e74b|verified:true|block:23720099|tx:0x462c6bfb556fdb097b63e32c3431fe98b5dbbaf76b9f899f89fb8f932024c1f5|first_check:1762192223

Submitted on: 2025-11-03 18:50:24

Comments

Log in to comment.

No comments yet.