TW
Tripwire
Find contracts. Test them. Review real vulns.

Analysis Run

019d5666-7210-72a7-9067-e58ac94c06d2

failed
Chain ID
1
Address
Block Number
Created
Sat, Apr 4, 2026 2:50 AM
Review Summary

What survived the funnel

Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?

Confirmed
0
Validated
0
Source
Verified
Review Backing
Present
Best current signal
Untrusted DELEGATECALL target reachable
crit 0 high 0 proxy context implementation known
Review Checklist
1. Read the findings table first. Ignore raw jobs until the result looks real.
2. Confirm attacker control, not just the existence of a sink.
3. Confirm money or privilege impact.
4. Keep it if the behavior is real. Kill it if this is normal proxy, admin, or upgrade plumbing.

Processing Jobs

Status & attempts
ID Type Status Attempts Heartbeat
42855 artifact.fetch complete 1 / 3 1 week ago
42856 proxy.resolve complete 1 / 3 1 week ago
42857 tool.cast_disasm complete 1 / 3 1 week ago
42858 analysis.bundle complete 1 / 3 1 week ago
42859 capability.graph complete 1 / 3 1 week ago
42860 detector.run complete 1 / 3 1 week ago
42861 validation.fork complete 1 / 3 1 week ago
42967 report.consolidate complete 1 / 6 1 week ago
42968 tool.slither failed 2 / 2 1 week ago

Artifact

Runtime bytecode: available
Creation TX:
Deployer:
Proxy: yes confirmed
Verified Source: yes
Proxy evidence
{
    "method": "etherscan+onchain",
    "status": "confirmed",
    "etherscan_hint": {
        "source": "etherscan",
        "etherscan": {
            "proxy_flag": true,
            "implementation": "0x29353f77c6b0d3772d73e708cc8e1fca08c80c11"
        }
    },
    "implementation": "0x29353f77c6b0d3772d73e708cc8e1fca08c80c11",
    "onchain_method": "eip1967"
}

Slither

tool.slither
Status
failed slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-1ab33d37) 'forge config --json' running 'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-1ab33d37) 'forge' returned non-zero exit code 1 Error: Encountered invalid solc version in lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol: No solc version exists that matches the version requirement: ^0.8.22 stderr: Encountered invalid solc version in lib/openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol: No solc version exists that matches the version requirement: ^0.8.22 Traceback (most recent call last): File "/var/www/tripwire/.venv/bin/slither", line 10, in <module> sys.exit(main()) ~~~~^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main main_impl(all_detector_classes=detectors, all_printer_classes=printers) ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl ) = process_all(filename, args, detector_classes, printer_classes) ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all compilations = compile_all(target, **vars(args)) File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all compilations.append(CryticCompile(target, **kwargs)) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__ self._compile(**kwargs) ~~~~~~~~~~~~~^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile self._platform.compile(self, **kwargs) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile hardhat_like_parsing( ~~~~~~~~~~~~~~~~~~~~^ crytic_compile, str(self._target), build_directory, str(self._project_root) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing raise InvalidCompilation(txt) crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command? /tmp/slither-1ab33d37/out/build-info is not a directory.
Attempts
2 / 2
Findings
0
Solc
View stderr
Traceback (most recent call last):
  File "/var/www/tripwire/worker/worker.py", line 198, in main
    payload = dispatch_job(job_type, job_id, job_meta, run_data, config, laravel_client)
  File "/var/www/tripwire/worker/worker.py", line 114, in dispatch_job
    return slither_run.run(job_id, job_meta, run_data, config, log, laravel_client)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/worker/jobs/slither_run.py", line 1084, in run
    stdout_text, stderr_text, result_json = _run_slither(
                                            ~~~~~~~~~~~~^
        workspace,
        ^^^^^^^^^^
    ...<4 lines>...
        target_path=target_path,
        ^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/var/www/tripwire/worker/jobs/slither_run.py", line 958, in _run_slither
    raise RuntimeError(f"slither failed (exit {result.returncode}): {stderr_text or stdout_text}")
RuntimeError: slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-1ab33d37)
'forge config --json' running
'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-1ab33d37)
'forge' returned non-zero exit code 1
Error: Encountered invalid solc version in lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol: No solc version exists that matches the version requirement: ^0.8.22
stderr: Encountered invalid solc version in lib/openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol: No solc version exists that matches the version requirement: ^0.8.22
Traceback (most recent call last):
  File "/var/www/tripwire/.venv/bin/slither", line 10, in <module>
    sys.exit(main())
             ~~~~^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main
    main_impl(all_detector_classes=detectors, all_printer_classes=printers)
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl
    ) = process_all(filename, args, detector_classes, printer_classes)
        ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all
    compilations = compile_all(target, **vars(args))
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all
    compilations.append(CryticCompile(target, **kwargs))
                        ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__
    self._compile(**kwargs)
    ~~~~~~~~~~~~~^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile
    self._platform.compile(self, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile
    hardhat_like_parsing(
    ~~~~~~~~~~~~~~~~~~~~^
        crytic_compile, str(self._target), build_directory, str(self._project_root)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing
    raise InvalidCompilation(txt)
crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command?
/tmp/slither-1ab33d37/out/build-info is not a directory.

Codex

report.consolidate
Job Status
complete
Attempts
1 / 6
Findings
1
No findings?
no
Summary
Fee-on-transfer ERC20s are accounted at the requested amount instead of the amount actually received, which can overmint wrapped balances and drain bridge collateral.
View stdout
{"no_findings":false,"summary":"Fee-on-transfer ERC20s are accounted at the requested amount instead of the amount actually received, which can overmint wrapped balances and drain bridge collateral.","findings":[{"severity":"high","category":"other","title":"Fee-on-transfer ERC20s can overmint bridge balances and drain pooled collateral","exploit_scenario":"An attacker bridges a deflationary ERC20 that burns or taxes 10% on transfer. `DepositBoxERC20.depositERC20Direct` records and forwards `amount=100` to the destination chain, then calls `safeTransferFrom` once without checking how many tokens actually arrived. Only 90 tokens reach the box, but 100 wrapped tokens are minted on the schain. The attacker can later exit the full 100 back to mainnet (or another schain), extracting the 10-token shortfall from liquidity supplied by honest users. The same invariant break exists in schain-to-schain transfers of main-chain ERC20s, where `TokenManagerERC20` escrows `amount` before verifying the real balance delta.","recommendation":"Measure balance deltas before and after each ERC20 transfer and use the actual received amount for `transferredAmount` accounting and for the bridged message payload. Reject tokens whose received amount is smaller than the requested amount if exact-amount semantics are required.","evidence":"`DepositBoxERC20.depositERC20Direct` builds the outgoing message and increments `transferredAmount` using the caller-supplied `amount`, then performs a single `safeTransferFrom` without comparing pre/post balances. `TokenManagerERC20._exit` mirrors this pattern for schain-to-schain escrow of main-chain tokens by saving `amount` before `transferFrom` and never validating the received balance.","locations":["contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:691","contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:704","contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:705","contracts/schain/TokenManagers/TokenManagerERC20.sol:340","contracts/schain/TokenManagers/TokenManagerERC20.sol:348","contracts/schain/TokenManagers/TokenManagerERC20.sol:350"]}]}
View stderr
unction mint(address account, uint256 value) external;
}

================================================================================
FILE: @skalenetwork/ima-interfaces/schain/tokens/IERC721OnChain.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   IERC721OnChain - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity >=0.8.19 <0.9.0;


interface IERC721OnChain {
    function setTokenURI(uint256 tokenId, string calldata tokenUri) external returns (bool);
    function mint(address account, uint256 tokenId) external;
}

================================================================================
FILE: @skalenetwork/ima-interfaces/schain/tokens/IEthErc20.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   IEthErc20 - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity >=0.8.19 <0.9.0;


interface IEthErc20 {
    function mint(address account, uint256 amount) external;
    function forceBurn(address account, uint256 amount) external;
    function initialize(address tokenManagerEthAddress) external;
}

================================================================================
FILE: @skalenetwork/skale-manager-interfaces/IContractManager.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    IContractManager.sol - SKALE Manager Interfaces
    Copyright (C) 2021-Present SKALE Labs
    @author Dmytro Stebaeiv

    SKALE Manager Interfaces is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE Manager Interfaces is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity >=0.6.10 <0.9.0;

interface IContractManager {
    /**
     * @dev Emitted when contract is upgraded.
     */
    event ContractUpgraded(string contractsName, address contractsAddress);

    function initialize() external;
    function setContractsAddress(string calldata contractsName, address newContractsAddress) external;
    function contracts(bytes32 nameHash) external view returns (address);
    function getDelegationPeriodManager() external view returns (address);
    function getBounty() external view returns (address);
    function getValidatorService() external view returns (address);
    function getTimeHelpers() external view returns (address);
    function getConstantsHolder() external view returns (address);
    function getSkaleToken() external view returns (address);
    function getTokenState() external view returns (address);
    function getPunisher() external view returns (address);
    function getContract(string calldata name) external view returns (address);
}

================================================================================
FILE: @skalenetwork/skale-manager-interfaces/INodes.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    INodes.sol - SKALE Manager
    Copyright (C) 2018-Present SKALE Labs
    @author Artem Payvin

    SKALE Manager is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE Manager is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity >=0.6.10 <0.9.0;

import "./utils/IRandom.sol";

interface INodes {
    // All Nodes states
    enum NodeStatus {Active, Leaving, Left, In_Maintenance}

    struct Node {
        string name;
        bytes4 ip;
        bytes4 publicIP;
        uint16 port;
        bytes32[2] publicKey;
        uint startBlock;
        uint lastRewardDate;
        uint finishTime;
        NodeStatus status;
        uint validatorId;
    }

    // struct to note which Nodes and which number of Nodes owned by user
    struct CreatedNodes {
        mapping (uint => bool) isNodeExist;
        uint numberOfNodes;
    }

    struct SpaceManaging {
        uint8 freeSpace;
        uint indexInSpaceMap;
    }

    struct NodeCreationParams {
        string name;
        bytes4 ip;
        bytes4 publicIp;
        uint16 port;
        bytes32[2] publicKey;
        uint16 nonce;
        string domainName;
    }
    
    /**
     * @dev Emitted when a node is created.
     */
    event NodeCreated(
        uint nodeIndex,
        address owner,
        string name,
        bytes4 ip,
        bytes4 publicIP,
        uint16 port,
        uint16 nonce,
        string domainName
    );

    /**
     * @dev Emitted when a node completes a network exit.
     */
    event ExitCompleted(
        uint nodeIndex
    );

    /**
     * @dev Emitted when a node begins to exit from the network.
     */
    event ExitInitialized(
        uint nodeIndex,
        uint startLeavingPeriod
    );

    /**
     * @dev Emitted when a node set to in compliant or compliant.
     */
    event IncompliantNode(
        uint indexed nodeIndex,
        bool status
    );

    /**
     * @dev Emitted when a node set to in maintenance or from in maintenance.
     */
    event MaintenanceNode(
        uint indexed nodeIndex,
        bool status
    );

    /**
     * @dev Emitted when a node status changed.
     */
    event IPChanged(
        uint indexed nodeIndex,
        bytes4 previousIP,
        bytes4 newIP
    );
    
    function removeSpaceFromNode(uint nodeIndex, uint8 space) external returns (bool);
    function addSpaceToNode(uint nodeIndex, uint8 space) external;
    function changeNodeLastRewardDate(uint nodeIndex) external;
    function changeNodeFinishTime(uint nodeIndex, uint time) external;
    function createNode(address from, NodeCreationParams calldata params) external;
    function initExit(uint nodeIndex) external;
    function completeExit(uint nodeIndex) external returns (bool);
    function deleteNodeForValidator(uint validatorId, uint nodeIndex) external;
    function checkPossibilityCreatingNode(address nodeAddress) external;
    function checkPossibilityToMaintainNode(uint validatorId, uint nodeIndex) external returns (bool);
    function setNodeInMaintenance(uint nodeIndex) external;
    function removeNodeFromInMaintenance(uint nodeIndex) external;
    function setNodeIncompliant(uint nodeIndex) external;
    function setNodeCompliant(uint nodeIndex) external;
    function setDomainName(uint nodeIndex, string memory domainName) external;
    function makeNodeVisible(uint nodeIndex) external;
    function makeNodeInvisible(uint nodeIndex) external;
    function changeIP(uint nodeIndex, bytes4 newIP, bytes4 newPublicIP) external;
    function numberOfActiveNodes() external view returns (uint);
    function incompliant(uint nodeIndex) external view returns (bool);
    function getRandomNodeWithFreeSpace(
        uint8 freeSpace,
        IRandom.RandomGenerator memory randomGenerator
    )
        external
        view
        returns (uint);
    function isTimeForReward(uint nodeIndex) external view returns (bool);
    function getNodeIP(uint nodeIndex) external view returns (bytes4);
    function getNodeDomainName(uint nodeIndex) external view returns (string memory);
    function getNodePort(uint nodeIndex) external view returns (uint16);
    function getNodePublicKey(uint nodeIndex) external view returns (bytes32[2] memory);
    function getNodeAddress(uint nodeIndex) external view returns (address);
    function getNodeFinishTime(uint nodeIndex) external view returns (uint);
    function isNodeLeft(uint nodeIndex) external view returns (bool);
    function isNodeInMaintenance(uint nodeIndex) external view returns (bool);
    function getNodeLastRewardDate(uint nodeIndex) external view returns (uint);
    function getNodeNextRewardDate(uint nodeIndex) external view returns (uint);
    function getNumberOfNodes() external view returns (uint);
    function getNumberOnlineNodes() external view returns (uint);
    function getActiveNodeIds() external view returns (uint[] memory activeNodeIds);
    function getNodeStatus(uint nodeIndex) external view returns (NodeStatus);
    function getValidatorNodeIndexes(uint validatorId) external view returns (uint[] memory);
    function countNodesWithFreeSpace(uint8 freeSpace) external view returns (uint count);
    function getValidatorId(uint nodeIndex) external view returns (uint);
    function isNodeExist(address from, uint nodeIndex) external view returns (bool);
    function isNodeActive(uint nodeIndex) external view returns (bool);
    function isNodeLeaving(uint nodeIndex) external view returns (bool);
}

================================================================================
FILE: @skalenetwork/skale-manager-interfaces/ISchains.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    ISchains.sol - SKALE Manager Interfaces
    Copyright (C) 2021-Present SKALE Labs
    @author Dmytro Stebaeiv

    SKALE Manager Interfaces is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE Manager Interfaces is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity >=0.6.10 <0.9.0;

interface ISchains {

    struct SchainOption {
        string name;
        bytes value;
    }
    
    /**
     * @dev Emitted when an schain is created.
     */
    event SchainCreated(
        string name,
        address owner,
        uint partOfNode,
        uint lifetime,
        uint numberOfNodes,
        uint deposit,
        uint16 nonce,
        bytes32 schainHash
    );

    /**
     * @dev Emitted when an schain is deleted.
     */
    event SchainDeleted(
        address owner,
        string name,
        bytes32 indexed schainHash
    );

    /**
     * @dev Emitted when a node in an schain is rotated.
     */
    event NodeRotated(
        bytes32 schainHash,
        uint oldNode,
        uint newNode
    );

    /**
     * @dev Emitted when a node is added to an schain.
     */
    event NodeAdded(
        bytes32 schainHash,
        uint newNode
    );

    /**
     * @dev Emitted when a group of nodes is created for an schain.
     */
    event SchainNodes(
        string name,
        bytes32 schainHash,
        uint[] nodesInGroup
    );

    function addSchain(address from, uint deposit, bytes calldata data) external;
    function addSchainByFoundation(
        uint lifetime,
        uint8 typeOfSchain,
        uint16 nonce,
        string calldata name,
        address schainOwner,
        address schainOriginator,
        SchainOption[] calldata options
    )
        external
        payable;
    function deleteSchain(address from, string calldata name) external;
    function deleteSchainByRoot(string calldata name) external;
    function restartSchainCreation(string calldata name) external;
    function verifySchainSignature(
        uint256 signA,
        uint256 signB,
        bytes32 hash,
        uint256 counter,
        uint256 hashA,
        uint256 hashB,
        string calldata schainName
    )
        external
        view
        returns (bool);
    function getSchainPrice(uint typeOfSchain, uint lifetime) external view returns (uint);
    function getOption(bytes32 schainHash, string calldata optionName) external view returns (bytes memory);
    function getOptions(bytes32 schainHash) external view returns (SchainOption[] memory);
}

================================================================================
FILE: @skalenetwork/skale-manager-interfaces/ISchainsInternal.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    ISchainsInternal - SKALE Manager Interfaces
    Copyright (C) 2021-Present SKALE Labs
    @author Dmytro Stebaeiv

    SKALE Manager Interfaces is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE Manager Interfaces is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity >=0.6.10 <0.9.0;

import "./INodes.sol";

interface ISchainsInternal {
    struct Schain {
        string name;
        address owner;
        uint indexInOwnerList;
        uint8 partOfNode;
        uint lifetime;
        uint startDate;
        uint startBlock;
        uint deposit;
        uint64 index;
        uint generation;
        address originator;
    }

    struct SchainType {
        uint8 partOfNode;
        uint numberOfNodes;
    }

    /**
     * @dev Emitted when schain type added.
     */
    event SchainTypeAdded(uint indexed schainType, uint partOfNode, uint numberOfNodes);

    /**
     * @dev Emitted when schain type removed.
     */
    event SchainTypeRemoved(uint indexed schainType);

    function initializeSchain(
        string calldata name,
        address from,
        address originator,
        uint lifetime,
        uint deposit) external;
    function createGroupForSchain(
        bytes32 schainHash,
        uint numberOfNodes,
        uint8 partOfNode
    )
        external
        returns (uint[] memory);
    function changeLifetime(bytes32 schainHash, uint lifetime, uint deposit) external;
    function removeSchain(bytes32 schainHash, address from) external;
    function removeNodeFromSchain(uint nodeIndex, bytes32 schainHash) external;
    function deleteGroup(bytes32 schainHash) external;
    function setException(bytes32 schainHash, uint nodeIndex) external;
    function setNodeInGroup(bytes32 schainHash, uint nodeIndex) external;
    function removeHolesForSchain(bytes32 schainHash) external;
    function addSchainType(uint8 partOfNode, uint numberOfNodes) external;
    function removeSchainType(uint typeOfSchain) external;
    function setNumberOfSchainTypes(uint newNumberOfSchainTypes) external;
    function removeNodeFromAllExceptionSchains(uint nodeIndex) external;
    function removeAllNodesFromSchainExceptions(bytes32 schainHash) external;
    function makeSchainNodesInvisible(bytes32 schainHash) external;
    function makeSchainNodesVisible(bytes32 schainHash) external;
    function newGeneration() external;
    function addSchainForNode(INodes nodes,uint nodeIndex, bytes32 schainHash) external;
    function removeSchainForNode(uint nodeIndex, uint schainIndex) external;
    function removeNodeFromExceptions(bytes32 schainHash, uint nodeIndex) external;
    function isSchainActive(bytes32 schainHash) external view returns (bool);
    function schainsAtSystem(uint index) external view returns (bytes32);
    function numberOfSchains() external view returns (uint64);
    function getSchains() external view returns (bytes32[] memory);
    function getSchainsPartOfNode(bytes32 schainHash) external view returns (uint8);
    function getSchainListSize(address from) external view returns (uint);
    function getSchainHashesByAddress(address from) external view returns (bytes32[] memory);
    function getSchainHashesForNode(uint nodeIndex) external view returns (bytes32[] memory);
    function getSchainOwner(bytes32 schainHash) external view returns (address);
    function getSchainOriginator(bytes32 schainHash) external view returns (address);
    function isSchainNameAvailable(string calldata name) external view returns (bool);
    function isTimeExpired(bytes32 schainHash) external view returns (bool);
    function isOwnerAddress(address from, bytes32 schainHash) external view returns (bool);
    function getSchainName(bytes32 schainHash) external view returns (string memory);
    function getActiveSchain(uint nodeIndex) external view returns (bytes32);
    function getActiveSchains(uint nodeIndex) external view returns (bytes32[] memory activeSchains);
    function getNumberOfNodesInGroup(bytes32 schainHash) external view returns (uint);
    function getNodesInGroup(bytes32 schainHash) external view returns (uint[] memory);
    function isNodeAddressesInGroup(bytes32 schainHash, address sender) external view returns (bool);
    function getNodeIndexInGroup(bytes32 schainHash, uint nodeHash) external view returns (uint);
    function isAnyFreeNode(bytes32 schainHash) external view returns (bool);
    function checkException(bytes32 schainHash, uint nodeIndex) external view returns (bool);
    function checkHoleForSchain(bytes32 schainHash, uint indexOfNode) external view returns (bool);
    function checkSchainOnNode(uint nodeIndex, bytes32 schainHash) external view returns (bool);
    function getSchainType(uint typeOfSchain) external view returns(uint8, uint);
    function getGeneration(bytes32 schainHash) external view returns (uint);
    function isSchainExist(bytes32 schainHash) external view returns (bool);
}

================================================================================
FILE: @skalenetwork/skale-manager-interfaces/IWallets.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    IWallets - SKALE Manager Interfaces
    Copyright (C) 2021-Present SKALE Labs
    @author Dmytro Stebaeiv

    SKALE Manager Interfaces is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE Manager Interfaces is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity >=0.6.10 <0.9.0;

interface IWallets {
    receive() external payable;
    function refundGasByValidator(uint validatorId, address payable spender, uint gasLimit) external;
    function refundGasByValidatorToSchain(uint validatorId, bytes32 schainHash) external;
    function refundGasBySchain(bytes32 schainId, address payable spender, uint spentGas, bool isDebt) external;
    function withdrawFundsFromSchainWallet(address payable schainOwner, bytes32 schainHash) external;
    function withdrawFundsFromValidatorWallet(uint amount) external;
    function rechargeValidatorWallet(uint validatorId) external payable;
    function rechargeSchainWallet(bytes32 schainId) external payable;
    function getSchainBalance(bytes32 schainHash) external view returns (uint);
    function getValidatorBalance(uint validatorId) external view returns (uint);
}

================================================================================
FILE: @skalenetwork/skale-manager-interfaces/utils/IRandom.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    IRandom.sol - SKALE Manager Interfaces
    Copyright (C) 2022-Present SKALE Labs
    @author Dmytro Stebaiev

    SKALE Manager Interfaces is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE Manager Interfaces is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE Manager Interfaces.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity >=0.6.10 <0.9.0;


interface IRandom {
    struct RandomGenerator {
        uint seed;
    }
}

================================================================================

exec
/bin/bash -lc "sed -n '3335,3623p' AUDIT_BUNDLE.txt" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
FILE: contracts/extensions/ERC721ReferenceMintAndMetadataMainnet.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ERC721ReferenceMintAndMetadataMainnet.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@skalenetwork/ima-interfaces/extensions/IERC721ReferenceMintAndMetadataMainnet.sol";

import "../schain/tokens/ERC721OnChain.sol";
import "./interfaces/MessageReceiver.sol";


// This contract runs on the main net and accepts deposits
contract ERC721ReferenceMintAndMetadataMainnet is MessageReceiver, IERC721ReferenceMintAndMetadataMainnet {

    address public immutable erc721ContractOnMainnet;
    address public senderContractOnSchain;
    string public schainName;

    address public immutable owner;

    modifier onlyOwner() {
        require(msg.sender == owner, "Sender is not an owner");
        _;
    }

    constructor(
        address newMessageProxyAddress,
        address newErc721Contract,
        string memory newSchainName
    )
        MessageProxyClient(newMessageProxyAddress)
    {
        require(newErc721Contract != address(0), "ERC721 contract has to be set");
        erc721ContractOnMainnet = newErc721Contract;
        schainName = newSchainName;
        owner = msg.sender;
    }

    function setSenderContractOnSchain(address newSenderContractOnSchain) external override onlyOwner {
        require(newSenderContractOnSchain != address(0), "Sender contract has to be set");
        senderContractOnSchain = newSenderContractOnSchain;
    }

    function postMessage(
        SchainHash schainHash,
        address sender,
        bytes calldata data
    )
        external
        override
        onlyMessageProxy
    {
        require(schainHash == SchainHash.wrap(keccak256(abi.encodePacked(schainName))), "Incorrect name of schain");
        require(sender == senderContractOnSchain, "Incorrect sender contract");
        address to;
        uint256 tokenId;
        string memory tokenURI;
        (to, tokenId, tokenURI) = abi.decode(data, (address, uint256, string));
        ERC721OnChain(erc721ContractOnMainnet).mint(address(this), tokenId);
        require(
            ERC721OnChain(erc721ContractOnMainnet).setTokenURI(tokenId, tokenURI),
            "Token URI was not set"
        );
        ERC721OnChain(erc721ContractOnMainnet).transferFrom(address(this), to, tokenId);
    }
}

================================================================================
FILE: contracts/extensions/ERC721ReferenceMintAndMetadataSchain.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ERC721ReferenceMintAndMetadataSchain.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2019-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@skalenetwork/ima-interfaces/extensions/IERC721ReferenceMintAndMetadataSchain.sol";

import "../schain/tokens/ERC721OnChain.sol";
import "./interfaces/MessageSender.sol";


/**
 * @title Token Manager
 * @dev Runs on SKALE Chains, accepts messages from mainnet, and instructs
 * TokenFactory to create clones.
  When a user exits a SKALE chain, TokenFactory
 * burns tokens.
 */
contract ERC721ReferenceMintAndMetadataSchain is MessageSender, IERC721ReferenceMintAndMetadataSchain {

    address public immutable erc721ContractOnSchain;
    address public immutable receiverContractOnMainnet;

    constructor(
        address newMessageProxyAddress,
        address newErc721ContractOnSchain,
        address newReceiverContractOnMainnet
    )
        MessageProxyClient(newMessageProxyAddress)
    {
        require(newErc721ContractOnSchain != address(0), "ERC721 contract has to be set");
        require(newReceiverContractOnMainnet != address(0), "Receiver contract has to be set");
        erc721ContractOnSchain = newErc721ContractOnSchain;
        receiverContractOnMainnet = newReceiverContractOnMainnet;
    }

    function sendTokenToMainnet(address receiver, uint256 tokenId) external override {
        require(
            ERC721OnChain(erc721ContractOnSchain).getApproved(tokenId) == address(this),
            "Not allowed ERC721 Token"
        );
        ERC721OnChain(erc721ContractOnSchain).transferFrom(msg.sender, address(this), tokenId);
        string memory tokenURI = ERC721OnChain(erc721ContractOnSchain).tokenURI(tokenId);
        ERC721OnChain(erc721ContractOnSchain).burn(tokenId);
        bytes memory data = encodeParams(receiver, tokenId, tokenURI);
        _sendMessage("Mainnet", receiverContractOnMainnet, data);
    }

    function encodeParams(
        address receiver,
        uint256 tokenId,
        string memory tokenURI
    )
        public
        pure
        override
        returns (bytes memory data)
    {
        data = abi.encode(receiver, tokenId, tokenURI);
    }
}

================================================================================
FILE: contracts/extensions/interfaces/MessageProxyClient.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   MessageProxyClient.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "../../MessageProxy.sol";

abstract contract MessageProxyClient {
    MessageProxy public immutable messageProxy;

    modifier onlyMessageProxy() {
        require(msg.sender == address(messageProxy), "Sender is not a message proxy");
        _;
    }

    constructor(address newMessageProxyAddress) {
        messageProxy = MessageProxy(newMessageProxyAddress);
    }
}

================================================================================
FILE: contracts/extensions/interfaces/MessageReceiver.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   MessageReceiver.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@skalenetwork/ima-interfaces/IMessageReceiver.sol";

import "./MessageProxyClient.sol";


// solhint-disable-next-line no-empty-blocks
abstract contract MessageReceiver is MessageProxyClient, IMessageReceiver {}

================================================================================
FILE: contracts/extensions/interfaces/MessageSender.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   MessageSender.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "./MessageProxyClient.sol";

abstract contract MessageSender is MessageProxyClient {

    function _sendMessage(
        string memory targetChainName,
        address targetContract,
        bytes memory data
    ) internal {
        messageProxy.postOutgoingMessage(
            SchainHash.wrap(keccak256(abi.encodePacked(targetChainName))),
            targetContract,
            data
        );
    }
}

================================================================================

exec
/bin/bash -lc "sed -n '10645,14132p' AUDIT_BUNDLE.txt" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
FILE: contracts/schain/bls/FieldOperations.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    FieldOperations.sol - SKALE Manager
    Copyright (C) 2021-Present SKALE Labs

    @author Dmytro Stebaiev

    SKALE Manager is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE Manager is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.8.27;

import "@skalenetwork/ima-interfaces/schain/bls/IFieldOperations.sol";

import "./Precompiled.sol";


/**
 * @title Fp2Operations
 * @dev This library contains operations of field that is an extension by imaginary unit of
 * a field of division remainders of a prime number
 *
 * Element of field is Fp2Point
 *
 * Prime divisor is P
 *
 * Defined operations:
 *
 * - addition
 * - subtraction
 * - scalar multiplication
 * - multiplication
 * - squaring
 * - comparison for equality
 */
library Fp2Operations {

    /**
     * @dev Prime devisor
     */
    uint constant public P = 21888242871839275222246405745257275088696311157297823662689037894645226208583;

    /**
     * @dev Add {value1} to {value2}
     */
    function addFp2(
        IFieldOperations.Fp2Point memory value1,
        IFieldOperations.Fp2Point memory value2
    )
        internal
        pure
        returns (IFieldOperations.Fp2Point memory)
    {
        return IFieldOperations.Fp2Point({ a: addmod(value1.a, value2.a, P), b: addmod(value1.b, value2.b, P) });
    }

    /**
     * @dev Perform scalar multiplication of {value} by {scalar}
     */
    function scalarMulFp2(
        IFieldOperations.Fp2Point memory value,
        uint scalar
    )
        internal
        pure
        returns (IFieldOperations.Fp2Point memory)
    {
        return IFieldOperations.Fp2Point({ a: mulmod(scalar, value.a, P), b: mulmod(scalar, value.b, P) });
    }

    /**
     * @dev Subtract {subtracted} from {diminished}
     */
    function minusFp2(
        IFieldOperations.Fp2Point memory diminished,
        IFieldOperations.Fp2Point memory subtracted
    )
        internal
        pure
        returns (IFieldOperations.Fp2Point memory difference)
    {
        uint p = P;
        if (diminished.a >= subtracted.a) {
            difference.a = addmod(diminished.a, p - subtracted.a, p);
        } else {
            difference.a = (p - addmod(subtracted.a, p - diminished.a, p)) % p;
        }
        if (diminished.b >= subtracted.b) {
            difference.b = addmod(diminished.b, p - subtracted.b, p);
        } else {
            difference.b = (p - addmod(subtracted.b, p - diminished.b, p)) % p;
        }
    }

    /**
     * @dev Multiply {value1} by {value2}
     */
    function mulFp2(
        IFieldOperations.Fp2Point memory value1,
        IFieldOperations.Fp2Point memory value2
    )
        internal
        pure
        returns (IFieldOperations.Fp2Point memory result)
    {
        uint p = P;
        IFieldOperations.Fp2Point memory point = IFieldOperations.Fp2Point({
            a: mulmod(value1.a, value2.a, p),
            b: mulmod(value1.b, value2.b, p)});
        result.a = addmod(
            point.a,
            mulmod(p - 1, point.b, p),
            p);
        result.b = addmod(
            mulmod(
                addmod(value1.a, value1.b, p),
                addmod(value2.a, value2.b, p),
                p),
            p - addmod(point.a, point.b, p),
            p);
    }

    /**
     * @dev Square {value}
     */
    function squaredFp2(
        IFieldOperations.Fp2Point memory value
    )
        internal
        pure
        returns (IFieldOperations.Fp2Point memory)
    {
        uint p = P;
        uint ab = mulmod(value.a, value.b, p);
        uint multiplication = mulmod(addmod(value.a, value.b, p), addmod(value.a, mulmod(p - 1, value.b, p), p), p);
        return IFieldOperations.Fp2Point({ a: multiplication, b: addmod(ab, ab, p) });
    }

    /**
     * @dev Check if {value1} is equal to {value2}
     */
    function isEqual(
        IFieldOperations.Fp2Point memory value1,
        IFieldOperations.Fp2Point memory value2
    )
        internal
        pure
        returns (bool)
    {
        return value1.a == value2.a && value1.b == value2.b;
    }
}

/**
 * @title G1Operations
 * @dev This library contains operations of a group of elements {x, y}
 * where y^2 = x^3 + 3 mod P and (x + iy) is an element of Fp2
 *
 * Element of the group is Fp2Point
 *
 * Prime divisor is Fp2Operations.P
 *
 * A group generator is {1, 2}
 *
 * Defined operations:
 *
 * - check if a point is in the group G1
 * - check if a point is in the field Fp2
 * - for x of Fp calculate -x
 */
library G1Operations {
    using Fp2Operations for IFieldOperations.Fp2Point;

    /**
     * @dev Get G1 group generator
     */
    function getG1Generator() internal pure returns (IFieldOperations.Fp2Point memory) {
        // Current solidity version does not support Constants of non-value type
        // so we implemented this function
        return IFieldOperations.Fp2Point({
            a: 1,
            b: 2
        });
    }

    /**
     * @dev Check if ({x], {y}) is a G1 element
     */
    function isG1Point(uint x, uint y) internal pure returns (bool) {
        uint p = Fp2Operations.P;
        return mulmod(y, y, p) ==
            addmod(mulmod(mulmod(x, x, p), x, p), 3, p);
    }

    /**
     * @dev Check if {point} is a G1 element
     */
    function isG1(IFieldOperations.Fp2Point memory point) internal pure returns (bool) {
        return isG1Point(point.a, point.b);
    }

    /**
     * @dev Check if {point} is a Fp2 element
     */
    function checkRange(IFieldOperations.Fp2Point memory point) internal pure returns (bool) {
        return point.a < Fp2Operations.P && point.b < Fp2Operations.P;
    }

    /**
     * @dev For {y} of Fp calculate -y
     */
    function negate(uint y) internal pure returns (uint) {
        return (Fp2Operations.P - y) % Fp2Operations.P;
    }

}

/**
 * @title G2Operations
 * @dev This library contains operations of a group of elements {x, y}
 * where y^2 = x^3 + TWISTB and x and y are elements of Fp2
 *
 * Element of the group is G2Point
 *
 * Prime divisor is Fp2Operations.P
 * TWISTB is
 * {
 *     19485874751759354771024239261021720505790618469301721065564631296452457478373,
 *     266929791119991161246907387137283842545076965332900288569378510910307636690
 * }
 * A group generator is
 * {
 *     {
 *         10857046999023057135944570762232829481370756359578518086990519993285655852781,
 *         11559732032986387107991004021392285783925812861821192530917403151452391805634
 *     },
 *     {
 *         8495653923123431417604973247489272438418190587263600148770280649306958101930,
 *         4082367875863433681332203403145435568316851327593401208105741076214120093531
 *     }
 * }
 *
 * Defined operations:
 *
 * - check if a point is in the group G2
 * - check if a point is zero element of group G2
 * - comparison for equality
 */
library G2Operations {
    using Fp2Operations for IFieldOperations.Fp2Point;


    /**
     * @dev Get value of TWISTB
     */
    function getTWISTB() internal pure returns (IFieldOperations.Fp2Point memory) {
        // Current solidity version does not support Constants of non-value type
        // so we implemented this function
        return IFieldOperations.Fp2Point({
            a: 19485874751759354771024239261021720505790618469301721065564631296452457478373,
            b: 266929791119991161246907387137283842545076965332900288569378510910307636690
        });
    }

    /**
     * @dev Get G2 group generator
     */
    function getG2Generator() internal pure returns (IFieldOperations.G2Point memory) {
        // Current solidity version does not support Constants of non-value type
        // so we implemented this function
        return IFieldOperations.G2Point({
            x: IFieldOperations.Fp2Point({
                a: 10857046999023057135944570762232829481370756359578518086990519993285655852781,
                b: 11559732032986387107991004021392285783925812861821192530917403151452391805634
            }),
            y: IFieldOperations.Fp2Point({
                a: 8495653923123431417604973247489272438418190587263600148770280649306958101930,
                b: 4082367875863433681332203403145435568316851327593401208105741076214120093531
            })
        });
    }

    /**
     * @dev Get G2 zero element
     */
    function getG2Zero() internal pure returns (IFieldOperations.G2Point memory) {
        // Current solidity version does not support Constants of non-value type
        // so we implemented this function
        return IFieldOperations.G2Point({
            x: IFieldOperations.Fp2Point({
                a: 0,
                b: 0
            }),
            y: IFieldOperations.Fp2Point({
                a: 1,
                b: 0
            })
        });
    }

    /**
     * @dev Check if ({x}, {y}) is an element of G2
     */
    function isG2Point(
        IFieldOperations.Fp2Point memory x,
        IFieldOperations.Fp2Point memory y
    )
        internal
        pure
        returns (bool)
    {
        if (isG2ZeroPoint(x, y)) {
            return true;
        }
        IFieldOperations.Fp2Point memory squaredY = y.squaredFp2();
        IFieldOperations.Fp2Point memory res = squaredY.minusFp2(
                x.squaredFp2().mulFp2(x)
            ).minusFp2(getTWISTB());
        return res.a == 0 && res.b == 0;
    }

    /**
     * @dev Check if {value} is an element of G2
     */
    function isG2(IFieldOperations.G2Point memory value) internal pure returns (bool) {
        return isG2Point(value.x, value.y);
    }

    /**
     * @dev Check if ({x}, {y}) is a zero element of G2
     */
    function isG2ZeroPoint(
        IFieldOperations.Fp2Point memory x,
        IFieldOperations.Fp2Point memory y
    )
        internal
        pure
        returns (bool)
    {
        return x.a == 0 && x.b == 0 && y.a == 1 && y.b == 0;
    }

    /**
     * @dev Check if {value} is a zero element of G2
     */
    function isG2Zero(IFieldOperations.G2Point memory value) internal pure returns (bool) {
        return value.x.a == 0 && value.x.b == 0 && value.y.a == 1 && value.y.b == 0;
    }

    /**
     * @dev Check if {value1} is equal to {value2}
     */
    function isEqual(
        IFieldOperations.G2Point memory value1,
        IFieldOperations.G2Point memory value2
    )
        internal
        pure
        returns (bool)
    {
        return value1.x.isEqual(value2.x) && value1.y.isEqual(value2.y);
    }
}

================================================================================
FILE: contracts/schain/bls/Precompiled.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    Precompiled.sol - SKALE Manager
    Copyright (C) 2021-Present SKALE Labs
    @author Dmytro Stebaiev

    SKALE Manager is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE Manager is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.8.27;

/**
 * @title Precompiled
 * @dev This library is a wrapper to call precompiled contracts
 *
 * Defined calls:
 *
 * - bn256Pairing
 */
library Precompiled {

    /**
     * @dev Calls precompiled contract with address 0x8
     * for elliptic curve pairing operations are required in order to perform zkSNARK verification
     * within the block gas limit.
     * see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md for more details
     */
    function bn256Pairing(
        uint x1,
        uint y1,
        uint a1,
        uint b1,
        uint c1,
        uint d1,
        uint x2,
        uint y2,
        uint a2,
        uint b2,
        uint c2,
        uint d2)
        internal view returns (bool)
    {
        bool success;
        uint[12] memory inputToPairing;
        inputToPairing[0] = x1;
        inputToPairing[1] = y1;
        inputToPairing[2] = a1;
        inputToPairing[3] = b1;
        inputToPairing[4] = c1;
        inputToPairing[5] = d1;
        inputToPairing[6] = x2;
        inputToPairing[7] = y2;
        inputToPairing[8] = a2;
        inputToPairing[9] = b2;
        inputToPairing[10] = c2;
        inputToPairing[11] = d2;
        uint[1] memory out;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(not(0), 8, inputToPairing, mul(12, 0x20), out, 0x20)
        }
        require(success, "Pairing check failed");
        return out[0] != 0;
    }
}

================================================================================
FILE: contracts/schain/bls/SkaleVerifier.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    SkaleVerifier.sol - SKALE Manager
    Copyright (C) 2021-Present SKALE Labs
    @author Dmytro Stebaiev

    SKALE Manager is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE Manager is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE Manager.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.8.27;

import "./Precompiled.sol";
import "./FieldOperations.sol";

/**
 * @title SkaleVerifier
 * @dev Contains verify function to perform BLS signature verification.
 */
library SkaleVerifier {
    using Fp2Operations for IFieldOperations.Fp2Point;


    /**
    * @dev Verifies a BLS signature.
    *
    * Requirements:
    *
    * - Signature is in G1.
    * - Hash is in G1.
    * - G2.one in G2.
    * - Public Key in G2.
    */
    function verify(
        IFieldOperations.Fp2Point memory signature,
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB,
        IFieldOperations.G2Point memory publicKey
    )
        internal
        view
        returns (bool)
    {
        require(G1Operations.checkRange(signature), "Signature is not valid");
        if (!_checkHashToGroupWithHelper(
            hash,
            counter,
            hashA,
            hashB
            )
        )
        {
            return false;
        }

        uint newSignB = G1Operations.negate(signature.b);
        require(G1Operations.isG1Point(signature.a, newSignB), "Sign not in G1");
        require(G1Operations.isG1Point(hashA, hashB), "Hash not in G1");

        IFieldOperations.G2Point memory g2 = G2Operations.getG2Generator();
        require(
            G2Operations.isG2(publicKey),
            "Public Key not in G2"
        );

        return Precompiled.bn256Pairing(
            signature.a, newSignB,
            g2.x.b, g2.x.a, g2.y.b, g2.y.a,
            hashA, hashB,
            publicKey.x.b, publicKey.x.a, publicKey.y.b, publicKey.y.a
        );
    }

    function _checkHashToGroupWithHelper(
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB
    )
        private
        pure
        returns (bool)
    {
        if (counter > 100) {
            return false;
        }
        uint xCoordinate = uint(hash) % Fp2Operations.P;
        xCoordinate = (xCoordinate + counter) % Fp2Operations.P;

        uint ySquared = addmod(
            mulmod(mulmod(xCoordinate, xCoordinate, Fp2Operations.P), xCoordinate, Fp2Operations.P),
            3,
            Fp2Operations.P
        );
        if (hashB < Fp2Operations.P / 2 || mulmod(hashB, hashB, Fp2Operations.P) != ySquared || xCoordinate != hashA) {
            return false;
        }

        return true;
    }
}

================================================================================
FILE: contracts/schain/tokens/ERC1155OnChain.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ERC1155OnChain.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@skalenetwork/ima-interfaces/schain/tokens/IERC1155OnChain.sol";


/**
 * @title ERC1155OnChain
 * @dev ERC1155 token that is used as an automatically deployed clone of ERC1155 on mainnet.
 */
contract ERC1155OnChain is AccessControlEnumerableUpgradeable, ERC1155BurnableUpgradeable, IERC1155OnChain {

    /**
     * @dev id of a role that allows token minting.
     */
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor(
        string memory uri
    ) initializer
    {
        AccessControlEnumerableUpgradeable.__AccessControlEnumerable_init();
        ERC1155Upgradeable.__ERC1155_init(uri);
        ERC1155BurnableUpgradeable.__ERC1155Burnable_init();

        _setRoleAdmin(MINTER_ROLE, MINTER_ROLE);
        _setupRole(MINTER_ROLE, _msgSender());
    }

    /**
     * @dev Mint tokens.
     *
     * Requirements:
     *
     * - sender must be granted with {MINTER_ROLE}.
     */
    function mint(
        address account,
        uint256 id,
        uint256 amount,
        bytes memory data
    )
        external
        override
    {
        require(hasRole(MINTER_ROLE, _msgSender()), "Sender is not a Minter");
        _mint(account, id, amount, data);
    }

    /**
     * @dev Mint batch of tokens.
     *
     * Requirements:
     *
     * - sender must be granted with {MINTER_ROLE}.
     */
    function mintBatch(
        address account,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    )
        external
        override
    {
        require(hasRole(MINTER_ROLE, _msgSender()), "Sender is not a Minter");
        _mintBatch(account, ids, amounts, data);
    }

    /**
     * @dev Check if contract support {interfaceId} interface.
     *
     * See https://eips.ethereum.org/EIPS/eip-165 for more details.
     */
    function supportsInterface(
        bytes4 interfaceId
    )
        public
        view
        override(AccessControlEnumerableUpgradeable, ERC1155Upgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

================================================================================
FILE: contracts/schain/tokens/ERC20OnChain.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ERC20OnChain.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@skalenetwork/ima-interfaces/schain/tokens/IERC20OnChain.sol";


/**
 * @title ERC20OnChain
 * @dev ERC20 token that is used as an automatically deployed clone of ERC20 on mainnet.
 */
contract ERC20OnChain is AccessControlEnumerableUpgradeable, ERC20BurnableUpgradeable, IERC20OnChain {

    /**
     * @dev id of a role that allows token minting.
     */
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor(
        string memory contractName,
        string memory contractSymbol
    ) initializer
    {
        AccessControlEnumerableUpgradeable.__AccessControlEnumerable_init();
        ERC20Upgradeable.__ERC20_init(contractName, contractSymbol);
        ERC20BurnableUpgradeable.__ERC20Burnable_init();
        _setRoleAdmin(MINTER_ROLE, MINTER_ROLE);
        _setupRole(MINTER_ROLE, _msgSender());
    }

    /**
     * @dev Mint tokens.
     *
     * Requirements:
     *
     * - sender must be granted with {MINTER_ROLE}.
     */
    function mint(address account, uint256 value) external override {
        require(hasRole(MINTER_ROLE, _msgSender()), "Sender is not a Minter");
        _mint(account, value);
    }
}

================================================================================
FILE: contracts/schain/tokens/ERC721OnChain.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ERC721OnChain.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@skalenetwork/ima-interfaces/schain/tokens/IERC721OnChain.sol";


/**
 * @title ERC721OnChain
 * @dev ERC721 token that is used as an automatically deployed clone of ERC721 on mainnet.
 */
contract ERC721OnChain is
    AccessControlEnumerableUpgradeable,
    ERC721BurnableUpgradeable,
    ERC721URIStorageUpgradeable,
    IERC721OnChain
{

    /**
     * @dev id of a role that allows token minting.
     */
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor(
        string memory contractName,
        string memory contractSymbol
    ) initializer
    {
        AccessControlEnumerableUpgradeable.__AccessControlEnumerable_init();
        ERC721Upgradeable.__ERC721_init(contractName, contractSymbol);
        ERC721BurnableUpgradeable.__ERC721Burnable_init();
        _setRoleAdmin(MINTER_ROLE, MINTER_ROLE);
        _setupRole(MINTER_ROLE, _msgSender());
    }

    /**
     * @dev Set URI of ERC721 token.
     *
     * Requirements:
     *
     * - token with {tokenId} must exist.
     * - sender must be the token owner or approved for the token.
     */
    function setTokenURI(uint256 tokenId, string calldata tokenUri)
        external
        override
        returns (bool)
    {
        require(_exists(tokenId), "Token does not exists");
        require(
            _isApprovedOrOwner(msg.sender, tokenId) ||
            hasRole(MINTER_ROLE, _msgSender()),
            "Sender can not set token URI"
        );
        _setTokenURI(tokenId, tokenUri);
        return true;
    }

    /**
     * @dev Mint token.
     *
     * Requirements:
     *
     * - sender must be granted with {MINTER_ROLE}.
     */
    function mint(address account, uint256 tokenId)
        external
        override
    {
        require(hasRole(MINTER_ROLE, _msgSender()), "Sender is not a Minter");
        _mint(account, tokenId);
    }

    /**
     * @dev Check if contract support {interfaceId} interface.
     *
     * See https://eips.ethereum.org/EIPS/eip-165 for more details.
     */
    function supportsInterface(
        bytes4 interfaceId
    )
        public
        view
        override(AccessControlEnumerableUpgradeable, ERC721Upgradeable, ERC721URIStorageUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    /**
     * @dev Get token URI.
     */
    function tokenURI(
        uint256 tokenId
    )
        public
        view
        override (ERC721Upgradeable, ERC721URIStorageUpgradeable)
        returns (string memory)
    {
        return ERC721URIStorageUpgradeable.tokenURI(tokenId);
    }

    // private

    /**
     * @dev Burn {tokenId}.
     */
    function _burn(uint256 tokenId) internal override (ERC721Upgradeable, ERC721URIStorageUpgradeable) {
        ERC721URIStorageUpgradeable._burn(tokenId);
    }
}

================================================================================
FILE: contracts/schain/tokens/EthErc20.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   EthErc20.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2019-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "@skalenetwork/ima-interfaces/schain/tokens/IEthErc20.sol";


/**
 * @title EthErc20
 * @dev ERC20 token that represents ETH on mainnet.
 */
contract EthErc20 is AccessControlEnumerableUpgradeable, ERC20BurnableUpgradeable, IEthErc20 {

    /**
     * @dev id of a role that allows token minting.
     */
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    /**
     * @dev id of a role that allows token burning.
     */
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    /**
     * @dev Mint tokens.
     *
     * Requirements:
     *
     * - sender must be granted with {MINTER_ROLE}.
     */
    function mint(address account, uint256 amount) external override {
        require(hasRole(MINTER_ROLE, _msgSender()), "MINTER role is required");
        _mint(account, amount);
    }

    /**
     * @dev Burn tokens for any account.
     *
     * Requirements:
     *
     * - sender must be granted with {BURNER_ROLE}.
     */
    function forceBurn(address account, uint256 amount) external override {
        require(hasRole(BURNER_ROLE, _msgSender()), "BURNER role is required");
        _burn(account, amount);
    }

    /**
     * @dev Is called once during contract deployment.
     */
    function initialize(address tokenManagerEthAddress)
        external
        override
        initializer
    {
        AccessControlEnumerableUpgradeable.__AccessControlEnumerable_init();
        ERC20Upgradeable.__ERC20_init("ERC20 Ether Clone", "ETHC");
        ERC20BurnableUpgradeable.__ERC20Burnable_init();
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(MINTER_ROLE, tokenManagerEthAddress);
        _setupRole(BURNER_ROLE, tokenManagerEthAddress);
    }
}

================================================================================
FILE: contracts/test/ConfigReader.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ConfigReader.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;


library ConfigReader {
    uint256 public constant FREE_MEM_PTR = 0x40;
    uint256 public constant FN_NUM_LOG_TEXT_MESSAGE = 0x12;
    uint256 public constant FN_NUM_GET_CONFIG_VARIABLE_UINT256 = 0x13;
    uint256 public constant FN_NUM_GET_CONFIG_VARIABLE_ADDRESS = 0x14;
    uint256 public constant FN_NUM_GET_CONFIG_VARIABLE_STRING = 0x15;
    uint256 public constant FN_NUM_RESERVED = 0x16;
    uint256 public constant FN_NUM_GET_CONFIG_PERMISSION_FLAG = 0x17;

    function getConfigVariableUint256(
        string memory strConfigVariableName
    )
        external
        view
        returns ( uint256 rv )
    {
        uint256 fmp = FREE_MEM_PTR;
        uint256 blocks = (bytes(strConfigVariableName).length + 31) / 32 + 1;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(fmp)
            for { let i := 0 } lt( i, blocks ) { i := add(1, i) } {
                let where := add(ptr, mul(32, i))
                let what := mload(add(strConfigVariableName, mul(32, i)))
                mstore(where, what)
            }
            success := staticcall(not(0), FN_NUM_GET_CONFIG_VARIABLE_UINT256, ptr, mul( blocks, 32 ), ptr, 32)
            rv := mload(ptr)
        }
        require(success, "Get config uint256 failed");
    }

    function getConfigVariableAddress(
        string memory strConfigVariableName
    )
        external
        view
        returns ( address rv )
    {
        uint256 fmp = FREE_MEM_PTR;
        uint256 blocks = (bytes(strConfigVariableName).length + 31) / 32 + 1;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(fmp)
            for { let i := 0 } lt( i, blocks ) { i := add(1, i) } {
                let where := add(ptr, mul(32, i))
                let what := mload(add(strConfigVariableName, mul(32, i)))
                mstore(where, what)
            }
            success := staticcall(not(0), FN_NUM_GET_CONFIG_VARIABLE_ADDRESS, ptr, mul( blocks, 32 ), ptr, 32)
            rv := mload(ptr)
        }
        require(success, "Get config address failed");
    }

    function getConfigVariableString( string memory strConfigVariableName ) public view returns ( string memory rv ) {
        uint256 fmp = FREE_MEM_PTR;
        uint256 blocks = (bytes(strConfigVariableName).length + 31) / 32 + 1;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(fmp)
            for { let i := 0 } lt( i, blocks ) { i := add(1, i) } {
                let where := add(ptr, mul(32, i))
                let what := mload(add(strConfigVariableName, mul(32, i)))
                mstore(where, what)
            }
            success := staticcall(
                not(0),
                FN_NUM_GET_CONFIG_VARIABLE_STRING,
                ptr,
                mul( blocks, 32 ),
                rv,
                mul( 1024, 1024 )
            )
        }
        require(success, "Get config string failed");
    }

    function getConfigPermissionFlag(
        address a,
        string memory strConfigVariableName
    )
        public
        view
        returns (uint256 rv)
    {
        uint256 fmp = FREE_MEM_PTR;
        uint256 fnc = FN_NUM_GET_CONFIG_PERMISSION_FLAG;
        uint256 blocks = (bytes(strConfigVariableName).length + 31) / 32 + 1;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let p := mload(fmp)
            mstore(p, a)
            let ptr := add(p, 32)
            for { let i := 0 } lt( i, blocks ) { i := add(1, i) } {
                let where := add(ptr, mul(32, i))
                let what := mload(add(strConfigVariableName, mul(32, i)))
                mstore(where, what)
            }
            success := staticcall(not(0), fnc, p, add(64, mul(blocks, 32) ), p, 32)
            rv := mload(ptr)
        }
        require(success, "Get config permission failed");
    }
}

================================================================================
FILE: contracts/test/EtherbaseMock.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   EtherbaseMock.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@skalenetwork/etherbase-interfaces/IEtherbaseUpgradeable.sol";


contract EtherbaseMock is IEtherbaseUpgradeable, AccessControlEnumerableUpgradeable {
    bytes32 public constant override ETHER_MANAGER_ROLE = keccak256("ETHER_MANAGER_ROLE");

    event EtherReceived(
        address sender,
        uint amount
    );

    event EtherSent(
        address receiver,
        uint amount
    );

    modifier onlyEtherManager() {
        require(hasRole(ETHER_MANAGER_ROLE, msg.sender), "ETHER_MANAGER_ROLE is required");
        _;
    }

    receive() external payable override {
        emit EtherReceived(msg.sender, msg.value);
    }

    function retrieve(address payable receiver) external override onlyEtherManager {
        partiallyRetrieve(receiver, address(this).balance);
    }

    function initialize(address schainOwner) external initializer override
    {
        AccessControlUpgradeable.__AccessControl_init();
        _setupRole(DEFAULT_ADMIN_ROLE, schainOwner);
        _setupRole(ETHER_MANAGER_ROLE, schainOwner);
    }

    function partiallyRetrieve(address payable receiver, uint amount) public override onlyEtherManager {
        require(receiver != address(0), "Receiver address is not set");
        require(amount <= address(this).balance, "Insufficient funds");

        emit EtherSent(receiver, amount);

        receiver.transfer(amount);
    }
}

================================================================================
FILE: contracts/test/FallbackEthTester.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   FallbackEthTester.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2022-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;

import "@skalenetwork/ima-interfaces/mainnet/DepositBoxes/IDepositBoxEth.sol";
import "@skalenetwork/ima-interfaces/mainnet/ICommunityPool.sol";

interface IFallbackEthTester {
    receive() external payable;
    function deposit() external payable;
    function rechargeUserWallet() external payable;
    function getMyEth() external;
}


contract FallbackEthTester is IFallbackEthTester {
    IDepositBoxEth public depositBoxEth;
    ICommunityPool public communityPool;

    string public schainName;

    bool private _receiveInProgress;
    bool private _getMyEthInProgress;

    constructor(
        IDepositBoxEth newDepositBoxEth,
        ICommunityPool newCommunityPool,
        string memory newSchainName
    ) {
        depositBoxEth = newDepositBoxEth;
        communityPool = newCommunityPool;
        schainName = newSchainName;
    }

    receive() external payable override {
        if (!_receiveInProgress && !_getMyEthInProgress) {
            _receiveInProgress = true;
            uint256 balance = communityPool.getBalance(address(this), schainName);
            communityPool.withdrawFunds(schainName, balance);
            _receiveInProgress = false;
        }
    }

    function deposit() external payable override {
        depositBoxEth.deposit{value: msg.value}(schainName);
    }

    function rechargeUserWallet() external payable override {
        communityPool.rechargeUserWallet{value: msg.value}(schainName, address(this));
    }

    function getMyEth() external override {
        _getMyEthInProgress = true;
        depositBoxEth.getMyEth();
        _getMyEthInProgress = false;
    }
}

================================================================================
FILE: contracts/test/KeyStorageMock.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   KeyStorageMock.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "../schain/KeyStorage.sol";


interface IKeyStorageMock is IKeyStorage {
    function setBlsCommonPublicKey(IFieldOperations.G2Point calldata key) external;
    function setBlsCommonPublicKeyForSchain(bytes32 schainHash, IFieldOperations.G2Point calldata key) external;
    function getBlsCommonPublicKeyForSchain(bytes32 schainHash) external view returns (IFieldOperations.G2Point memory);
}


contract KeyStorageMock is KeyStorage, IKeyStorageMock {

    IFieldOperations.G2Point public blsCommonPublicKey;
    mapping (bytes32 => IFieldOperations.G2Point) public blsCommonPublicKeys;
    string public hello = "Hello";

    function setBlsCommonPublicKey(IFieldOperations.G2Point calldata key) external override {
        // TODO: remove when update compiler will be updated
        IFieldOperations.G2Point memory _key = key;
        blsCommonPublicKey = _key;
    }

    function setBlsCommonPublicKeyForSchain(
        bytes32 schainHash,
        IFieldOperations.G2Point calldata key
    )
        external
        override
    {
        // TODO: remove when update compiler will be updated
        IFieldOperations.G2Point memory _key = key;
        blsCommonPublicKeys[schainHash] = _key;
    }

    function getBlsCommonPublicKey()
        external
        view
        override(IKeyStorage, KeyStorage)
        returns (IFieldOperations.G2Point memory)
    {
        require(
            !(blsCommonPublicKey.x.a == 0 &&
              blsCommonPublicKey.x.b == 0 &&
              blsCommonPublicKey.y.a == 0 &&
              blsCommonPublicKey.y.b == 0),
            "BLS common public key is not set in the mock"
        );
        return blsCommonPublicKey;
    }

    function getBlsCommonPublicKeyForSchain(
        bytes32 schainHash
    )
        external
        view
        override
        returns (IFieldOperations.G2Point memory)
    {
        IFieldOperations.G2Point memory key = blsCommonPublicKeys[schainHash];
        require(
            !(key.x.a == 0 &&
              key.x.b == 0 &&
              key.y.a == 0 &&
              key.y.b == 0),
            "BLS common public key is not set in the mock"
        );
        return key;
    }
}


================================================================================
FILE: contracts/test/Logger.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   Logger.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;


library Logger {

    uint256 public constant FREE_MEM_PTR = 0x40;
    uint256 public constant FN_NUM_LOG_TEXT_MESSAGE = 0x12;

    function logTextMessage( uint256 messageType, string memory strTextMessage ) public view returns ( uint256 rv ) {
        uint256 fmp = FREE_MEM_PTR;
        uint256 fnc = FN_NUM_LOG_TEXT_MESSAGE;
        address who = msg.sender;
        uint256 blocks = (bytes(strTextMessage).length + 31) / 32 + 1;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let p := mload(fmp)
            let ptr := p
            // who
            mstore(ptr, who)
            ptr := add(ptr, 32)
            // type
            mstore(ptr, messageType )
            ptr := add(ptr, 32)
            // message
            for { let i := 0 } lt( i, blocks ) { i := add(1, i) } {
                let where := add(ptr, mul(32, i))
                let what := mload(add( strTextMessage, mul(32, i)))
                mstore(where, what)
            }
            rv := staticcall(not(0), fnc, p, add( 64, mul(blocks, 32) ), p, 32)
        }
    }

    function logMessage( string memory strMessage ) public view returns  (uint256 rv) {
        rv = logTextMessage(0, strMessage);
    }

    function logDebug  ( string memory strMessage ) public view returns  (uint256 rv) {
        rv = logTextMessage(1, strMessage);
    }

    function logTrace  ( string memory strMessage ) public view returns  (uint256 rv) {
        rv = logTextMessage(2, strMessage);
    }

    function logWarning( string memory strMessage ) public view returns  (uint256 rv) {
        rv = logTextMessage(3, strMessage);
    }

    function logError  ( string memory strMessage ) public view returns  (uint256 rv) {
        rv = logTextMessage(4, strMessage);
    }

    function logFatal  ( string memory strMessage ) public view returns  (uint256 rv) {
        rv = logTextMessage(5, strMessage);
    }
}

================================================================================
FILE: contracts/test/MessageProxyCaller.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   MessageProxyCaller.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "../mainnet/MessageProxyForMainnet.sol";
import "../schain/MessageProxyForSchain.sol";


interface IMessageProxyCaller {
    function postOutgoingMessageTester(
        MessageProxyForMainnet messageProxyForMainnet,
        SchainHash targetChainHash,
        address targetContract,
        bytes calldata data
    ) external;
    function postOutgoingMessageTesterOnSchain(
        MessageProxyForSchain messageProxyForSchain,
        SchainHash targetChainHash,
        address targetContract,
        bytes calldata data
    ) external;
}


contract MessageProxyCaller is IMessageProxyCaller {

    function postOutgoingMessageTester(
        MessageProxyForMainnet messageProxyForMainnet,
        SchainHash targetChainHash,
        address targetContract,
        bytes calldata data
    )
        external
        override
    {
        messageProxyForMainnet.postOutgoingMessage(targetChainHash, targetContract, data);
    }

    function postOutgoingMessageTesterOnSchain(
        MessageProxyForSchain messageProxyForSchain,
        SchainHash targetChainHash,
        address targetContract,
        bytes calldata data
    )
        external
        override
    {
        messageProxyForSchain.postOutgoingMessage(targetChainHash, targetContract, data);
    }
}

================================================================================
FILE: contracts/test/MessageProxyForMainnetTester.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   MessageProxyForSchainTester.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "../mainnet/MessageProxyForMainnet.sol";


interface IMessageProxyForMainnetTester {
    function refundGasByUser(
        SchainHash fromSchainHash,
        address payable node,
        address user,
        uint256 gas
    ) external;
}


contract MessageProxyForMainnetTester is MessageProxyForMainnet, IMessageProxyForMainnetTester {

    function refundGasByUser(
        SchainHash fromSchainHash,
        address payable node,
        address user,
        uint256 gas
    )
        external
        override
    {
        communityPool.refundGasByUser(fromSchainHash, node, user, gas);
    }
}

================================================================================
FILE: contracts/test/MessageProxyForSchainTester.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   MessageProxyForSchainTester.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "../schain/MessageProxyForSchain.sol";


interface IMessageProxyForSchainTester {
    function postMessage(
        IMessageReceiver targetContract,
        SchainHash fromSchainHash,
        address sender,
        bytes calldata data
    )
    external;
    function postOutgoingMessageTester(
        MessageProxyForSchain targetContract,
        SchainHash targetChainHash,
        address dstContract,
        bytes calldata data
    )
    external;
    function setEtherbase(IEtherbaseUpgradeable etherbaseAddress) external;
}


contract MessageProxyForSchainTester is MessageProxyForSchain, IMessageProxyForSchainTester {

    IEtherbaseUpgradeable public etherbase = ETHERBASE;

    constructor(IKeyStorage _keyStorage, string memory schainName) {
        MessageProxyForSchain.initialize(_keyStorage, schainName);
    }

    function postMessage(
        IMessageReceiver targetContract,
        SchainHash fromSchainHash,
        address sender,
        bytes calldata data
    )
    external
    override
    {
        targetContract.postMessage(fromSchainHash, sender, data);
    }

    function postOutgoingMessageTester(
        MessageProxyForSchain targetContract,
        SchainHash targetChainHash,
        address dstContract,
        bytes calldata data
    )
    external
    override
    {
        targetContract.postOutgoingMessage(targetChainHash, dstContract, data);
    }

    function setEtherbase(IEtherbaseUpgradeable etherbaseAddress) external override {
        etherbase = etherbaseAddress;
    }

    function _getEtherbase() internal view override returns (IEtherbaseUpgradeable) {
        return etherbase;
    }
}

================================================================================
FILE: contracts/test/MessageProxyForSchainWithoutSignature.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   MessageProxyForSchainWithoutSignature.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "./MessageProxyForSchainTester.sol";

contract MessageProxyForSchainWithoutSignature is MessageProxyForSchainTester {

    constructor(string memory schainName)
        MessageProxyForSchainTester(IKeyStorage(address(0)), schainName)
    // solhint-disable-next-line no-empty-blocks
    {}

    function _verifyMessages(
        bytes32,
        Signature calldata
    )
        internal
        pure
        override
        returns (bool)
    {
        return true;
    }
}

================================================================================
FILE: contracts/test/MessagesTester.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   MessagesTester.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;

import "../Messages.sol";


interface IMessagesTester {
    function encodeTransferEthMessage(address receiver, uint256 amount) external pure returns (bytes memory);
    function encodeTransferErc20Message(
        address token,
        address receiver,
        uint256 amount
    ) external pure returns (bytes memory);
    function encodeTransferErc20AndTotalSupplyMessage(
        address token,
        address receiver,
        uint256 amount,
        uint256 totalSupply
    ) external pure returns (bytes memory);
    function encodeTransferErc20AndTokenInfoMessage(
        address token,
        address receiver,
        uint256 amount,
        uint256 totalSupply,
        Messages.Erc20TokenInfo memory tokenInfo
    ) external pure returns (bytes memory);
    function encodeTransferErc721Message(
        address token,
        address receiver,
        uint256 tokenId
    ) external pure returns (bytes memory);
    function encodeTransferErc721AndTokenInfoMessage(
        address token,
        address receiver,
        uint256 tokenId,
        Messages.Erc721TokenInfo memory tokenInfo
    ) external pure returns (bytes memory);
    function encodeTransferErc721MessageWithMetadata(
        address token,
        address receiver,
        uint256 tokenId,
        string memory tokenURI
    ) external pure returns (bytes memory);
    function encodeTransferErc721WithMetadataAndTokenInfoMessage(
        address token,
        address receiver,
        uint256 tokenId,
        string memory tokenURI,
        Messages.Erc721TokenInfo memory tokenInfo
    ) external pure returns (bytes memory);
    function encodeActivateUserMessage(address receiver) external pure returns (bytes memory);
    function encodeLockUserMessage(address receiver) external pure returns (bytes memory);
    function encodeInterchainConnectionMessage(bool isAllowed) external pure returns (bytes memory);
    function encodeTransferErc1155Message(
        address token,
        address receiver,
        uint256 id,
        uint256 amount
    ) external pure returns (bytes memory);
    function encodeTransferErc1155AndTokenInfoMessage(
        address token,
        address receiver,
        uint256 id,
        uint256 amount,
        Messages.Erc1155TokenInfo memory tokenInfo
    ) external pure returns (bytes memory);
    function encodeTransferErc1155BatchMessage(
        address token,
        address receiver,
        uint256[] memory ids,
        uint256[] memory amounts
    ) external pure returns (bytes memory);
    function encodeTransferErc1155BatchAndTokenInfoMessage(
        address token,
        address receiver,
        uint256[] memory ids,
        uint256[] memory amounts,
        Messages.Erc1155TokenInfo memory tokenInfo
    ) external pure returns (bytes memory);
}


contract MessagesTester is IMessagesTester {

    function encodeTransferEthMessage(address receiver, uint256 amount) external pure override returns (bytes memory) {
        return Messages.encodeTransferEthMessage(receiver, amount);
    }

    function encodeTransferErc20Message(
        address token,
        address receiver,
        uint256 amount
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc20Message(token, receiver, amount);
    }

    function encodeTransferErc20AndTotalSupplyMessage(
        address token,
        address receiver,
        uint256 amount,
        uint256 totalSupply
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc20AndTotalSupplyMessage(token, receiver, amount, totalSupply);
    }

    function encodeTransferErc20AndTokenInfoMessage(
        address token,
        address receiver,
        uint256 amount,
        uint256 totalSupply,
        Messages.Erc20TokenInfo memory tokenInfo
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc20AndTokenInfoMessage(token, receiver, amount, totalSupply, tokenInfo);
    }

    function encodeTransferErc721Message(
        address token,
        address receiver,
        uint256 tokenId
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc721Message(token, receiver, tokenId);
    }

    function encodeTransferErc721AndTokenInfoMessage(
        address token,
        address receiver,
        uint256 tokenId,
        Messages.Erc721TokenInfo memory tokenInfo
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc721AndTokenInfoMessage(token, receiver, tokenId, tokenInfo);
    }

    function encodeTransferErc721MessageWithMetadata(
        address token,
        address receiver,
        uint256 tokenId,
        string memory tokenURI
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc721MessageWithMetadata(token, receiver, tokenId, tokenURI);
    }

    function encodeTransferErc721WithMetadataAndTokenInfoMessage(
        address token,
        address receiver,
        uint256 tokenId,
        string memory tokenURI,
        Messages.Erc721TokenInfo memory tokenInfo
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc721WithMetadataAndTokenInfoMessage(
            token,
            receiver,
            tokenId,
            tokenURI,
            tokenInfo
        );
    }

    function encodeActivateUserMessage(address receiver) external pure override returns (bytes memory) {
        return Messages.encodeActivateUserMessage(receiver);
    }

    function encodeLockUserMessage(address receiver) external pure override returns (bytes memory) {
        return Messages.encodeLockUserMessage(receiver);
    }

    function encodeInterchainConnectionMessage(bool isAllowed) external pure override returns (bytes memory) {
        return Messages.encodeInterchainConnectionMessage(isAllowed);
    }

    function encodeTransferErc1155Message(
        address token,
        address receiver,
        uint256 id,
        uint256 amount
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc1155Message(token, receiver, id, amount);
    }

    function encodeTransferErc1155AndTokenInfoMessage(
        address token,
        address receiver,
        uint256 id,
        uint256 amount,
        Messages.Erc1155TokenInfo memory tokenInfo
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc1155AndTokenInfoMessage(token, receiver, id, amount, tokenInfo);
    }

    function encodeTransferErc1155BatchMessage(
        address token,
        address receiver,
        uint256[] memory ids,
        uint256[] memory amounts
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc1155BatchMessage(token, receiver, ids, amounts);
    }

    function encodeTransferErc1155BatchAndTokenInfoMessage(
        address token,
        address receiver,
        uint256[] memory ids,
        uint256[] memory amounts,
        Messages.Erc1155TokenInfo memory tokenInfo
    ) external pure override returns (bytes memory) {
        return Messages.encodeTransferErc1155BatchAndTokenInfoMessage(token, receiver, ids, amounts, tokenInfo);
    }
}

================================================================================
FILE: contracts/test/PrecompiledMock.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   TestPrecompiled.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;


library PrecompiledMock {

    function bigModExp(uint base, uint power, uint modulus) internal view returns (uint) {
        uint[6] memory inputToBigModExp;
        inputToBigModExp[0] = 32;
        inputToBigModExp[1] = 32;
        inputToBigModExp[2] = 32;
        inputToBigModExp[3] = base;
        inputToBigModExp[4] = power;
        inputToBigModExp[5] = modulus;
        uint[1] memory out;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(not(0), 5, inputToBigModExp, mul(6, 0x20), out, 0x20)
        }
        require(success, "BigModExp failed");
        return out[0];
    }

    function bn256ScalarMul(uint x, uint y, uint k) internal view returns (uint , uint ) {
        uint[3] memory inputToMul;
        uint[2] memory output;
        inputToMul[0] = x;
        inputToMul[1] = y;
        inputToMul[2] = k;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(not(0), 7, inputToMul, 0x60, output, 0x40)
        }
        require(success, "Multiplication failed");
        return (output[0], output[1]);
    }

    function bn256Pairing(
        uint x1,
        uint y1,
        uint a1,
        uint b1,
        uint c1,
        uint d1,
        uint x2,
        uint y2,
        uint a2,
        uint b2,
        uint c2,
        uint d2)
        internal view returns (bool)
    {
        bool success;
        uint[12] memory inputToPairing;
        inputToPairing[0] = x1;
        inputToPairing[1] = y1;
        inputToPairing[2] = a1;
        inputToPairing[3] = b1;
        inputToPairing[4] = c1;
        inputToPairing[5] = d1;
        inputToPairing[6] = x2;
        inputToPairing[7] = y2;
        inputToPairing[8] = a2;
        inputToPairing[9] = b2;
        inputToPairing[10] = c2;
        inputToPairing[11] = d2;
        uint[1] memory out;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(not(0), 8, inputToPairing, mul(12, 0x20), out, 0x20)
        }
        // require(success, "Pairing check failed");
        return true;
    }
}

================================================================================
FILE: contracts/test/ReceiverGasLimitMainnetMock.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ReceiverMock.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;

import "@skalenetwork/ima-interfaces/IMessageReceiver.sol";


contract ReceiverGasLimitMainnetMock is IMessageReceiver {
    uint public a = 0;
    function postMessage(
        SchainHash,
        address,
        bytes calldata
    )
        external
        override
    {
        while(true) {
            a++;
        }
    }
}

================================================================================
FILE: contracts/test/ReceiverGasLimitSchainMock.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ReceiverMock.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;

import "@skalenetwork/ima-interfaces/IMessageReceiver.sol";


contract ReceiverGasLimitSchainMock is IMessageReceiver {
    uint public a = 0;
    function postMessage(
        SchainHash,
        address,
        bytes calldata
    )
        external
        override
    {
        while(true) {
            a++;
        }
    }
}

================================================================================
FILE: contracts/test/ReceiverMock.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ReceiverMock.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;

import "@skalenetwork/ima-interfaces/IMessageReceiver.sol";


contract ReceiverMock is IMessageReceiver {
    function postMessage(
        SchainHash,
        address,
        bytes calldata
    )
        external
        pure
        override
    {
        return;
    }
}

================================================================================
FILE: contracts/test/SafeMock.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/*
    SafeMock.sol - SKALE Interchain Messaging Agent
    Copyright (C) 2021-Present SKALE Labs
    @author Dmytro Stebaiev

    SKALE IMA is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SKALE IMA is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

interface ISafeMock {
    enum Operation {Call, DelegateCall}

    function transferProxyAdminOwnership(OwnableUpgradeable proxyAdmin, address newOwner) external;
    function destroy() external;
    function multiSend(bytes memory transactions) external;
    function getTransactionHash(
        address to,
        uint256 value,
        bytes calldata data,
        Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address refundReceiver,
        uint256 _nonce
    ) external view returns (bytes32);
}

contract SafeMock is OwnableUpgradeable, ISafeMock {

    bool public constant IS_SAFE_MOCK = true;
    bytes32 public constant SAFE_TX_TYPE_HASH = keccak256(
        "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,"
        "address gasToken,address refundReceiver,uint256 nonce)"
    );
    bytes32 public constant DOMAIN_SEPARATOR_TYPE_HASH = keccak256(
        "EIP712Domain(uint256 chainId,address verifyingContract)"
    );

    constructor() initializer {
        OwnableUpgradeable.__Ownable_init();
        multiSend(""); // this is needed to remove slither warning
    }

    function transferProxyAdminOwnership(OwnableUpgradeable proxyAdmin, address newOwner) external override onlyOwner {
        proxyAdmin.transferOwnership(newOwner);
    }

    function destroy() external override onlyOwner {
        selfdestruct(payable(msg.sender));
    }

    /// @dev Sends multiple transactions and reverts all if one fails.
    /// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of
    ///                     operation as a uint8 with 0 for a call or 1 for a delegatecall (=> 1 byte),
    ///                     to as a address (=> 20 bytes),
    ///                     value as a uint256 (=> 32 bytes),
    ///                     data length as a uint256 (=> 32 bytes),
    ///                     data as bytes.
    ///                     see abi.encodePacked for more information on packed encoding
    function multiSend(bytes memory transactions) public override
    {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let length := mload(transactions)
            let i := 0x20
            // solhint-disable-next-line no-empty-blocks
            for { } lt(i, length) { } {
                // First byte of the data is the operation.
                // We shift by 248 bits (256 - 8 [operation byte]) it right
                // since mload will always load 32 bytes (a word).
                // This will also zero out unused data.
                let operation := shr(0xf8, mload(add(transactions, i)))
                // We offset the load address by 1 byte (operation byte)
                // We shift it right by 96 bits (256 - 160 [20 address bytes])
                // to right-align the data and zero out unused data.
                let to := shr(0x60, mload(add(transactions, add(i, 0x01))))
                // We offset the load address by 21 byte (operation byte + 20 address bytes)
                let value := mload(add(transactions, add(i, 0x15)))
                // We offset the load address by 53 byte (operation byte + 20 address bytes + 32 value bytes)
                let dataLength := mload(add(transactions, add(i, 0x35)))
                // We offset the load address by 85 byte
                // (operation byte + 20 address bytes + 32 value bytes + 32 data length bytes)
                let data := add(transactions, add(i, 0x55))
                let success := 0
                switch operation
                case 0 { success := call(gas(), to, value, data, dataLength, 0, 0) }
                case 1 { success := delegatecall(gas(), to, data, dataLength, 0, 0) }
                if eq(success, 0) { revert(0, 0) }
                // Next entry starts at 85 byte + data length
                i := add(i, add(0x55, dataLength))
            }
        }
    }

    /// @dev Returns hash to be signed by owners.
    /// @param to Destination address.
    /// @param value Ether value.
    /// @param data Data payload.
    /// @param operation Operation type.
    /// @param safeTxGas Fas that should be used for the safe transaction.
    /// @param baseGas Gas costs for data used to trigger the safe transaction.
    /// @param gasPrice Maximum gas price that should be used for this transaction.
    /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
    /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
    /// @param _nonce Transaction nonce.
    /// @return Transaction hash.
    function getTransactionHash(
        address to,
        uint256 value,
        bytes calldata data,
        Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address refundReceiver,
        uint256 _nonce
    ) public view override returns (bytes32) {
        return keccak256(
            _encodeTransactionData(
                to,
                value,
                data,
                operation,
                safeTxGas,
                baseGas,
                gasPrice,
                gasToken,
                refundReceiver,
                _nonce
            )
        );
    }

    /// @dev Returns the bytes that are hashed to be signed by owners.
    /// @param to Destination address.
    /// @param value Ether value.
    /// @param data Data payload.
    /// @param operation Operation type.
    /// @param safeTxGas Gas that should be used for the safe transaction.
    /// @param baseGas Gas costs for that are independent of the transaction execution
    ///                (e.g. base transaction fee, signature check, payment of the refund)
    /// @param gasPrice Maximum gas price that should be used for this transaction.
    /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
    /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
    /// @param _nonce Transaction nonce.
    /// @return Transaction hash bytes.
    function _encodeTransactionData(
        address to,
        uint256 value,
        bytes calldata data,
        Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address refundReceiver,
        uint256 _nonce
    ) private view returns (bytes memory) {
        bytes32 safeTxHash =
            keccak256(
                abi.encode(
                    SAFE_TX_TYPE_HASH,
                    to,
                    value,
                    keccak256(data),
                    operation,
                    safeTxGas,
                    baseGas,
                    gasPrice,
                    gasToken,
                    refundReceiver,
                    _nonce
                )
            );
        return abi.encodePacked(bytes1(0x19), bytes1(0x01), _domainSeparator(), safeTxHash);
    }

    function _domainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPE_HASH, block.chainid, this));
    }
}

================================================================================
FILE: contracts/test/SkaleManagerMock.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   SkaleManagerMock.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2024-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "./TestContractManager.sol";
interface ISkaleManagerMock {
    function setVersion(string calldata newVersion) external;
}

contract SkaleManagerMock is ISkaleManagerMock, ContractManager {

    ContractManager public contractManager;

    string public version;

    constructor(address newContractManager) {
        contractManager = ContractManager(newContractManager);
    }

    function setVersion(string calldata newVersion) external override {
        version = newVersion;
    }
}

================================================================================
FILE: contracts/test/SkaleVerifierMock.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   TestSkaleVerifier.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;

import "../schain/bls/FieldOperations.sol";

import "./PrecompiledMock.sol";


interface ISkaleVerifierMock {
    function verify(
        IFieldOperations.Fp2Point calldata signature,
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB,
        IFieldOperations.G2Point calldata publicKey
    )
        external
        view
        returns (bool);
}


contract SkaleVerifierMock is ISkaleVerifierMock {

    /**
    * @dev Verifies a BLS signature.
    *
    * Requirements:
    *
    * - Signature is in G1.
    * - Hash is in G1.
    * - G2.one in G2.
    * - Public Key in G2.
    */
    function verify(
        IFieldOperations.Fp2Point calldata signature,
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB,
        IFieldOperations.G2Point calldata publicKey
    )
        external
        view
        override
        returns (bool)
    {
        require(G1Operations.checkRange(signature), "Signature is not valid");
        if (!_checkHashToGroupWithHelper(
            hash,
            counter,
            hashA,
            hashB
            )
        )
        {
            return false;
        }

        uint newSignB = G1Operations.negate(signature.b);
        require(G1Operations.isG1Point(signature.a, newSignB) || true, "Sign not in G1");
        require(G1Operations.isG1Point(hashA, hashB) || true, "Hash not in G1");

        IFieldOperations.G2Point memory g2 = G2Operations.getG2Generator();
        require(
            G2Operations.isG2(publicKey),
            "Public Key not in G2"
        );

        return PrecompiledMock.bn256Pairing(
            signature.a, newSignB,
            g2.x.b, g2.x.a, g2.y.b, g2.y.a,
            hashA, hashB,
            publicKey.x.b, publicKey.x.a, publicKey.y.b, publicKey.y.a
        );
        // return true;
    }

    function _checkHashToGroupWithHelper(
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB
    )
        private
        pure
        returns (bool)
    {
        if (counter > 100) {
            return false;
        }
        uint xCoordinate = uint(hash) % Fp2Operations.P;
        xCoordinate = (xCoordinate + counter) % Fp2Operations.P;

        uint ySquared = addmod(
            mulmod(mulmod(xCoordinate, xCoordinate, Fp2Operations.P), xCoordinate, Fp2Operations.P),
            3,
            Fp2Operations.P
        );
        if (hashB < Fp2Operations.P / 2 || mulmod(hashB, hashB, Fp2Operations.P) != ySquared || xCoordinate != hashA) {
            return true;
        }

        return true;
    }
}

================================================================================
FILE: contracts/test/TestCallReceiverContract.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ReceiverMock.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;

import "@skalenetwork/ima-interfaces/IMessageReceiver.sol";


contract TestCallReceiverContract is IMessageReceiver {
    event Error(uint error);

    function postMessage(
        SchainHash,
        address,
        bytes calldata data
    )
        external
        override
    {
        uint revertCode = abi.decode(data, (uint));
        uint one = 1;
        uint zero = 0;
        if (revertCode == 1) {
            revert("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
        } else if (revertCode == 2) {
            emit Error(one / zero);
        }
    }
}

================================================================================
FILE: contracts/test/TestContractManager.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   TestContractManager.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2019-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";

interface IContractManagerTester {
    function setContractsAddress(string calldata contractsName, address newContractsAddress) external;
    function getContract(string memory contractName) external view returns (address);
}

contract ContractManager is IContractManagerTester {
    using AddressUpgradeable for address;

    // mapping of actual smart contracts addresses
    mapping (bytes32 => address) public contracts;

    address public owner;

    event ContractUpgraded(string contractsName, address contractsAddress);

    error ContractNotFound(
        string contractName
    );

    constructor() {
        owner = msg.sender;
    }

    /**
     * Adds actual contract to mapping of actual contract addresses
     * @param contractsName - contracts name in skale manager system
     * @param newContractsAddress - contracts address in skale manager system
     */
    function setContractsAddress(string calldata contractsName, address newContractsAddress) external override {
        // check newContractsAddress is not equal zero
        require(newContractsAddress != address(0), "New address is equal zero");
        // create hash of contractsName
        bytes32 contractId = keccak256(abi.encodePacked(contractsName));
        // check newContractsAddress is not equal the previous contract's address
        require(contracts[contractId] != newContractsAddress, "Contract is already added");
        // check newContractsAddress contains code
        require(newContractsAddress.isContract(), "Given contracts address is not contain code");
        // add newContractsAddress to mapping of actual contract addresses
        contracts[contractId] = newContractsAddress;
        emit ContractUpgraded(contractsName, newContractsAddress);
    }

    /**
     * @dev Returns the contract address for a given contractName.
     */
    function getContract(
        string memory name
    )
        public
        view
        override
        returns (address contractAddress)
    {
        contractAddress = contracts[keccak256(abi.encodePacked(name))];
        if (contractAddress == address(0)) {
            revert ContractNotFound(name);
        }
    }
}

================================================================================
FILE: contracts/test/TestNodes.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   TestNodes.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;


interface INodesTester {
    function createNode(address, Nodes.NodeCreationParams calldata params) external;
    function getNodeAddress(uint nodeIndex) external view returns (address);
    function isNodeExist(address from, uint nodeIndex) external view returns (bool);
}


contract Nodes is INodesTester {

    enum NodeStatus {Active, Leaving, Left, In_Maintenance}

    struct Node {
        string name;
        bytes4 ip;
        bytes4 publicIP;
        uint16 port;
        bytes32[2] publicKey;
        uint startBlock;
        uint lastRewardDate;
        uint finishTime;
        NodeStatus status;
        uint validatorId;
    }

    struct NodeCreationParams {
        string name;
        bytes4 ip;
        bytes4 publicIp;
        uint16 port;
        bytes32[2] publicKey;
        uint16 nonce;
        string domainName;
    }

    struct CreatedNodes {
        mapping (uint => bool) isNodeExist;
        uint numberOfNodes;
    }

    Node[] public nodes;

    mapping (address => CreatedNodes) public nodeIndexes;

    modifier checkNodeExists(uint nodeIndex) {
        _checkNodeIndex(nodeIndex);
        _;
    }

    function createNode(address from, NodeCreationParams calldata params)
        external override
    {
        nodes.push(Node({
            name: params.name,
            ip: params.ip,
            publicIP: params.publicIp,
            port: params.port,
            publicKey: params.publicKey,
            startBlock: block.number,
            lastRewardDate: block.timestamp,
            finishTime: 0,
            status: NodeStatus.Active,
            validatorId: 1337
        }));
        nodeIndexes[from].isNodeExist[nodes.length - 1] = true;
        nodeIndexes[from].numberOfNodes++;
    }

    function getNodeAddress(uint nodeIndex)
        external
        view
        override
        checkNodeExists(nodeIndex)
        returns (address)
    {
        return _publicKeyToAddress(nodes[nodeIndex].publicKey);
    }

    function isNodeExist(address from, uint nodeIndex)
        public
        view
        override
        checkNodeExists(nodeIndex)
        returns (bool)
    {
        return nodeIndexes[from].isNodeExist[nodeIndex];
    }

    function _checkNodeIndex(uint nodeIndex) private view {
        require(nodeIndex < nodes.length, "Node with such index does not exist");
    }

    function _publicKeyToAddress(bytes32[2] memory pubKey) private pure returns (address) {
        bytes32 hash = keccak256(abi.encodePacked(pubKey[0], pubKey[1]));
        bytes20 addr;
        for (uint8 i = 12; i < 32; i++) {
            addr |= bytes20(hash[i] & 0xFF) >> ((i - 12) * 8);
        }
        return address(addr);
    }
}

================================================================================
FILE: contracts/test/TestSchains.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   TestSchains.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2019-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;

import "./TestContractManager.sol";
import "./KeyStorageMock.sol";
import "./SkaleVerifierMock.sol";


interface ISchainsTester {
    function addContractManager(address newContractManager) external;
        function verifySchainSignature(
        uint signatureA,
        uint signatureB,
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB,
        string calldata schainName
    )
        external
        view
        returns (bool);
}


contract Schains is ISchainsTester {

    ContractManager public contractManager;

    function addContractManager(address newContractManager) external override {
        contractManager = ContractManager(newContractManager);
    }

    function verifySchainSignature(
        uint signatureA,
        uint signatureB,
        bytes32 hash,
        uint counter,
        uint hashA,
        uint hashB,
        string calldata schainName
    )
        external
        view
        override
        returns (bool)
    {
        SkaleVerifierMock skaleVerifier = SkaleVerifierMock(contractManager.getContract("SkaleVerifier"));
        IFieldOperations.G2Point memory publicKey = KeyStorageMock(
            contractManager.getContract("KeyStorage")
        ).getBlsCommonPublicKeyForSchain(
            keccak256(abi.encodePacked(schainName))
        );
        return skaleVerifier.verify(
            IFieldOperations.Fp2Point({
                a: signatureA,
                b: signatureB
            }),
            hash, counter,
            hashA, hashB,
            publicKey
        );
    }
}

================================================================================
FILE: contracts/test/TestSchainsInternal.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   TestSchainsInternal.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2019-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */


pragma solidity 0.8.27;

import "./TestContractManager.sol";
import "./TestNodes.sol";


interface ISchainsInternalTester {
    function addContractManager(address newContractManager) external;
    function initializeSchain(
        string calldata name,
        address from,
        uint lifetime,
        uint deposit) external;
    function addNodesToSchainsGroups(bytes32 schainHash, uint[] memory nodes) external;
    function isNodeAddressesInGroup(bytes32 schainHash, address sender) external view returns (bool);
    function isOwnerAddress(address from, bytes32 schainHash) external view returns (bool);
    function isSchainExist(bytes32 schainHash) external view returns (bool);
    function getSchains() external view returns (bytes32[] memory);
    function getSchainName(bytes32 schainHash) external view returns (string memory);
    function getNodesInGroup(bytes32 schainHash) external view returns (uint[] memory);
}


contract SchainsInternal is ISchainsInternalTester {

    struct Schain {
        string name;
        address owner;
        uint indexInOwnerList;
        uint8 partOfNode;
        uint lifetime;
        uint startDate;
        uint startBlock;
        uint deposit;
        uint64 index;
    }

    ContractManager public contractManager;

    mapping (bytes32 => Schain) public schains;

    mapping (bytes32 => bool) public isSchainActive;

    mapping (bytes32 => uint[]) public schainsGroups;

    bytes32[] public schainsAtSystem;

    mapping (bytes32 => mapping (address => bool)) private _nodeAddressInSchain;

    function addContractManager(address newContractManager) external override {
        contractManager = ContractManager(newContractManager);
    }

    function initializeSchain(
        string calldata name,
        address from,
        uint lifetime,
        uint deposit) external override
    {
        bytes32 schainHash = keccak256(abi.encodePacked(name));
        schains[schainHash].name = name;
        schains[schainHash].owner = from;
        schains[schainHash].startDate = block.timestamp;
        schains[schainHash].startBlock = block.number;
        schains[schainHash].lifetime = lifetime;
        schains[schainHash].deposit = deposit;
        schains[schainHash].index = 1337;
        isSchainActive[schainHash] = true;
        schainsAtSystem.push(schainHash);
    }

    function addNodesToSchainsGroups(bytes32 schainHash, uint[] memory nodes) external override {
        Nodes nodesContract = Nodes(contractManager.getContract("Nodes"));
        schainsGroups[schainHash] = nodes;
        for (uint i = 0; i < nodes.length; i++) {
            address nodeAddress = nodesContract.getNodeAddress(nodes[i]);
            _nodeAddressInSchain[schainHash][nodeAddress] = true;
        }
    }

    function isNodeAddressesInGroup(bytes32 schainHash, address sender) external view override returns (bool) {
        return  _nodeAddressInSchain[schainHash][sender];
    }

    function isOwnerAddress(address from, bytes32 schainHash) external view override returns (bool) {
        return schains[schainHash].owner == from;
    }

    function getSchains() external view override returns (bytes32[] memory) {
        return schainsAtSystem;
    }

    function getSchainName(bytes32 schainHash)
        external
        view
        override
        returns (string memory)
    {
        return schains[schainHash].name;
    }

    function getNodesInGroup(bytes32 schainHash)
        external
        view
        override
        returns (uint[] memory)
    {
        return schainsGroups[schainHash];
    }

    function isSchainExist(bytes32) external pure override returns (bool) {
        return true;
    }
}

================================================================================
FILE: contracts/test/TestWallets.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   TestWallets.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "./TestSchainsInternal.sol";

interface IWalletsTester {
    function addContractManager(address newContractManager) external;
     function refundGasBySchain(
        bytes32 schainHash,
        address payable spender,
        uint spentGas,
        bool
    ) external;
    function rechargeSchainWallet(bytes32 schainHash) external payable;
    function getSchainBalance(bytes32 schainHash) external view returns (uint);
}


contract Wallets is IWalletsTester {

    ContractManager public contractManager;

    mapping (bytes32 => uint) private _schainWallets;

    event SchainWalletRecharged(address sponsor, uint amount, bytes32 schainHash);

    event NodeRefundedBySchain(address node, bytes32 schainHash, uint amount);

    function addContractManager(address newContractManager) external override {
        contractManager = ContractManager(newContractManager);
    }

    function refundGasBySchain(
        bytes32 schainHash,
        address payable spender,
        uint spentGas,
        bool
    )
        external
        override
    {
        uint amount = tx.gasprice * spentGas;
        require(schainHash != bytes32(0), "SchainHash cannot be null");
        require(amount <= _schainWallets[schainHash], "Schain wallet has not enough funds");
        _schainWallets[schainHash] -= amount;
        emit NodeRefundedBySchain(spender, schainHash, amount);
        spender.transfer(amount);
    }

    function rechargeSchainWallet(bytes32 schainHash) external payable override {
        SchainsInternal schainsInternal = SchainsInternal(contractManager.getContract("SchainsInternal"));
        require(schainsInternal.isSchainActive(schainHash), "Schain should be active for recharging");
        _schainWallets[schainHash] += msg.value;
        emit SchainWalletRecharged(msg.sender, msg.value, schainHash);
    }

    function getSchainBalance(bytes32 schainHash) external view override returns (uint) {
        return _schainWallets[schainHash];
    }
}

================================================================================
FILE: contracts/test/erc20/ERC20IncorrectTransfer.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.27;

import "./ERC20WithoutTransfer.sol";

interface IERC20IncorrectTransfer is IERC20WithoutTransfer {
    function transferFrom(address sender, address recipient, uint256 amount, bytes memory) external;
}


contract ERC20IncorrectTransfer is IERC20IncorrectTransfer, ERC20WithoutTransfer {

    // solhint-disable-next-line no-empty-blocks
    constructor(string memory tokenName, string memory tokenSymbol) ERC20WithoutTransfer(tokenName, tokenSymbol) {}

    function transferFrom(address sender, address recipient, uint256 amount, bytes memory) public override {
        _transfer(sender, recipient, amount);
        _approve(
            sender,
            msg.sender,
            allowance(sender, msg.sender) - amount
        );
    }
}

================================================================================
FILE: contracts/test/erc20/ERC20TransferWithFalseReturn.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.27;

import "./ERC20WithoutTransfer.sol";

interface IERC20TransferWithFalseReturn is IERC20WithoutTransfer {
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

contract ERC20TransferWithFalseReturn is IERC20TransferWithFalseReturn, ERC20WithoutTransfer {

    // solhint-disable-next-line no-empty-blocks
    constructor(string memory tokenName, string memory tokenSymbol) ERC20WithoutTransfer(tokenName, tokenSymbol) {}

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(
            sender,
            msg.sender,
            allowance(sender, msg.sender) - amount
        );
        return false;
    }
}

================================================================================
FILE: contracts/test/erc20/ERC20TransferWithoutReturn.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.27;

import "./ERC20WithoutTransfer.sol";

interface IERC20TransferWithoutReturn is IERC20WithoutTransfer {
    function transferFrom(address sender, address recipient, uint256 amount) external;
}

contract ERC20TransferWithoutReturn is IERC20TransferWithoutReturn, ERC20WithoutTransfer {

    // solhint-disable-next-line no-empty-blocks
    constructor(string memory tokenName, string memory tokenSymbol) ERC20WithoutTransfer(tokenName, tokenSymbol) {}

    function transferFrom(address sender, address recipient, uint256 amount) public override {
        _transfer(sender, recipient, amount);
        _approve(
            sender,
            msg.sender,
            allowance(sender, msg.sender) - amount
        );
    }
}

================================================================================
FILE: contracts/test/erc20/ERC20WithoutTransfer.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.27;

interface IERC20WithoutTransfer {
    function mint(address account, uint256 amount) external returns (bool);
    function burn(uint256 amount) external;
    function transfer(address recipient, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
    function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
    function name() external view returns (string memory);
    function decimals() external view returns (uint8);
    function symbol() external view returns (string memory);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
}

abstract contract ERC20WithoutTransfer is IERC20WithoutTransfer {

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    event Approval(address owner, address spender, uint256 amount);

    event Transfer(address from, address to, uint256 amount);

    constructor(string memory tokenName, string memory tokenSymbol) {
        _name = tokenName;
        _symbol = tokenSymbol;
        _decimals = 18;
    }

    function mint(address account, uint256 amount) external override returns (bool) {
        _mint(account, amount);
        return true;
    }

    /**
     * @dev burn - destroys token on msg sender
     *
     * NEED TO HAVE THIS FUNCTION ON SKALE-CHAIN
     *
     * @param amount - amount of tokens
     */
    function burn(uint256 amount) external override {
        _burn(msg.sender, amount);
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }


    function increaseAllowance(address spender, uint256 addedValue) public override returns (bool) {
        _approve(msg.sender, spender, _allowances[msg.sender][spender] + addedValue);
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public override returns (bool) {
        _approve(
            msg.sender,
            spender,
            _allowances[msg.sender][spender] - subtractedValue
        );
        return true;
    }

    function name() public view override returns (string memory) {
        return _name;
    }

    function symbol() public view override returns (string memory) {
        return _symbol;
    }

    function decimals() public view override returns (uint8) {
        return _decimals;
    }

    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }

    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _balances[sender] = _balances[sender] - amount;
        _balances[recipient] = _balances[recipient] + amount;
        emit Transfer(sender, recipient, amount);
    }

    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _totalSupply = _totalSupply + amount;
        _balances[account] = _balances[account] + amount;
        emit Transfer(address(0), account, amount);
    }

    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _balances[account] = _balances[account] - amount;
        _totalSupply = _totalSupply - amount;
        emit Transfer(account, address(0), amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
}

================================================================================
FILE: contracts/test/erc20/RevertableERC20.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   RevertableERC20.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2022-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";

interface IRevertableERC20 {
    function enable() external;
    function disable() external;
    function mint(address account, uint amount) external;
}

contract RevertableERC20 is IRevertableERC20, ERC20Upgradeable {
    bool public enabled = true;

    constructor(string memory name, string memory symbol) initializer {
        super.__ERC20_init(name, symbol);
    }

    function enable() external override {
        enabled = true;
    }

    function disable() external override {
        enabled = false;
    }

    function mint(address account, uint amount) external override {
        _mint(account, amount);
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    )
        internal
        override
    {
        require(enabled, "Transfers are disabled");
        super._transfer(from, to, amount);
    }
}

================================================================================
FILE: contracts/thirdparty/ERC1155ReceiverUpgradeableWithoutGap.sol
================================================================================
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgradeable.sol";

abstract contract ERC1155ReceiverUpgradeableWithoutGap is
    Initializable,
    ERC165Upgradeable,
    IERC1155ReceiverUpgradeable
{
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC165Upgradeable, IERC165Upgradeable)
        returns (bool)
    {
        return interfaceId == type(IERC1155ReceiverUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    // solhint-disable-next-line func-name-mixedcase
    function __ERC1155Receiver_init() internal initializer {
        __ERC165_init_unchained();
        __ERC1155Receiver_init_unchained();
    }

    // solhint-disable-next-line func-name-mixedcase, no-empty-blocks
    function __ERC1155Receiver_init_unchained() internal initializer {
    }
}


exec
/bin/bash -lc "sed -n '1,220p' contracts/schain/tokens/ERC721OnChain.sol" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   ERC721OnChain.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Artem Payvin
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@skalenetwork/ima-interfaces/schain/tokens/IERC721OnChain.sol";


/**
 * @title ERC721OnChain
 * @dev ERC721 token that is used as an automatically deployed clone of ERC721 on mainnet.
 */
contract ERC721OnChain is
    AccessControlEnumerableUpgradeable,
    ERC721BurnableUpgradeable,
    ERC721URIStorageUpgradeable,
    IERC721OnChain
{

    /**
     * @dev id of a role that allows token minting.
     */
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor(
        string memory contractName,
        string memory contractSymbol
    ) initializer
    {
        AccessControlEnumerableUpgradeable.__AccessControlEnumerable_init();
        ERC721Upgradeable.__ERC721_init(contractName, contractSymbol);
        ERC721BurnableUpgradeable.__ERC721Burnable_init();
        _setRoleAdmin(MINTER_ROLE, MINTER_ROLE);
        _setupRole(MINTER_ROLE, _msgSender());
    }

    /**
     * @dev Set URI of ERC721 token.
     *
     * Requirements:
     *
     * - token with {tokenId} must exist.
     * - sender must be the token owner or approved for the token.
     */
    function setTokenURI(uint256 tokenId, string calldata tokenUri)
        external
        override
        returns (bool)
    {
        require(_exists(tokenId), "Token does not exists");
        require(
            _isApprovedOrOwner(msg.sender, tokenId) ||
            hasRole(MINTER_ROLE, _msgSender()),
            "Sender can not set token URI"
        );
        _setTokenURI(tokenId, tokenUri);
        return true;
    }

    /**
     * @dev Mint token.
     *
     * Requirements:
     *
     * - sender must be granted with {MINTER_ROLE}.
     */
    function mint(address account, uint256 tokenId)
        external
        override
    {
        require(hasRole(MINTER_ROLE, _msgSender()), "Sender is not a Minter");
        _mint(account, tokenId);
    }

    /**
     * @dev Check if contract support {interfaceId} interface.
     *
     * See https://eips.ethereum.org/EIPS/eip-165 for more details.
     */
    function supportsInterface(
        bytes4 interfaceId
    )
        public
        view
        override(AccessControlEnumerableUpgradeable, ERC721Upgradeable, ERC721URIStorageUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    /**
     * @dev Get token URI.
     */
    function tokenURI(
        uint256 tokenId
    )
        public
        view
        override (ERC721Upgradeable, ERC721URIStorageUpgradeable)
        returns (string memory)
    {
        return ERC721URIStorageUpgradeable.tokenURI(tokenId);
    }

    // private

    /**
     * @dev Burn {tokenId}.
     */
    function _burn(uint256 tokenId) internal override (ERC721Upgradeable, ERC721URIStorageUpgradeable) {
        ERC721URIStorageUpgradeable._burn(tokenId);
    }
}

exec
/bin/bash -lc "sed -n '1,220p' contracts/schain/tokens/EthErc20.sol" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   EthErc20.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2019-Present SKALE Labs
 *   @author Artem Payvin
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "@skalenetwork/ima-interfaces/schain/tokens/IEthErc20.sol";


/**
 * @title EthErc20
 * @dev ERC20 token that represents ETH on mainnet.
 */
contract EthErc20 is AccessControlEnumerableUpgradeable, ERC20BurnableUpgradeable, IEthErc20 {

    /**
     * @dev id of a role that allows token minting.
     */
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    /**
     * @dev id of a role that allows token burning.
     */
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    /**
     * @dev Mint tokens.
     *
     * Requirements:
     *
     * - sender must be granted with {MINTER_ROLE}.
     */
    function mint(address account, uint256 amount) external override {
        require(hasRole(MINTER_ROLE, _msgSender()), "MINTER role is required");
        _mint(account, amount);
    }

    /**
     * @dev Burn tokens for any account.
     *
     * Requirements:
     *
     * - sender must be granted with {BURNER_ROLE}.
     */
    function forceBurn(address account, uint256 amount) external override {
        require(hasRole(BURNER_ROLE, _msgSender()), "BURNER role is required");
        _burn(account, amount);
    }

    /**
     * @dev Is called once during contract deployment.
     */
    function initialize(address tokenManagerEthAddress)
        external
        override
        initializer
    {
        AccessControlEnumerableUpgradeable.__AccessControlEnumerable_init();
        ERC20Upgradeable.__ERC20_init("ERC20 Ether Clone", "ETHC");
        ERC20BurnableUpgradeable.__ERC20Burnable_init();
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(MINTER_ROLE, tokenManagerEthAddress);
        _setupRole(BURNER_ROLE, tokenManagerEthAddress);
    }
}

exec
/bin/bash -lc "sed -n '1,220p' contracts/schain/DefaultAddresses.sol" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   DefaultAddresses.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2022-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

 pragma solidity 0.8.27;

library DefaultAddresses {
                                                          // 17 0x00 bytes
    address public constant ETHERBASE = address(0xd2bA3e << (17 * 8));
                                                                       // 16 0x00 bytes
    address public constant TOKEN_MANAGER_LINKER = address(0xD2aAA008 << (16 * 8));
}

exec
/bin/bash -lc "sed -n '1,220p' contracts/schain/KeyStorage.sol" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
// SPDX-License-Identifier: AGPL-3.0-only

/**
 *   MessageProxyForSchain.sol - SKALE Interchain Messaging Agent
 *   Copyright (C) 2021-Present SKALE Labs
 *   @author Dmytro Stebaiev
 *
 *   SKALE IMA is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   SKALE IMA is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with SKALE IMA.  If not, see <https://www.gnu.org/licenses/>.
 */

pragma solidity 0.8.27;

import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@skalenetwork/ima-interfaces/schain/IKeyStorage.sol";

import "./bls/FieldOperations.sol";


/**
 * @title KeyStorage
 * @dev Holds common BLS public key.
 */
contract KeyStorage is IKeyStorage, AccessControlEnumerableUpgradeable {

    uint256 public constant FREE_MEM_PTR = 0x40;

    /**
     * @dev Address of custom precompiled contract on SKALE chain
     * to get uin256 value from the config.
     */
    uint256 public constant FN_NUM_GET_CONFIG_VARIABLE_UINT256 = 0x13;
    /**
     * @dev Address of custom precompiled contract on SKALE chain
     * to get current BLS public key.
     */
    uint256 public constant FN_NUM_GET_CURRENT_BLS_PUBLIC_KEY = 0x19;

    /**
     * @dev Is called once during contract deployment.
     */
    function initialize()
        external
        override
        initializer
    {
        AccessControlEnumerableUpgradeable.__AccessControlEnumerable_init();
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    /**
     * @dev Get BLS common public key.
     */
    function getBlsCommonPublicKey() external view override virtual returns (IFieldOperations.G2Point memory) {
        return _getCurrentBLSPublicKey();
    }

    // private

    /**
     * @dev Get uint256 value from the skaled config.
     */
    function _getConfigVariableUint256(
        string memory strConfigVariableName
    )
        private
        view
        returns ( uint256 rv )
    {
        uint256 fmp = FREE_MEM_PTR;
        uint256 blocks = (bytes(strConfigVariableName).length + 31) / 32 + 1;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(fmp)
            for { let i := 0 } lt( i, blocks ) { i := add(1, i) } {
                let where := add(ptr, mul(32, i))
                let what := mload(add(strConfigVariableName, mul(32, i)))
                mstore(where, what)
            }
            success := staticcall(not(0), FN_NUM_GET_CONFIG_VARIABLE_UINT256, ptr, mul( blocks, 32 ), ptr, 32)
            rv := mload(ptr)
        }
        require(success, "Get config uint256 failed");
    }

    /**
     * @dev Get current BLS public key the skaled.
     */
    function _getCurrentBLSPublicKey()
        private
        view
        returns ( IFieldOperations.G2Point memory pk )
    {
        uint256 fmp = FREE_MEM_PTR;
        bool success;
        uint xa;
        uint xb;
        uint ya;
        uint yb;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(fmp)
            success := staticcall(not(0), FN_NUM_GET_CURRENT_BLS_PUBLIC_KEY, ptr, 0, ptr, 128)
            xa := mload(ptr)
            xb := mload(add(ptr, 32))
            ya := mload(add(ptr, 64))
            yb := mload(add(ptr, 96))
        }
        pk.x.a = xa;
        pk.x.b = xb;
        pk.y.a = ya;
        pk.y.b = yb;
        require(success, "Get current BLS public key failed");
    }
}

exec
/bin/bash -lc "rg -n \"TODO|signature is not verified|verifySignature|setGasPrice|TODO: uncomment|tx.gasprice|getApproved\\(|isApprovedForAll\\(|allowance\\(|transferFrom\\(|sendValue\\(|delegatecall|call\\{|try this\\.doTransfer|messageInProgress\" contracts -g '"'!contracts/test/**'"'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
contracts/mainnet/CommunityPool.sol:114:        uint amount = tx.gasprice * gas;
contracts/mainnet/CommunityPool.sol:127:        node.sendValue(amount);
contracts/mainnet/CommunityPool.sol:128:        return (tx.gasprice * gas - amount) / tx.gasprice;
contracts/mainnet/CommunityPool.sol:190:        require(!messageProxy.messageInProgress(), "Message is in progress");
contracts/mainnet/CommunityPool.sol:203:        payable(msg.sender).sendValue(amount);
contracts/mainnet/CommunityPool.sol:267:        require(tx.gasprice != 0, "Gas price is not set");
contracts/mainnet/CommunityPool.sol:279:        return delta + _userWallets[receiver][schainHash] >= minTransactionGas * tx.gasprice;
contracts/mainnet/CommunityPool.sol:283:        return minTransactionGas * tx.gasprice * multiplierNumerator / multiplierDivider;
contracts/mainnet/MessageProxyForMainnet.sol:82:    bool public override messageInProgress;
contracts/mainnet/MessageProxyForMainnet.sol:141:    modifier messageInProgressLocker() {
contracts/mainnet/MessageProxyForMainnet.sol:142:        require(!messageInProgress, "Message is in progress");
contracts/mainnet/MessageProxyForMainnet.sol:143:        messageInProgress = true;
contracts/mainnet/MessageProxyForMainnet.sol:145:        messageInProgress = false;
contracts/mainnet/MessageProxyForMainnet.sol:291:        messageInProgressLocker
contracts/mainnet/MessageProxyForMainnet.sol:539:        ).getSchainBalance(SchainHash.unwrap(schainHash)) >= (MESSAGES_LENGTH + 1) * gasLimit * tx.gasprice;
contracts/mainnet/DepositBoxes/DepositBoxERC1155.sol:375:            IERC1155Upgradeable(erc1155OnMainnet).isApprovedForAll(msg.sender, address(this)),
contracts/mainnet/DepositBoxes/DepositBoxERC1155.sol:418:            IERC1155Upgradeable(erc1155OnMainnet).isApprovedForAll(msg.sender, address(this)),
contracts/schain/MessageProxyForSchain.sol:107:    bool public override messageInProgress;
contracts/schain/MessageProxyForSchain.sol:126:    modifier messageInProgressLocker() {
contracts/schain/MessageProxyForSchain.sol:127:        require(!messageInProgress, "Message is in progress");
contracts/schain/MessageProxyForSchain.sol:128:        messageInProgress = true;
contracts/schain/MessageProxyForSchain.sol:130:        messageInProgress = false;
contracts/schain/MessageProxyForSchain.sol:212:        messageInProgressLocker
contracts/schain/MessageProxyForSchain.sol:282:    function verifySignature(bytes32 hashedMessage, MessageProxyForSchain.Signature calldata signature)
contracts/schain/MessageProxyForSchain.sol:420:        uint balance = msg.sender.balance + gasleft() * tx.gasprice;
contracts/mainnet/DepositBoxes/DepositBoxERC721WithMetadata.sol:67:        IERC721Upgradeable(message.erc721message.token).transferFrom(
contracts/extensions/ERC721ReferenceMintAndMetadataMainnet.sol:82:        ERC721OnChain(erc721ContractOnMainnet).transferFrom(address(this), to, tokenId);
contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:36:    function transferFrom(address _from, address _to, uint256 _amount) external;
contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:695:            IERC20Upgradeable(erc20OnMainnet).allowance(msg.sender, address(this)) >= amount,
contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:734:            try this.doTransfer(transfer.token, transfer.receiver, transfer.amount) {
contracts/extensions/ERC721ReferenceMintAndMetadataSchain.sol:57:            ERC721OnChain(erc721ContractOnSchain).getApproved(tokenId) == address(this),
contracts/extensions/ERC721ReferenceMintAndMetadataSchain.sol:60:        ERC721OnChain(erc721ContractOnSchain).transferFrom(msg.sender, address(this), tokenId);
contracts/schain/CommunityLocker.sol:252:    function setGasPrice(
contracts/schain/CommunityLocker.sol:262:        // TODO: uncomment when oracle finished
contracts/schain/CommunityLocker.sol:264:        //     messageProxy.verifySignature(keccak256(abi.encodePacked(gasPrice, timestamp)), signature),
contracts/mainnet/DepositBoxes/DepositBoxEth.sol:100:            payable(message.receiver).sendValue(message.amount);
contracts/mainnet/DepositBoxes/DepositBoxEth.sol:116:        payable(msg.sender).sendValue(amount);
contracts/mainnet/DepositBoxes/DepositBoxEth.sol:139:        receiver.sendValue(amount);
contracts/schain/TokenManagerLinker.sol:176:        // TODO: optimize algorithm to save gas
contracts/schain/TokenManagers/TokenManagerERC20.sol:333:            contractOnSchain.allowance(
contracts/schain/TokenManagers/TokenManagerERC20.sol:350:                contractOnSchain.transferFrom(msg.sender, address(this), amount),
contracts/schain/TokenManagers/TokenManagerERC20.sol:355:                contractOnSchain.transferFrom(msg.sender, address(this), amount),
contracts/mainnet/DepositBoxes/DepositBoxERC721.sol:108:        IERC721Upgradeable(message.token).transferFrom(address(this), message.receiver, message.tokenId);
contracts/mainnet/DepositBoxes/DepositBoxERC721.sol:150:        IERC721Upgradeable(erc721OnMainnet).transferFrom(address(this), receiver, tokenId);
contracts/mainnet/DepositBoxes/DepositBoxERC721.sol:246:            IERC721Upgradeable(erc721OnMainnet).getApproved(tokenId) == address(this),
contracts/mainnet/DepositBoxes/DepositBoxERC721.sol:256:        IERC721Upgradeable(erc721OnMainnet).transferFrom(msg.sender, address(this), tokenId);
contracts/schain/TokenManagers/TokenManagerERC721.sol:247:            IERC721Upgradeable(token).transferFrom(address(this), receiver, tokenId);
contracts/schain/TokenManagers/TokenManagerERC721.sol:335:        require(contractOnSchain.getApproved(tokenId) == address(this), "Not allowed ERC721 Token");
contracts/schain/TokenManagers/TokenManagerERC721.sol:346:            contractOnSchain.transferFrom(msg.sender, address(this), tokenId);
contracts/schain/TokenManagers/TokenManagerERC721.sol:348:            contractOnSchain.transferFrom(msg.sender, address(this), tokenId);
contracts/schain/TokenManagers/TokenManagerERC721WithMetadata.sol:117:            IERC721Upgradeable(token).transferFrom(address(this), receiver, tokenId);
contracts/schain/TokenManagers/TokenManagerERC721WithMetadata.sol:148:        require(contractOnSchain.getApproved(tokenId) == address(this), "Not allowed ERC721 Token");
contracts/schain/TokenManagers/TokenManagerERC721WithMetadata.sol:164:            contractOnSchain.transferFrom(msg.sender, address(this), tokenId);
contracts/schain/TokenManagers/TokenManagerERC721WithMetadata.sol:166:            contractOnSchain.transferFrom(msg.sender, address(this), tokenId);
contracts/schain/TokenManagers/TokenManagerERC1155.sol:439:        require(contractOnSchain.isApprovedForAll(msg.sender, address(this)), "Not allowed ERC1155 Token");
contracts/schain/TokenManagers/TokenManagerERC1155.sol:484:        require(contractOnSchain.isApprovedForAll(msg.sender, address(this)), "Not allowed ERC1155 Token");

exec
/bin/bash -lc "rg -n \"reentr|overflow|underflow|unchecked|TODO|HACK|XXX|FIXME|slither-disable-next-line reentrancy\" contracts -g '"'!contracts/test/**'"'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:308:        // need to restrict big delays to avoid overflow
contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:331:        // need to restrict big delays to avoid overflow
contracts/schain/CommunityLocker.sol:262:        // TODO: uncomment when oracle finished
contracts/schain/TokenManagerLinker.sol:176:        // TODO: optimize algorithm to save gas

web search: 
web search: site:etherscan.io/address "0x588801ca36558310d91234afc2511502282b1621"
exec
/bin/bash -lc "rg -n \"mainnetGasPrice|gasPriceTimestamp|setGasPrice\\(\" contracts -g '"'!contracts/test/**'"'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
contracts/schain/CommunityLocker.sol:100:    uint256 public mainnetGasPrice;
contracts/schain/CommunityLocker.sol:105:    uint256 public gasPriceTimestamp;
contracts/schain/CommunityLocker.sol:244:     * @dev Set value of {mainnetGasPrice}.
contracts/schain/CommunityLocker.sol:252:    function setGasPrice(
contracts/schain/CommunityLocker.sol:260:        require(timestamp > gasPriceTimestamp, "Gas price timestamp already updated");
contracts/schain/CommunityLocker.sol:269:            mainnetGasPrice,
contracts/schain/CommunityLocker.sol:272:        mainnetGasPrice = gasPrice;
contracts/schain/CommunityLocker.sol:273:        gasPriceTimestamp = timestamp;

exec
/bin/bash -lc 'rg -n "DelayedTransfer|TransferDelayed|retrieveFor|escalate|validateTransfer|rejectTransfer|setGasPrice|approveTransfers|activeEthTransfers" contracts/test AUDIT_BUNDLE.txt' in /tmp/codex-wul8xs7h
 succeeded in 0ms:
AUDIT_BUNDLE.txt:493:    function escalate(uint256 transferId) external;
AUDIT_BUNDLE.txt:501:    function rejectTransfer(uint transferId) external;
AUDIT_BUNDLE.txt:503:    function retrieveFor(address receiver) external;
AUDIT_BUNDLE.txt:509:    function validateTransfer(uint transferId) external;
AUDIT_BUNDLE.txt:931:    function setGasPrice(
AUDIT_BUNDLE.txt:4691:    enum DelayedTransferStatus {
AUDIT_BUNDLE.txt:4697:    struct DelayedTransfer {
AUDIT_BUNDLE.txt:4703:        DelayedTransferStatus status;
AUDIT_BUNDLE.txt:4735:    /// @custom:oz-retyped-from mapping(uint256 => struct DepositBoxERC20.DelayedTransfer)
AUDIT_BUNDLE.txt:4736:    mapping(uint256 => DelayedTransfer) public delayedTransfers;
AUDIT_BUNDLE.txt:4751:    event TransferDelayed(uint256 id, address receiver, address token, uint256 amount);
AUDIT_BUNDLE.txt:4849:            _createDelayedTransfer(schainHash, message, delay);
AUDIT_BUNDLE.txt:5018:        retrieveFor(msg.sender);
AUDIT_BUNDLE.txt:5029:    function escalate(uint256 transferId) external override {
AUDIT_BUNDLE.txt:5035:        require(delayedTransfers[transferId].status == DelayedTransferStatus.DELAYED, "The transfer has to be delayed");
AUDIT_BUNDLE.txt:5036:        delayedTransfers[transferId].status = DelayedTransferStatus.ARBITRAGE;
AUDIT_BUNDLE.txt:5052:    function validateTransfer(
AUDIT_BUNDLE.txt:5059:        DelayedTransfer storage transfer = delayedTransfers[transferId];
AUDIT_BUNDLE.txt:5060:        require(transfer.status == DelayedTransferStatus.ARBITRAGE, "Arbitrage has to be active");
AUDIT_BUNDLE.txt:5061:        transfer.status = DelayedTransferStatus.COMPLETED;
AUDIT_BUNDLE.txt:5074:    function rejectTransfer(
AUDIT_BUNDLE.txt:5081:        DelayedTransfer storage transfer = delayedTransfers[transferId];
AUDIT_BUNDLE.txt:5082:        require(transfer.status == DelayedTransferStatus.ARBITRAGE, "Arbitrage has to be active");
AUDIT_BUNDLE.txt:5083:        transfer.status = DelayedTransferStatus.COMPLETED;
AUDIT_BUNDLE.txt:5170:            DelayedTransfer storage transfer = delayedTransfers[uint256(delayedTransfersByReceiver[receiver].at(i))];
AUDIT_BUNDLE.txt:5171:            DelayedTransferStatus status = transfer.status;
AUDIT_BUNDLE.txt:5173:                if (status == DelayedTransferStatus.DELAYED || status == DelayedTransferStatus.ARBITRAGE) {
AUDIT_BUNDLE.txt:5195:            DelayedTransfer storage transfer = delayedTransfers[uint256(delayedTransfersByReceiver[receiver].at(i))];
AUDIT_BUNDLE.txt:5196:            DelayedTransferStatus status = transfer.status;
AUDIT_BUNDLE.txt:5198:                if (status != DelayedTransferStatus.COMPLETED) {
AUDIT_BUNDLE.txt:5201:                if (status == DelayedTransferStatus.DELAYED) {
AUDIT_BUNDLE.txt:5246:    function retrieveFor(address receiver) public override {
AUDIT_BUNDLE.txt:5254:        DelayedTransfer[] memory transfers = new DelayedTransfer[](transfersAmount);
AUDIT_BUNDLE.txt:5258:            DelayedTransfer memory transfer = delayedTransfers[transferId];
AUDIT_BUNDLE.txt:5259:            if (transfer.status != DelayedTransferStatus.COMPLETED) {
AUDIT_BUNDLE.txt:5264:                    if (transfer.status == DelayedTransferStatus.DELAYED) {
AUDIT_BUNDLE.txt:5270:                        _removeOldestDelayedTransfer(receiver);
AUDIT_BUNDLE.txt:5272:                        delayedTransfers[transferId].status = DelayedTransferStatus.COMPLETED;
AUDIT_BUNDLE.txt:5283:                    _removeOldestDelayedTransfer(receiver);
AUDIT_BUNDLE.txt:5363:        DelayedTransfer[] memory transfers,
AUDIT_BUNDLE.txt:5370:            DelayedTransfer memory transfer = transfers[i];
AUDIT_BUNDLE.txt:5496:     * Create instance of DelayedTransfer and initialize all auxiliary fields.
AUDIT_BUNDLE.txt:5498:    function _createDelayedTransfer(
AUDIT_BUNDLE.txt:5506:        delayedTransfers[delayId] = DelayedTransfer({
AUDIT_BUNDLE.txt:5512:            status: DelayedTransferStatus.DELAYED
AUDIT_BUNDLE.txt:5515:        emit TransferDelayed(delayId, message.receiver, message.token, message.amount);
AUDIT_BUNDLE.txt:5519:     * Remove instance of DelayedTransfer and clean auxiliary fields.
AUDIT_BUNDLE.txt:5521:    function _removeOldestDelayedTransfer(address receiver) private {
AUDIT_BUNDLE.txt:6103:    mapping(address => uint256) public approveTransfers;
AUDIT_BUNDLE.txt:6109:    mapping(SchainHash => bool) public activeEthTransfers;
AUDIT_BUNDLE.txt:6160:        if (!activeEthTransfers[schainHash]) {
AUDIT_BUNDLE.txt:6161:            approveTransfers[message.receiver] += message.amount;
AUDIT_BUNDLE.txt:6176:        require(approveTransfers[msg.sender] > 0, "User has insufficient ETH");
AUDIT_BUNDLE.txt:6177:        uint256 amount = approveTransfers[msg.sender];
AUDIT_BUNDLE.txt:6178:        approveTransfers[msg.sender] = 0;
AUDIT_BUNDLE.txt:6220:        require(!activeEthTransfers[schainHash], "Active eth transfers enabled");
AUDIT_BUNDLE.txt:6222:        activeEthTransfers[schainHash] = true;
AUDIT_BUNDLE.txt:6240:        require(activeEthTransfers[schainHash], "Active eth transfers disabled");
AUDIT_BUNDLE.txt:6242:        activeEthTransfers[schainHash] = false;
AUDIT_BUNDLE.txt:7572:    function setGasPrice(

exec
/bin/bash -lc "nl -ba contracts/mainnet/DepositBoxes/DepositBoxERC20.sol | sed -n '684,712p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   684	        address receiver
   685	    )
   686	        public
   687	        override
   688	        rightTransaction(schainName, receiver)
   689	        whenNotKilled(_schainHash(schainName))
   690	    {
   691	        SchainHash schainHash = _schainHash(schainName);
   692	        address contractReceiver = schainLinks[schainHash];
   693	        require(contractReceiver != address(0), "Unconnected chain");
   694	        require(
   695	            IERC20Upgradeable(erc20OnMainnet).allowance(msg.sender, address(this)) >= amount,
   696	            "DepositBox was not approved for ERC20 token"
   697	        );
   698	        bytes memory data = _receiveERC20(
   699	            schainName,
   700	            erc20OnMainnet,
   701	            receiver,
   702	            amount
   703	        );
   704	        _saveTransferredAmount(schainHash, erc20OnMainnet, amount);
   705	        IERC20MetadataUpgradeable(erc20OnMainnet).safeTransferFrom(msg.sender, address(this), amount);
   706	        messageProxy.postOutgoingMessage(
   707	            schainHash,
   708	            contractReceiver,
   709	            data
   710	        );
   711	    }
   712	

exec
/bin/bash -lc "nl -ba contracts/schain/TokenManagers/TokenManagerERC20.sol | sed -n '338,356p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   338	        );
   339	        bytes memory data = Messages.encodeTransferErc20Message(address(contractOnMainChain), to, amount);
   340	        if (isMainChainToken) {
   341	            require(chainHash != MAINNET_HASH, "Main chain token could not be transferred to Mainnet");
   342	            data = _receiveERC20(
   343	                chainHash,
   344	                address(contractOnSchain),
   345	                to,
   346	                amount
   347	            );
   348	            _saveTransferredAmount(chainHash, address(contractOnSchain), amount);
   349	            require(
   350	                contractOnSchain.transferFrom(msg.sender, address(this), amount),
   351	                "Transfer was failed"
   352	            );
   353	        } else {
   354	            require(
   355	                contractOnSchain.transferFrom(msg.sender, address(this), amount),
   356	                "Transfer was failed"

exec
/bin/bash -lc "nl -ba contracts/mainnet/DepositBoxes/DepositBoxERC20.sol | sed -n '525,592p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   525	    }
   526	
   527	    /**
   528	     * @dev Get amount of tokens that are delayed for specified receiver
   529	     */
   530	    function getDelayedAmount(address receiver, address token) external view override returns (uint256 value) {
   531	        uint256 delayedTransfersAmount = delayedTransfersByReceiver[receiver].length();
   532	        for (uint256 i = 0; i < delayedTransfersAmount; ++i) {
   533	            DelayedTransfer storage transfer = delayedTransfers[uint256(delayedTransfersByReceiver[receiver].at(i))];
   534	            DelayedTransferStatus status = transfer.status;
   535	            if (transfer.token == token) {
   536	                if (status == DelayedTransferStatus.DELAYED || status == DelayedTransferStatus.ARBITRAGE) {
   537	                    value += transfer.amount;
   538	                }
   539	            }
   540	        }
   541	    }
   542	
   543	    /**
   544	     * @dev Get timestamp of next unlock of tokens that are delayed for specified receiver
   545	     */
   546	    function getNextUnlockTimestamp(
   547	        address receiver,
   548	        address token
   549	    )
   550	        external
   551	        view
   552	        override
   553	        returns (uint256 unlockTimestamp)
   554	    {
   555	        uint256 delayedTransfersAmount = delayedTransfersByReceiver[receiver].length();
   556	        unlockTimestamp = type(uint256).max;
   557	        for (uint256 i = 0; i < delayedTransfersAmount; ++i) {
   558	            DelayedTransfer storage transfer = delayedTransfers[uint256(delayedTransfersByReceiver[receiver].at(i))];
   559	            DelayedTransferStatus status = transfer.status;
   560	            if (transfer.token == token) {
   561	                if (status != DelayedTransferStatus.COMPLETED) {
   562	                    unlockTimestamp = MathUpgradeable.min(unlockTimestamp, transfer.untilTimestamp);
   563	                }
   564	                if (status == DelayedTransferStatus.DELAYED) {
   565	                    break;
   566	                }
   567	            }
   568	        }
   569	    }
   570	
   571	    /**
   572	     * @dev Get amount of addresses that are added to the whitelist
   573	     */
   574	    function getTrustedReceiversAmount(SchainHash schainHash) external view override returns (uint256) {
   575	        return _delayConfig[schainHash].trustedReceivers.length();
   576	    }
   577	
   578	    /**
   579	     * @dev Get i-th address of the whitelist
   580	     */
   581	    function getTrustedReceiver(string calldata schainName, uint256 index) external view override returns (address) {
   582	        return _delayConfig[_schainHash(schainName)].trustedReceivers.at(index);
   583	    }
   584	
   585	    /**
   586	     * @dev Get amount of tokens that are considered as a big transfer
   587	     */
   588	    function getBigTransferThreshold(SchainHash schainHash, address token) external view override returns (uint256) {
   589	        return _delayConfig[schainHash].bigTransferThreshold[token];
   590	    }
   591	
   592	    /**

exec
/bin/bash -lc "nl -ba contracts/mainnet/DepositBoxes/DepositBoxERC20.sol | sed -n '742,786p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   742	    /**
   743	     * @dev Saves amount of tokens that was transferred to schain.
   744	     */
   745	    function _saveTransferredAmount(SchainHash schainHash, address erc20Token, uint256 amount) private {
   746	        transferredAmount[schainHash][erc20Token] += amount;
   747	    }
   748	
   749	    /**
   750	     * @dev Removes amount of tokens that was transferred from schain.
   751	     */
   752	    function _removeTransferredAmount(SchainHash schainHash, address erc20Token, uint256 amount) private {
   753	        transferredAmount[schainHash][erc20Token] -= amount;
   754	    }
   755	
   756	    /**
   757	     * @dev Allows DepositBoxERC20 to receive ERC20 tokens.
   758	     *
   759	     * Emits an {ERC20TokenReady} event.
   760	     *
   761	     * Requirements:
   762	     *
   763	     * - Amount must be less than or equal to the total supply of the ERC20 contract.
   764	     * - Whitelist should be turned off for auto adding tokens to DepositBoxERC20.
   765	     */
   766	    function _receiveERC20(
   767	        string calldata schainName,
   768	        address erc20OnMainnet,
   769	        address to,
   770	        uint256 amount
   771	    )
   772	        private
   773	        returns (bytes memory data)
   774	    {
   775	        SchainHash schainHash = _schainHash(schainName);
   776	        IERC20MetadataUpgradeable erc20 = IERC20MetadataUpgradeable(erc20OnMainnet);
   777	        uint256 totalSupply = erc20.totalSupply();
   778	        require(amount <= totalSupply, "Amount is incorrect");
   779	        bool isERC20AddedToSchain = _schainToERC20[schainHash].contains(erc20OnMainnet);
   780	        if (!isERC20AddedToSchain) {
   781	            require(!isWhitelisted(schainName), "Whitelist is enabled");
   782	            _addERC20ForSchain(schainName, erc20OnMainnet);
   783	            data = Messages.encodeTransferErc20AndTokenInfoMessage(
   784	                erc20OnMainnet,
   785	                to,
   786	                amount,

exec
/bin/bash -lc "nl -ba contracts/mainnet/DepositBoxes/DepositBoxERC20.sol | sed -n '544,568p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   544	     * @dev Get timestamp of next unlock of tokens that are delayed for specified receiver
   545	     */
   546	    function getNextUnlockTimestamp(
   547	        address receiver,
   548	        address token
   549	    )
   550	        external
   551	        view
   552	        override
   553	        returns (uint256 unlockTimestamp)
   554	    {
   555	        uint256 delayedTransfersAmount = delayedTransfersByReceiver[receiver].length();
   556	        unlockTimestamp = type(uint256).max;
   557	        for (uint256 i = 0; i < delayedTransfersAmount; ++i) {
   558	            DelayedTransfer storage transfer = delayedTransfers[uint256(delayedTransfersByReceiver[receiver].at(i))];
   559	            DelayedTransferStatus status = transfer.status;
   560	            if (transfer.token == token) {
   561	                if (status != DelayedTransferStatus.COMPLETED) {
   562	                    unlockTimestamp = MathUpgradeable.min(unlockTimestamp, transfer.untilTimestamp);
   563	                }
   564	                if (status == DelayedTransferStatus.DELAYED) {
   565	                    break;
   566	                }
   567	            }
   568	        }

exec
/bin/bash -lc "nl -ba contracts/mainnet/DepositBoxes/DepositBoxERC20.sol | sed -n '524,542p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   524	        }
   525	    }
   526	
   527	    /**
   528	     * @dev Get amount of tokens that are delayed for specified receiver
   529	     */
   530	    function getDelayedAmount(address receiver, address token) external view override returns (uint256 value) {
   531	        uint256 delayedTransfersAmount = delayedTransfersByReceiver[receiver].length();
   532	        for (uint256 i = 0; i < delayedTransfersAmount; ++i) {
   533	            DelayedTransfer storage transfer = delayedTransfers[uint256(delayedTransfersByReceiver[receiver].at(i))];
   534	            DelayedTransferStatus status = transfer.status;
   535	            if (transfer.token == token) {
   536	                if (status == DelayedTransferStatus.DELAYED || status == DelayedTransferStatus.ARBITRAGE) {
   537	                    value += transfer.amount;
   538	                }
   539	            }
   540	        }
   541	    }
   542	

exec
/bin/bash -lc "nl -ba contracts/mainnet/DepositBoxes/DepositBoxERC20.sol | sed -n '612,666p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   612	            _QUEUE_PROCESSING_LIMIT
   613	        );
   614	        uint256 currentIndex = 0;
   615	        bool retrieved = false;
   616	        uint256 countToTransfer = 0;
   617	        DelayedTransfer[] memory transfers = new DelayedTransfer[](transfersAmount);
   618	        uint256[] memory transfersIds = new uint256[](transfersAmount);
   619	        for (uint256 i = 0; i < transfersAmount; ++i) {
   620	            uint256 transferId = uint256(delayedTransfersByReceiver[receiver].at(currentIndex++));
   621	            DelayedTransfer memory transfer = delayedTransfers[transferId];
   622	            if (transfer.status != DelayedTransferStatus.COMPLETED) {
   623	                if (block.timestamp < transfer.untilTimestamp) {
   624	                    // disable detector until slither fixes false positive
   625	                    // https://github.com/crytic/slither/issues/778
   626	                    // slither-disable-next-line incorrect-equality
   627	                    if (transfer.status == DelayedTransferStatus.DELAYED) {
   628	                        break;
   629	                    } // else status is ARBITRAGE -> continue
   630	                } else { // it's time to unlock
   631	                    if (currentIndex == 1) {
   632	                        --currentIndex;
   633	                        _removeOldestDelayedTransfer(receiver);
   634	                    } else {
   635	                        delayedTransfers[transferId].status = DelayedTransferStatus.COMPLETED;
   636	                    }
   637	                    retrieved = true;
   638	                    transfers[countToTransfer] = transfer;
   639	                    transfersIds[countToTransfer] = transferId;
   640	                    ++countToTransfer;
   641	                }
   642	            } else { // status is COMPLETED
   643	                if (currentIndex == 1) {
   644	                    --currentIndex;
   645	                    retrieved = true;
   646	                    _removeOldestDelayedTransfer(receiver);
   647	                }
   648	            }
   649	        }
   650	        require(retrieved, "There are no transfers available for retrieving");
   651	        _doNTransfers(transfers, transfersIds, countToTransfer);
   652	    }
   653	
   654	    /**
   655	     * @dev Creates a new DepositBoxERC20 contract.
   656	     */
   657	    function initialize(
   658	        IContractManager contractManagerOfSkaleManagerValue,
   659	        ILinker linkerValue,
   660	        IMessageProxyForMainnet messageProxyValue
   661	    )
   662	        public
   663	        override(DepositBox, IDepositBox)
   664	        initializer
   665	    {
   666	        DepositBox.initialize(contractManagerOfSkaleManagerValue, linkerValue, messageProxyValue);

exec
/bin/bash -lc "nl -ba contracts/mainnet/DepositBoxes/DepositBoxERC20.sol | sed -n '835,879p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   835	     * the element is added at back - depthLimit index
   836	     */
   837	    function _addToDelayedQueueWithPriority(
   838	        DoubleEndedQueueUpgradeable.Bytes32Deque storage queue,
   839	        uint256 id,
   840	        uint256 until,
   841	        uint256 depthLimit
   842	    )
   843	        private
   844	    {
   845	        if (depthLimit == 0 || queue.empty()) {
   846	            queue.pushBack(bytes32(id));
   847	        } else {
   848	            if (delayedTransfers[uint256(queue.back())].untilTimestamp <= until) {
   849	                queue.pushBack(bytes32(id));
   850	            } else {
   851	                bytes32 lowPriorityValue = queue.popBack();
   852	                _addToDelayedQueueWithPriority(queue, id, until, depthLimit - 1);
   853	                queue.pushBack(lowPriorityValue);
   854	            }
   855	        }
   856	    }
   857	
   858	    /**
   859	     * Create instance of DelayedTransfer and initialize all auxiliary fields.
   860	     */
   861	    function _createDelayedTransfer(
   862	        SchainHash schainHash,
   863	        Messages.TransferErc20Message memory message,
   864	        uint256 delay
   865	    )
   866	        private
   867	    {
   868	        uint256 delayId = delayedTransfersSize++;
   869	        delayedTransfers[delayId] = DelayedTransfer({
   870	            receiver: message.receiver,
   871	            schainHash: schainHash,
   872	            token: message.token,
   873	            amount: message.amount,
   874	            untilTimestamp: block.timestamp + delay,
   875	            status: DelayedTransferStatus.DELAYED
   876	        });
   877	        _addToDelayedQueue(message.receiver, delayId, block.timestamp + delay);
   878	        emit TransferDelayed(delayId, message.receiver, message.token, message.amount);
   879	    }

exec
/bin/bash -lc "nl -ba contracts/mainnet/Linker.sol | sed -n '88,136p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
    88	     *
    89	     * - Numbers of mainnet contracts and schain contracts must be equal.
    90	     * - Mainnet contract must implement method `addSchainContract`.
    91	     */
    92	    function connectSchain(
    93	        string calldata schainName,
    94	        address[] calldata schainContracts
    95	    )
    96	        external
    97	        override
    98	        onlyLinker
    99	    {
   100	        require(schainContracts.length == _mainnetContracts.length(), "Incorrect number of addresses");
   101	        for (uint i = 0; i < schainContracts.length; i++) {
   102	            Twin(_mainnetContracts.at(i)).addSchainContract(schainName, schainContracts[i]);
   103	        }
   104	        messageProxy.addConnectedChain(schainName);
   105	    }
   106	
   107	    /**
   108	     * @dev Allows Schain owner and contract deployer to kill schain.
   109	     * To kill the schain, both entities must call this function, and the order is not important.
   110	     *
   111	     * Requirements:
   112	     *
   113	     * - Interchain connection should be turned off.
   114	     */
   115	    function kill(string calldata schainName) override external {
   116	        SchainHash schainHash = _schainHash(schainName);
   117	        if (statuses[schainHash] == KillProcess.NotKilled) {
   118	            if (hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) {
   119	                statuses[schainHash] = KillProcess.PartiallyKilledByContractOwner;
   120	            } else if (isSchainOwner(msg.sender, schainHash)) {
   121	                statuses[schainHash] = KillProcess.PartiallyKilledBySchainOwner;
   122	            } else {
   123	                revert("Not allowed");
   124	            }
   125	        } else if (
   126	            (
   127	                statuses[schainHash] == KillProcess.PartiallyKilledBySchainOwner &&
   128	                hasRole(DEFAULT_ADMIN_ROLE, msg.sender)
   129	            ) || (
   130	                statuses[schainHash] == KillProcess.PartiallyKilledByContractOwner &&
   131	                isSchainOwner(msg.sender, schainHash)
   132	            )
   133	        ) {
   134	            statuses[schainHash] = KillProcess.Killed;
   135	        } else {
   136	            revert("Already killed or incorrect sender");

exec
/bin/bash -lc "nl -ba contracts/mainnet/DepositBoxes/DepositBoxERC20.sol | sed -n '274,295p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   274	    )
   275	        external
   276	        override
   277	        onlySchainOwner(schainName)
   278	    {
   279	        SchainHash schainHash = _schainHash(schainName);
   280	        emit BigTransferThresholdIsChanged(
   281	            schainHash,
   282	            token,
   283	            _delayConfig[schainHash].bigTransferThreshold[token],
   284	            value
   285	        );
   286	        _delayConfig[schainHash].bigTransferThreshold[token] = value;
   287	    }
   288	
   289	    /**
   290	     * @dev Set a time delay.
   291	     * If amount of tokens that exits IMA is bigger than a threshold
   292	     * the transfer is delayed for set amount of time
   293	     * and can be canceled by a voting
   294	     *
   295	     * Requirements:

exec
/bin/bash -lc "nl -ba contracts/mainnet/DepositBoxes/DepositBoxERC20.sol | sed -n '228,270p'" in /tmp/codex-wul8xs7h
 succeeded in 0ms:
   228	    function addERC20TokenByOwner(string calldata schainName, address erc20OnMainnet)
   229	        external
   230	        override
   231	        onlySchainOwner(schainName)
   232	        whenNotKilled(_schainHash(schainName))
   233	    {
   234	        _addERC20ForSchain(schainName, erc20OnMainnet);
   235	    }
   236	
   237	    /**
   238	     * @dev Allows Schain owner to return each user their tokens.
   239	     * The Schain owner decides which tokens to send to which address,
   240	     * since the contract on mainnet does not store information about which tokens belong to whom.
   241	     *
   242	     * Requirements:
   243	     *
   244	     * - Amount of tokens on schain should be equal or more than transferred amount.
   245	     * - msg.sender should be an owner of schain
   246	     * - IMA transfers Mainnet <-> schain should be killed
   247	     */
   248	    function getFunds(string calldata schainName, address erc20OnMainnet, address receiver, uint amount)
   249	        external
   250	        override
   251	        onlySchainOwner(schainName)
   252	        whenKilled(_schainHash(schainName))
   253	    {
   254	        SchainHash schainHash = _schainHash(schainName);
   255	        require(transferredAmount[schainHash][erc20OnMainnet] >= amount, "Incorrect amount");
   256	        _removeTransferredAmount(schainHash, erc20OnMainnet, amount);
   257	        IERC20MetadataUpgradeable(erc20OnMainnet).safeTransfer(receiver, amount);
   258	    }
   259	
   260	    /**
   261	     * @dev Set a threshold amount of tokens.
   262	     * If amount of tokens that exits IMA is bigger than the threshold
   263	     * the transfer is delayed for configurable amount of time
   264	     * and can be canceled by a voting
   265	     *
   266	     * Requirements:
   267	     *
   268	     * - msg.sender should be an owner of schain
   269	     */
   270	    function setBigTransferValue(

codex
{"no_findings":false,"summary":"Fee-on-transfer ERC20s are accounted at the requested amount instead of the amount actually received, which can overmint wrapped balances and drain bridge collateral.","findings":[{"severity":"high","category":"other","title":"Fee-on-transfer ERC20s can overmint bridge balances and drain pooled collateral","exploit_scenario":"An attacker bridges a deflationary ERC20 that burns or taxes 10% on transfer. `DepositBoxERC20.depositERC20Direct` records and forwards `amount=100` to the destination chain, then calls `safeTransferFrom` once without checking how many tokens actually arrived. Only 90 tokens reach the box, but 100 wrapped tokens are minted on the schain. The attacker can later exit the full 100 back to mainnet (or another schain), extracting the 10-token shortfall from liquidity supplied by honest users. The same invariant break exists in schain-to-schain transfers of main-chain ERC20s, where `TokenManagerERC20` escrows `amount` before verifying the real balance delta.","recommendation":"Measure balance deltas before and after each ERC20 transfer and use the actual received amount for `transferredAmount` accounting and for the bridged message payload. Reject tokens whose received amount is smaller than the requested amount if exact-amount semantics are required.","evidence":"`DepositBoxERC20.depositERC20Direct` builds the outgoing message and increments `transferredAmount` using the caller-supplied `amount`, then performs a single `safeTransferFrom` without comparing pre/post balances. `TokenManagerERC20._exit` mirrors this pattern for schain-to-schain escrow of main-chain tokens by saving `amount` before `transferFrom` and never validating the received balance.","locations":["contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:691","contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:704","contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:705","contracts/schain/TokenManagers/TokenManagerERC20.sol:340","contracts/schain/TokenManagers/TokenManagerERC20.sol:348","contracts/schain/TokenManagers/TokenManagerERC20.sol:350"]}]}
tokens used
168,153
Metadata
{
    "codex": {
        "ran_at": "2026-04-04T03:44:21.688682+00:00",
        "returncode": 0
    },
    "project": {
        "mode": "source",
        "file_count": 143,
        "import_count": 168,
        "first_party_contracts_count": 105
    },
    "summary": "Fee-on-transfer ERC20s are accounted at the requested amount instead of the amount actually received, which can overmint wrapped balances and drain bridge collateral.",
    "raw_final": {
        "summary": "Fee-on-transfer ERC20s are accounted at the requested amount instead of the amount actually received, which can overmint wrapped balances and drain bridge collateral.",
        "findings": [
            {
                "title": "Fee-on-transfer ERC20s can overmint bridge balances and drain pooled collateral",
                "category": "other",
                "evidence": "`DepositBoxERC20.depositERC20Direct` builds the outgoing message and increments `transferredAmount` using the caller-supplied `amount`, then performs a single `safeTransferFrom` without comparing pre/post balances. `TokenManagerERC20._exit` mirrors this pattern for schain-to-schain escrow of main-chain tokens by saving `amount` before `transferFrom` and never validating the received balance.",
                "severity": "high",
                "locations": [
                    "contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:691",
                    "contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:704",
                    "contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:705",
                    "contracts/schain/TokenManagers/TokenManagerERC20.sol:340",
                    "contracts/schain/TokenManagers/TokenManagerERC20.sol:348",
                    "contracts/schain/TokenManagers/TokenManagerERC20.sol:350"
                ],
                "recommendation": "Measure balance deltas before and after each ERC20 transfer and use the actual received amount for `transferredAmount` accounting and for the bridged message payload. Reject tokens whose received amount is smaller than the requested amount if exact-amount semantics are required.",
                "exploit_scenario": "An attacker bridges a deflationary ERC20 that burns or taxes 10% on transfer. `DepositBoxERC20.depositERC20Direct` records and forwards `amount=100` to the destination chain, then calls `safeTransferFrom` once without checking how many tokens actually arrived. Only 90 tokens reach the box, but 100 wrapped tokens are minted on the schain. The attacker can later exit the full 100 back to mainnet (or another schain), extracting the 10-token shortfall from liquidity supplied by honest users. The same invariant break exists in schain-to-schain transfers of main-chain ERC20s, where `TokenManagerERC20` escrows `amount` before verifying the real balance delta."
            }
        ],
        "no_findings": false
    },
    "no_findings": false,
    "proxy_context": {
        "proxy_run_id": "019d5666-7210-72a7-9067-e58ac94c06d2",
        "proxy_address": "0x588801ca36558310d91234afc2511502282b1621",
        "implementation_run_id": "019d5666-e916-7246-acfd-c2f7d6ef4d74",
        "implementation_address": "0x29353f77c6b0d3772d73e708cc8e1fca08c80c11"
    },
    "schema_version": 1
}

Opcode Heuristics

tool.cast_disasm
Delegatecall
3
Selfdestruct
0
CREATE2
0
CALL-family (heavy)
0
EXT*/BALANCE
1
Total opcodes
1180
Flags
delegatecall_present
View cast disassembly output
00000000: PUSH1 0x80
00000002: PUSH1 0x40
00000004: MSTORE
00000005: PUSH1 0x04
00000007: CALLDATASIZE
00000008: LT
00000009: PUSH2 0x004e
0000000c: JUMPI
0000000d: PUSH1 0x00
0000000f: CALLDATALOAD
00000010: PUSH1 0xe0
00000012: SHR
00000013: DUP1
00000014: PUSH4 0x3659cfe6
00000019: EQ
0000001a: PUSH2 0x0065
0000001d: JUMPI
0000001e: DUP1
0000001f: PUSH4 0x4f1ef286
00000024: EQ
00000025: PUSH2 0x0085
00000028: JUMPI
00000029: DUP1
0000002a: PUSH4 0x5c60da1b
0000002f: EQ
00000030: PUSH2 0x0098
00000033: JUMPI
00000034: DUP1
00000035: PUSH4 0x8f283970
0000003a: EQ
0000003b: PUSH2 0x00c9
0000003e: JUMPI
0000003f: DUP1
00000040: PUSH4 0xf851a440
00000045: EQ
00000046: PUSH2 0x00e9
00000049: JUMPI
0000004a: PUSH2 0x005d
0000004d: JUMP
0000004e: JUMPDEST
0000004f: CALLDATASIZE
00000050: PUSH2 0x005d
00000053: JUMPI
00000054: PUSH2 0x005b
00000057: PUSH2 0x00fe
0000005a: JUMP
0000005b: JUMPDEST
0000005c: STOP
0000005d: JUMPDEST
0000005e: PUSH2 0x005b
00000061: PUSH2 0x00fe
00000064: JUMP
00000065: JUMPDEST
00000066: CALLVALUE
00000067: DUP1
00000068: ISZERO
00000069: PUSH2 0x0071
0000006c: JUMPI
0000006d: PUSH1 0x00
0000006f: DUP1
00000070: REVERT
00000071: JUMPDEST
00000072: POP
00000073: PUSH2 0x005b
00000076: PUSH2 0x0080
00000079: CALLDATASIZE
0000007a: PUSH1 0x04
0000007c: PUSH2 0x06ed
0000007f: JUMP
00000080: JUMPDEST
00000081: PUSH2 0x0118
00000084: JUMP
00000085: JUMPDEST
00000086: PUSH2 0x005b
00000089: PUSH2 0x0093
0000008c: CALLDATASIZE
0000008d: PUSH1 0x04
0000008f: PUSH2 0x0707
00000092: JUMP
00000093: JUMPDEST
00000094: PUSH2 0x0164
00000097: JUMP
00000098: JUMPDEST
00000099: CALLVALUE
0000009a: DUP1
0000009b: ISZERO
0000009c: PUSH2 0x00a4
0000009f: JUMPI
000000a0: PUSH1 0x00
000000a2: DUP1
000000a3: REVERT
000000a4: JUMPDEST
000000a5: POP
000000a6: PUSH2 0x00ad
000000a9: PUSH2 0x01da
000000ac: JUMP
000000ad: JUMPDEST
000000ae: PUSH1 0x40
000000b0: MLOAD
000000b1: PUSH1 0x01
000000b3: PUSH1 0x01
000000b5: PUSH1 0xa0
000000b7: SHL
000000b8: SUB
000000b9: SWAP1
000000ba: SWAP2
000000bb: AND
000000bc: DUP2
000000bd: MSTORE
000000be: PUSH1 0x20
000000c0: ADD
000000c1: PUSH1 0x40
000000c3: MLOAD
000000c4: DUP1
000000c5: SWAP2
000000c6: SUB
000000c7: SWAP1
000000c8: RETURN
000000c9: JUMPDEST
000000ca: CALLVALUE
000000cb: DUP1
000000cc: ISZERO
000000cd: PUSH2 0x00d5
000000d0: JUMPI
000000d1: PUSH1 0x00
000000d3: DUP1
000000d4: REVERT
000000d5: JUMPDEST
000000d6: POP
000000d7: PUSH2 0x005b
000000da: PUSH2 0x00e4
000000dd: CALLDATASIZE
000000de: PUSH1 0x04
000000e0: PUSH2 0x06ed
000000e3: JUMP
000000e4: JUMPDEST
000000e5: PUSH2 0x0217
000000e8: JUMP
000000e9: JUMPDEST
000000ea: CALLVALUE
000000eb: DUP1
000000ec: ISZERO
000000ed: PUSH2 0x00f5
000000f0: JUMPI
000000f1: PUSH1 0x00
000000f3: DUP1
000000f4: REVERT
000000f5: JUMPDEST
000000f6: POP
000000f7: PUSH2 0x00ad
000000fa: PUSH2 0x0241
000000fd: JUMP
000000fe: JUMPDEST
000000ff: PUSH2 0x0106
00000102: PUSH2 0x02a2
00000105: JUMP
00000106: JUMPDEST
00000107: PUSH2 0x0116
0000010a: PUSH2 0x0111
0000010d: PUSH2 0x0346
00000110: JUMP
00000111: JUMPDEST
00000112: PUSH2 0x0355
00000115: JUMP
00000116: JUMPDEST
00000117: JUMP
00000118: JUMPDEST
00000119: PUSH2 0x0120
0000011c: PUSH2 0x0379
0000011f: JUMP
00000120: JUMPDEST
00000121: PUSH1 0x01
00000123: PUSH1 0x01
00000125: PUSH1 0xa0
00000127: SHL
00000128: SUB
00000129: AND
0000012a: CALLER
0000012b: PUSH1 0x01
0000012d: PUSH1 0x01
0000012f: PUSH1 0xa0
00000131: SHL
00000132: SUB
00000133: AND
00000134: EQ
00000135: ISZERO
00000136: PUSH2 0x0159
00000139: JUMPI
0000013a: PUSH2 0x0154
0000013d: DUP2
0000013e: PUSH1 0x40
00000140: MLOAD
00000141: DUP1
00000142: PUSH1 0x20
00000144: ADD
00000145: PUSH1 0x40
00000147: MSTORE
00000148: DUP1
00000149: PUSH1 0x00
0000014b: DUP2
0000014c: MSTORE
0000014d: POP
0000014e: PUSH1 0x00
00000150: PUSH2 0x03ac
00000153: JUMP
00000154: JUMPDEST
00000155: PUSH2 0x0161
00000158: JUMP
00000159: JUMPDEST
0000015a: PUSH2 0x0161
0000015d: PUSH2 0x00fe
00000160: JUMP
00000161: JUMPDEST
00000162: POP
00000163: JUMP
00000164: JUMPDEST
00000165: PUSH2 0x016c
00000168: PUSH2 0x0379
0000016b: JUMP
0000016c: JUMPDEST
0000016d: PUSH1 0x01
0000016f: PUSH1 0x01
00000171: PUSH1 0xa0
00000173: SHL
00000174: SUB
00000175: AND
00000176: CALLER
00000177: PUSH1 0x01
00000179: PUSH1 0x01
0000017b: PUSH1 0xa0
0000017d: SHL
0000017e: SUB
0000017f: AND
00000180: EQ
00000181: ISZERO
00000182: PUSH2 0x01cd
00000185: JUMPI
00000186: PUSH2 0x01c8
00000189: DUP4
0000018a: DUP4
0000018b: DUP4
0000018c: DUP1
0000018d: DUP1
0000018e: PUSH1 0x1f
00000190: ADD
00000191: PUSH1 0x20
00000193: DUP1
00000194: SWAP2
00000195: DIV
00000196: MUL
00000197: PUSH1 0x20
00000199: ADD
0000019a: PUSH1 0x40
0000019c: MLOAD
0000019d: SWAP1
0000019e: DUP2
0000019f: ADD
000001a0: PUSH1 0x40
000001a2: MSTORE
000001a3: DUP1
000001a4: SWAP4
000001a5: SWAP3
000001a6: SWAP2
000001a7: SWAP1
000001a8: DUP2
000001a9: DUP2
000001aa: MSTORE
000001ab: PUSH1 0x20
000001ad: ADD
000001ae: DUP4
000001af: DUP4
000001b0: DUP1
000001b1: DUP3
000001b2: DUP5
000001b3: CALLDATACOPY
000001b4: PUSH1 0x00
000001b6: SWAP3
000001b7: ADD
000001b8: SWAP2
000001b9: SWAP1
000001ba: SWAP2
000001bb: MSTORE
000001bc: POP
000001bd: PUSH1 0x01
000001bf: SWAP3
000001c0: POP
000001c1: PUSH2 0x03ac
000001c4: SWAP2
000001c5: POP
000001c6: POP
000001c7: JUMP
000001c8: JUMPDEST
000001c9: PUSH2 0x01d5
000001cc: JUMP
000001cd: JUMPDEST
000001ce: PUSH2 0x01d5
000001d1: PUSH2 0x00fe
000001d4: JUMP
000001d5: JUMPDEST
000001d6: POP
000001d7: POP
000001d8: POP
000001d9: JUMP
000001da: JUMPDEST
000001db: PUSH1 0x00
000001dd: PUSH2 0x01e4
000001e0: PUSH2 0x0379
000001e3: JUMP
000001e4: JUMPDEST
000001e5: PUSH1 0x01
000001e7: PUSH1 0x01
000001e9: PUSH1 0xa0
000001eb: SHL
000001ec: SUB
000001ed: AND
000001ee: CALLER
000001ef: PUSH1 0x01
000001f1: PUSH1 0x01
000001f3: PUSH1 0xa0
000001f5: SHL
000001f6: SUB
000001f7: AND
000001f8: EQ
000001f9: ISZERO
000001fa: PUSH2 0x020c
000001fd: JUMPI
000001fe: PUSH2 0x0205
00000201: PUSH2 0x0346
00000204: JUMP
00000205: JUMPDEST
00000206: SWAP1
00000207: POP
00000208: PUSH2 0x0214
0000020b: JUMP
0000020c: JUMPDEST
0000020d: PUSH2 0x0214
00000210: PUSH2 0x00fe
00000213: JUMP
00000214: JUMPDEST
00000215: SWAP1
00000216: JUMP
00000217: JUMPDEST
00000218: PUSH2 0x021f
0000021b: PUSH2 0x0379
0000021e: JUMP
0000021f: JUMPDEST
00000220: PUSH1 0x01
00000222: PUSH1 0x01
00000224: PUSH1 0xa0
00000226: SHL
00000227: SUB
00000228: AND
00000229: CALLER
0000022a: PUSH1 0x01
0000022c: PUSH1 0x01
0000022e: PUSH1 0xa0
00000230: SHL
00000231: SUB
00000232: AND
00000233: EQ
00000234: ISZERO
00000235: PUSH2 0x0159
00000238: JUMPI
00000239: PUSH2 0x0154
0000023c: DUP2
0000023d: PUSH2 0x040b
00000240: JUMP
00000241: JUMPDEST
00000242: PUSH1 0x00
00000244: PUSH2 0x024b
00000247: PUSH2 0x0379
0000024a: JUMP
0000024b: JUMPDEST
0000024c: PUSH1 0x01
0000024e: PUSH1 0x01
00000250: PUSH1 0xa0
00000252: SHL
00000253: SUB
00000254: AND
00000255: CALLER
00000256: PUSH1 0x01
00000258: PUSH1 0x01
0000025a: PUSH1 0xa0
0000025c: SHL
0000025d: SUB
0000025e: AND
0000025f: EQ
00000260: ISZERO
00000261: PUSH2 0x020c
00000264: JUMPI
00000265: PUSH2 0x0205
00000268: PUSH2 0x0379
0000026b: JUMP
0000026c: JUMPDEST
0000026d: PUSH1 0x60
0000026f: PUSH2 0x0291
00000272: DUP4
00000273: DUP4
00000274: PUSH1 0x40
00000276: MLOAD
00000277: DUP1
00000278: PUSH1 0x60
0000027a: ADD
0000027b: PUSH1 0x40
0000027d: MSTORE
0000027e: DUP1
0000027f: PUSH1 0x27
00000281: DUP2
00000282: MSTORE
00000283: PUSH1 0x20
00000285: ADD
00000286: PUSH2 0x0801
00000289: PUSH1 0x27
0000028b: SWAP2
0000028c: CODECOPY
0000028d: PUSH2 0x045f
00000290: JUMP
00000291: JUMPDEST
00000292: SWAP4
00000293: SWAP3
00000294: POP
00000295: POP
00000296: POP
00000297: JUMP
00000298: JUMPDEST
00000299: DUP1
0000029a: EXTCODESIZE
0000029b: ISZERO
0000029c: ISZERO
0000029d: JUMPDEST
0000029e: SWAP2
0000029f: SWAP1
000002a0: POP
000002a1: JUMP
000002a2: JUMPDEST
000002a3: PUSH2 0x02aa
000002a6: PUSH2 0x0379
000002a9: JUMP
000002aa: JUMPDEST
000002ab: PUSH1 0x01
000002ad: PUSH1 0x01
000002af: PUSH1 0xa0
000002b1: SHL
000002b2: SUB
000002b3: AND
000002b4: CALLER
000002b5: PUSH1 0x01
000002b7: PUSH1 0x01
000002b9: PUSH1 0xa0
000002bb: SHL
000002bc: SUB
000002bd: AND
000002be: EQ
000002bf: ISZERO
000002c0: PUSH2 0x0341
000002c3: JUMPI
000002c4: PUSH1 0x40
000002c6: MLOAD
000002c7: PUSH3 0x461bcd
000002cb: PUSH1 0xe5
000002cd: SHL
000002ce: DUP2
000002cf: MSTORE
000002d0: PUSH1 0x20
000002d2: PUSH1 0x04
000002d4: DUP3
000002d5: ADD
000002d6: MSTORE
000002d7: PUSH1 0x42
000002d9: PUSH1 0x24
000002db: DUP3
000002dc: ADD
000002dd: MSTORE
000002de: PUSH32 0x5472616e73706172656e745570677261646561626c6550726f78793a2061646d
000002ff: PUSH1 0x44
00000301: DUP3
00000302: ADD
00000303: MSTORE
00000304: PUSH32 0x696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267
00000325: PUSH1 0x64
00000327: DUP3
00000328: ADD
00000329: MSTORE
0000032a: PUSH2 0x195d
0000032d: PUSH1 0xf2
0000032f: SHL
00000330: PUSH1 0x84
00000332: DUP3
00000333: ADD
00000334: MSTORE
00000335: PUSH1 0xa4
00000337: ADD
00000338: JUMPDEST
00000339: PUSH1 0x40
0000033b: MLOAD
0000033c: DUP1
0000033d: SWAP2
0000033e: SUB
0000033f: SWAP1
00000340: REVERT
00000341: JUMPDEST
00000342: PUSH2 0x0116
00000345: JUMP
00000346: JUMPDEST
00000347: PUSH1 0x00
00000349: PUSH2 0x0350
0000034c: PUSH2 0x053a
0000034f: JUMP
00000350: JUMPDEST
00000351: SWAP1
00000352: POP
00000353: SWAP1
00000354: JUMP
00000355: JUMPDEST
00000356: CALLDATASIZE
00000357: PUSH1 0x00
00000359: DUP1
0000035a: CALLDATACOPY
0000035b: PUSH1 0x00
0000035d: DUP1
0000035e: CALLDATASIZE
0000035f: PUSH1 0x00
00000361: DUP5
00000362: GAS
00000363: DELEGATECALL
00000364: RETURNDATASIZE
00000365: PUSH1 0x00
00000367: DUP1
00000368: RETURNDATACOPY
00000369: DUP1
0000036a: DUP1
0000036b: ISZERO
0000036c: PUSH2 0x0374
0000036f: JUMPI
00000370: RETURNDATASIZE
00000371: PUSH1 0x00
00000373: RETURN
00000374: JUMPDEST
00000375: RETURNDATASIZE
00000376: PUSH1 0x00
00000378: REVERT
00000379: JUMPDEST
0000037a: PUSH1 0x00
0000037c: PUSH32 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
0000039d: JUMPDEST
0000039e: SLOAD
0000039f: PUSH1 0x01
000003a1: PUSH1 0x01
000003a3: PUSH1 0xa0
000003a5: SHL
000003a6: SUB
000003a7: AND
000003a8: SWAP1
000003a9: POP
000003aa: SWAP1
000003ab: JUMP
000003ac: JUMPDEST
000003ad: PUSH2 0x03b5
000003b0: DUP4
000003b1: PUSH2 0x0562
000003b4: JUMP
000003b5: JUMPDEST
000003b6: PUSH1 0x40
000003b8: MLOAD
000003b9: PUSH1 0x01
000003bb: PUSH1 0x01
000003bd: PUSH1 0xa0
000003bf: SHL
000003c0: SUB
000003c1: DUP5
000003c2: AND
000003c3: SWAP1
000003c4: PUSH32 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b
000003e5: SWAP1
000003e6: PUSH1 0x00
000003e8: SWAP1
000003e9: LOG2
000003ea: PUSH1 0x00
000003ec: DUP3
000003ed: MLOAD
000003ee: GT
000003ef: DUP1
000003f0: PUSH2 0x03f6
000003f3: JUMPI
000003f4: POP
000003f5: DUP1
000003f6: JUMPDEST
000003f7: ISZERO
000003f8: PUSH2 0x01d5
000003fb: JUMPI
000003fc: PUSH2 0x0405
000003ff: DUP4
00000400: DUP4
00000401: PUSH2 0x026c
00000404: JUMP
00000405: JUMPDEST
00000406: POP
00000407: POP
00000408: POP
00000409: POP
0000040a: JUMP
0000040b: JUMPDEST
0000040c: PUSH32 0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f
0000042d: PUSH2 0x0434
00000430: PUSH2 0x0379
00000433: JUMP
00000434: JUMPDEST
00000435: PUSH1 0x40
00000437: DUP1
00000438: MLOAD
00000439: PUSH1 0x01
0000043b: PUSH1 0x01
0000043d: PUSH1 0xa0
0000043f: SHL
00000440: SUB
00000441: SWAP3
00000442: DUP4
00000443: AND
00000444: DUP2
00000445: MSTORE
00000446: SWAP2
00000447: DUP5
00000448: AND
00000449: PUSH1 0x20
0000044b: DUP4
0000044c: ADD
0000044d: MSTORE
0000044e: ADD
0000044f: PUSH1 0x40
00000451: MLOAD
00000452: DUP1
00000453: SWAP2
00000454: SUB
00000455: SWAP1
00000456: LOG1
00000457: PUSH2 0x0161
0000045a: DUP2
0000045b: PUSH2 0x0611
0000045e: JUMP
0000045f: JUMPDEST
00000460: PUSH1 0x60
00000462: PUSH2 0x046a
00000465: DUP5
00000466: PUSH2 0x0298
00000469: JUMP
0000046a: JUMPDEST
0000046b: PUSH2 0x04c5
0000046e: JUMPI
0000046f: PUSH1 0x40
00000471: MLOAD
00000472: PUSH3 0x461bcd
00000476: PUSH1 0xe5
00000478: SHL
00000479: DUP2
0000047a: MSTORE
0000047b: PUSH1 0x20
0000047d: PUSH1 0x04
0000047f: DUP3
00000480: ADD
00000481: MSTORE
00000482: PUSH1 0x26
00000484: PUSH1 0x24
00000486: DUP3
00000487: ADD
00000488: MSTORE
00000489: PUSH32 0x416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f
000004aa: PUSH1 0x44
000004ac: DUP3
000004ad: ADD
000004ae: MSTORE
000004af: PUSH6 0x1b9d1c9858dd
000004b6: PUSH1 0xd2
000004b8: SHL
000004b9: PUSH1 0x64
000004bb: DUP3
000004bc: ADD
000004bd: MSTORE
000004be: PUSH1 0x84
000004c0: ADD
000004c1: PUSH2 0x0338
000004c4: JUMP
000004c5: JUMPDEST
000004c6: PUSH1 0x00
000004c8: DUP1
000004c9: DUP6
000004ca: PUSH1 0x01
000004cc: PUSH1 0x01
000004ce: PUSH1 0xa0
000004d0: SHL
000004d1: SUB
000004d2: AND
000004d3: DUP6
000004d4: PUSH1 0x40
000004d6: MLOAD
000004d7: PUSH2 0x04e0
000004da: SWAP2
000004db: SWAP1
000004dc: PUSH2 0x0785
000004df: JUMP
000004e0: JUMPDEST
000004e1: PUSH1 0x00
000004e3: PUSH1 0x40
000004e5: MLOAD
000004e6: DUP1
000004e7: DUP4
000004e8: SUB
000004e9: DUP2
000004ea: DUP6
000004eb: GAS
000004ec: DELEGATECALL
000004ed: SWAP2
000004ee: POP
000004ef: POP
000004f0: RETURNDATASIZE
000004f1: DUP1
000004f2: PUSH1 0x00
000004f4: DUP2
000004f5: EQ
000004f6: PUSH2 0x051b
000004f9: JUMPI
000004fa: PUSH1 0x40
000004fc: MLOAD
000004fd: SWAP2
000004fe: POP
000004ff: PUSH1 0x1f
00000501: NOT
00000502: PUSH1 0x3f
00000504: RETURNDATASIZE
00000505: ADD
00000506: AND
00000507: DUP3
00000508: ADD
00000509: PUSH1 0x40
0000050b: MSTORE
0000050c: RETURNDATASIZE
0000050d: DUP3
0000050e: MSTORE
0000050f: RETURNDATASIZE
00000510: PUSH1 0x00
00000512: PUSH1 0x20
00000514: DUP5
00000515: ADD
00000516: RETURNDATACOPY
00000517: PUSH2 0x0520
0000051a: JUMP
0000051b: JUMPDEST
0000051c: PUSH1 0x60
0000051e: SWAP2
0000051f: POP
00000520: JUMPDEST
00000521: POP
00000522: SWAP2
00000523: POP
00000524: SWAP2
00000525: POP
00000526: PUSH2 0x0530
00000529: DUP3
0000052a: DUP3
0000052b: DUP7
0000052c: PUSH2 0x069d
0000052f: JUMP
00000530: JUMPDEST
00000531: SWAP7
00000532: SWAP6
00000533: POP
00000534: POP
00000535: POP
00000536: POP
00000537: POP
00000538: POP
00000539: JUMP
0000053a: JUMPDEST
0000053b: PUSH1 0x00
0000053d: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
0000055e: PUSH2 0x039d
00000561: JUMP
00000562: JUMPDEST
00000563: PUSH2 0x056b
00000566: DUP2
00000567: PUSH2 0x0298
0000056a: JUMP
0000056b: JUMPDEST
0000056c: PUSH2 0x05cd
0000056f: JUMPI
00000570: PUSH1 0x40
00000572: MLOAD
00000573: PUSH3 0x461bcd
00000577: PUSH1 0xe5
00000579: SHL
0000057a: DUP2
0000057b: MSTORE
0000057c: PUSH1 0x20
0000057e: PUSH1 0x04
00000580: DUP3
00000581: ADD
00000582: MSTORE
00000583: PUSH1 0x2d
00000585: PUSH1 0x24
00000587: DUP3
00000588: ADD
00000589: MSTORE
0000058a: PUSH32 0x455243313936373a206e657720696d706c656d656e746174696f6e206973206e
000005ab: PUSH1 0x44
000005ad: DUP3
000005ae: ADD
000005af: MSTORE
000005b0: PUSH13 0x1bdd08184818dbdb9d1c9858dd
000005be: PUSH1 0x9a
000005c0: SHL
000005c1: PUSH1 0x64
000005c3: DUP3
000005c4: ADD
000005c5: MSTORE
000005c6: PUSH1 0x84
000005c8: ADD
000005c9: PUSH2 0x0338
000005cc: JUMP
000005cd: JUMPDEST
000005ce: DUP1
000005cf: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
000005f0: JUMPDEST
000005f1: DUP1
000005f2: SLOAD
000005f3: PUSH1 0x01
000005f5: PUSH1 0x01
000005f7: PUSH1 0xa0
000005f9: SHL
000005fa: SUB
000005fb: NOT
000005fc: AND
000005fd: PUSH1 0x01
000005ff: PUSH1 0x01
00000601: PUSH1 0xa0
00000603: SHL
00000604: SUB
00000605: SWAP3
00000606: SWAP1
00000607: SWAP3
00000608: AND
00000609: SWAP2
0000060a: SWAP1
0000060b: SWAP2
0000060c: OR
0000060d: SWAP1
0000060e: SSTORE
0000060f: POP
00000610: JUMP
00000611: JUMPDEST
00000612: PUSH1 0x01
00000614: PUSH1 0x01
00000616: PUSH1 0xa0
00000618: SHL
00000619: SUB
0000061a: DUP2
0000061b: AND
0000061c: PUSH2 0x0676
0000061f: JUMPI
00000620: PUSH1 0x40
00000622: MLOAD
00000623: PUSH3 0x461bcd
00000627: PUSH1 0xe5
00000629: SHL
0000062a: DUP2
0000062b: MSTORE
0000062c: PUSH1 0x20
0000062e: PUSH1 0x04
00000630: DUP3
00000631: ADD
00000632: MSTORE
00000633: PUSH1 0x26
00000635: PUSH1 0x24
00000637: DUP3
00000638: ADD
00000639: MSTORE
0000063a: PUSH32 0x455243313936373a206e65772061646d696e20697320746865207a65726f2061
0000065b: PUSH1 0x44
0000065d: DUP3
0000065e: ADD
0000065f: MSTORE
00000660: PUSH6 0x646472657373
00000667: PUSH1 0xd0
00000669: SHL
0000066a: PUSH1 0x64
0000066c: DUP3
0000066d: ADD
0000066e: MSTORE
0000066f: PUSH1 0x84
00000671: ADD
00000672: PUSH2 0x0338
00000675: JUMP
00000676: JUMPDEST
00000677: DUP1
00000678: PUSH32 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
00000699: PUSH2 0x05f0
0000069c: JUMP
0000069d: JUMPDEST
0000069e: PUSH1 0x60
000006a0: DUP4
000006a1: ISZERO
000006a2: PUSH2 0x06ac
000006a5: JUMPI
000006a6: POP
000006a7: DUP2
000006a8: PUSH2 0x0291
000006ab: JUMP
000006ac: JUMPDEST
000006ad: DUP3
000006ae: MLOAD
000006af: ISZERO
000006b0: PUSH2 0x06bc
000006b3: JUMPI
000006b4: DUP3
000006b5: MLOAD
000006b6: DUP1
000006b7: DUP5
000006b8: PUSH1 0x20
000006ba: ADD
000006bb: REVERT
000006bc: JUMPDEST
000006bd: DUP2
000006be: PUSH1 0x40
000006c0: MLOAD
000006c1: PUSH3 0x461bcd
000006c5: PUSH1 0xe5
000006c7: SHL
000006c8: DUP2
000006c9: MSTORE
000006ca: PUSH1 0x04
000006cc: ADD
000006cd: PUSH2 0x0338
000006d0: SWAP2
000006d1: SWAP1
000006d2: PUSH2 0x07a1
000006d5: JUMP
000006d6: JUMPDEST
000006d7: DUP1
000006d8: CALLDATALOAD
000006d9: PUSH1 0x01
000006db: PUSH1 0x01
000006dd: PUSH1 0xa0
000006df: SHL
000006e0: SUB
000006e1: DUP2
000006e2: AND
000006e3: DUP2
000006e4: EQ
000006e5: PUSH2 0x029d
000006e8: JUMPI
000006e9: PUSH1 0x00
000006eb: DUP1
000006ec: REVERT
000006ed: JUMPDEST
000006ee: PUSH1 0x00
000006f0: PUSH1 0x20
000006f2: DUP3
000006f3: DUP5
000006f4: SUB
000006f5: SLT
000006f6: ISZERO
000006f7: PUSH2 0x06fe
000006fa: JUMPI
000006fb: DUP1
000006fc: DUP2
000006fd: REVERT
000006fe: JUMPDEST
000006ff: PUSH2 0x0291
00000702: DUP3
00000703: PUSH2 0x06d6
00000706: JUMP
00000707: JUMPDEST
00000708: PUSH1 0x00
0000070a: DUP1
0000070b: PUSH1 0x00
0000070d: PUSH1 0x40
0000070f: DUP5
00000710: DUP7
00000711: SUB
00000712: SLT
00000713: ISZERO
00000714: PUSH2 0x071b
00000717: JUMPI
00000718: DUP2
00000719: DUP3
0000071a: REVERT
0000071b: JUMPDEST
0000071c: PUSH2 0x0724
0000071f: DUP5
00000720: PUSH2 0x06d6
00000723: JUMP
00000724: JUMPDEST
00000725: SWAP3
00000726: POP
00000727: PUSH1 0x20
00000729: DUP5
0000072a: ADD
0000072b: CALLDATALOAD
0000072c: PUSH8 0xffffffffffffffff
00000735: DUP1
00000736: DUP3
00000737: GT
00000738: ISZERO
00000739: PUSH2 0x0740
0000073c: JUMPI
0000073d: DUP4
0000073e: DUP5
0000073f: REVERT
00000740: JUMPDEST
00000741: DUP2
00000742: DUP7
00000743: ADD
00000744: SWAP2
00000745: POP
00000746: DUP7
00000747: PUSH1 0x1f
00000749: DUP4
0000074a: ADD
0000074b: SLT
0000074c: PUSH2 0x0753
0000074f: JUMPI
00000750: DUP4
00000751: DUP5
00000752: REVERT
00000753: JUMPDEST
00000754: DUP2
00000755: CALLDATALOAD
00000756: DUP2
00000757: DUP2
00000758: GT
00000759: ISZERO
0000075a: PUSH2 0x0761
0000075d: JUMPI
0000075e: DUP5
0000075f: DUP6
00000760: REVERT
00000761: JUMPDEST
00000762: DUP8
00000763: PUSH1 0x20
00000765: DUP3
00000766: DUP6
00000767: ADD
00000768: ADD
00000769: GT
0000076a: ISZERO
0000076b: PUSH2 0x0772
0000076e: JUMPI
0000076f: DUP5
00000770: DUP6
00000771: REVERT
00000772: JUMPDEST
00000773: PUSH1 0x20
00000775: DUP4
00000776: ADD
00000777: SWAP5
00000778: POP
00000779: DUP1
0000077a: SWAP4
0000077b: POP
0000077c: POP
0000077d: POP
0000077e: POP
0000077f: SWAP3
00000780: POP
00000781: SWAP3
00000782: POP
00000783: SWAP3
00000784: JUMP
00000785: JUMPDEST
00000786: PUSH1 0x00
00000788: DUP3
00000789: MLOAD
0000078a: PUSH2 0x0797
0000078d: DUP2
0000078e: DUP5
0000078f: PUSH1 0x20
00000791: DUP8
00000792: ADD
00000793: PUSH2 0x07d4
00000796: JUMP
00000797: JUMPDEST
00000798: SWAP2
00000799: SWAP1
0000079a: SWAP2
0000079b: ADD
0000079c: SWAP3
0000079d: SWAP2
0000079e: POP
0000079f: POP
000007a0: JUMP
000007a1: JUMPDEST
000007a2: PUSH1 0x00
000007a4: PUSH1 0x20
000007a6: DUP3
000007a7: MSTORE
000007a8: DUP3
000007a9: MLOAD
000007aa: DUP1
000007ab: PUSH1 0x20
000007ad: DUP5
000007ae: ADD
000007af: MSTORE
000007b0: PUSH2 0x07c0
000007b3: DUP2
000007b4: PUSH1 0x40
000007b6: DUP6
000007b7: ADD
000007b8: PUSH1 0x20
000007ba: DUP8
000007bb: ADD
000007bc: PUSH2 0x07d4
000007bf: JUMP
000007c0: JUMPDEST
000007c1: PUSH1 0x1f
000007c3: ADD
000007c4: PUSH1 0x1f
000007c6: NOT
000007c7: AND
000007c8: SWAP2
000007c9: SWAP1
000007ca: SWAP2
000007cb: ADD
000007cc: PUSH1 0x40
000007ce: ADD
000007cf: SWAP3
000007d0: SWAP2
000007d1: POP
000007d2: POP
000007d3: JUMP
000007d4: JUMPDEST
000007d5: PUSH1 0x00
000007d7: JUMPDEST
000007d8: DUP4
000007d9: DUP2
000007da: LT
000007db: ISZERO
000007dc: PUSH2 0x07ef
000007df: JUMPI
000007e0: DUP2
000007e1: DUP2
000007e2: ADD
000007e3: MLOAD
000007e4: DUP4
000007e5: DUP3
000007e6: ADD
000007e7: MSTORE
000007e8: PUSH1 0x20
000007ea: ADD
000007eb: PUSH2 0x07d7
000007ee: JUMP
000007ef: JUMPDEST
000007f0: DUP4
000007f1: DUP2
000007f2: GT
000007f3: ISZERO
000007f4: PUSH2 0x0405
000007f7: JUMPI
000007f8: POP
000007f9: POP
000007fa: PUSH1 0x00
000007fc: SWAP2
000007fd: ADD
000007fe: MSTORE
000007ff: JUMP
00000800: INVALID
00000801: COINBASE
00000802: PUSH5 0x6472657373
00000808: GASPRICE
00000809: KECCAK256
0000080a: PUSH13 0x6f772d6c6576656c2064656c65
00000818: PUSH8 0x6174652063616c6c
00000821: KECCAK256
00000822: PUSH7 0x61696c6564a264
0000082a: PUSH10 0x7066735822122093f028
00000835: UNKNOWN(0x25)
00000836: POP
00000837: CALLDATALOAD
00000838: UNKNOWN(0xB6)
00000839: SAR
0000083a: DELEGATECALL
0000083b: PUSH23 0xb13b9dba3c4f06f60e51b9b4caee31680b389aef327f64
00000853: PUSH20

Findings

validated = confirmed for call sinks; sink observed for others
Severity Category Tool Title Confidence Validated Confirmed Details
critical upgradeability detector Untrusted DELEGATECALL target reachable 70 no no
view
trace_id: 0x5ce6e86648576fc0957b372932ca920d9d5d984b87536f8bab2901ba21b51bdd
call_targets: 0x29353f77c6b0d3772d73e708cc8e1fca08c80c11
target_varies: no
classification: constant_target
validation_json
{
    "sink": "DELEGATECALL",
    "errors": 0,
    "status": "sink_reached",
    "attempts": 2,
    "trace_id": "0x5ce6e86648576fc0957b372932ca920d9d5d984b87536f8bab2901ba21b51bdd",
    "confirmed": false,
    "trace_mode": "callTracer",
    "call_targets": [
        "0x29353f77c6b0d3772d73e708cc8e1fca08c80c11"
    ],
    "matched_probe": null,
    "target_varies": false,
    "classification": "constant_target"
}
witness_json
{
    "notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
    "constraints": [],
    "entrypoints": [
        {
            "calldata": "0x3659cfe6",
            "selector": "0x3659cfe6",
            "calldata_variants": [
                "0x3659cfe6",
                "0x3659cfe60000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0x4f1ef286",
            "selector": "0x4f1ef286",
            "calldata_variants": [
                "0x4f1ef286",
                "0x4f1ef2860000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0x5c60da1b",
            "selector": "0x5c60da1b",
            "calldata_variants": [
                "0x5c60da1b",
                "0x5c60da1b0000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0x8f283970",
            "selector": "0x8f283970",
            "calldata_variants": [
                "0x8f283970",
                "0x8f2839700000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0xf851a440",
            "selector": "0xf851a440",
            "calldata_variants": [
                "0xf851a440",
                "0xf851a4400000000000000000000000000000000000000000000000000000000000000000"
            ]
        }
    ]
}
evidence_json
{
    "examples": [
        {
            "pc": 867,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        },
        {
            "pc": 1260,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        },
        {
            "pc": 2106,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        }
    ],
    "guards_detected": [],
    "delegatecall_count": 3,
    "untrusted_delegatecall_count": 3
}
high other codex Fee-on-transfer ERC20s can overmint bridge balances and drain pooled collateral 85 no
view
evidence_json
{
    "evidence": "`DepositBoxERC20.depositERC20Direct` builds the outgoing message and increments `transferredAmount` using the caller-supplied `amount`, then performs a single `safeTransferFrom` without comparing pre/post balances. `TokenManagerERC20._exit` mirrors this pattern for schain-to-schain escrow of main-chain tokens by saving `amount` before `transferFrom` and never validating the received balance.",
    "locations": [
        "contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:691",
        "contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:704",
        "contracts/mainnet/DepositBoxes/DepositBoxERC20.sol:705",
        "contracts/schain/TokenManagers/TokenManagerERC20.sol:340",
        "contracts/schain/TokenManagers/TokenManagerERC20.sol:348",
        "contracts/schain/TokenManagers/TokenManagerERC20.sol:350"
    ],
    "recommendation": "Measure balance deltas before and after each ERC20 transfer and use the actual received amount for `transferredAmount` accounting and for the bridged message payload. Reject tokens whose received amount is smaller than the requested amount if exact-amount semantics are required.",
    "exploit_scenario": "An attacker bridges a deflationary ERC20 that burns or taxes 10% on transfer. `DepositBoxERC20.depositERC20Direct` records and forwards `amount=100` to the destination chain, then calls `safeTransferFrom` once without checking how many tokens actually arrived. Only 90 tokens reach the box, but 100 wrapped tokens are minted on the schain. The attacker can later exit the full 100 back to mainnet (or another schain), extracting the 10-token shortfall from liquidity supplied by honest users. The same invariant break exists in schain-to-schain transfers of main-chain ERC20s, where `TokenManagerERC20` escrows `amount` before verifying the real balance delta."
}
medium upgradeability cast DELEGATECALL present 80 no
view
evidence_json
{
    "notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
    "delegatecall_count": 3
}