Repository: skalenetwork/IMA (language: python)

Issue:
Update deployed version



Discussion / hints:

Fix patch:
diff --git a/proxy/DEPLOYED b/proxy/DEPLOYED
index a939f53a1..10e7a5846 100644
--- a/proxy/DEPLOYED
+++ b/proxy/DEPLOYED
@@ -1,1 +1,1 @@
-1.3.2-stable.1
\ No newline at end of file
+1.3.4-stable.0
\ No newline at end of file
diff --git a/proxy/contracts/mainnet/DepositBoxes/DepositBoxERC20.sol b/proxy/contracts/mainnet/DepositBoxes/DepositBoxERC20.sol
index 01a598a85..4f25abadf 100644
--- a/proxy/contracts/mainnet/DepositBoxes/DepositBoxERC20.sol
+++ b/proxy/contracts/mainnet/DepositBoxes/DepositBoxERC20.sol
@@ -25,7 +25,8 @@ import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
 import "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
 import "@openzeppelin/contracts-upgradeable/utils/structs/DoubleEndedQueueUpgradeable.sol";
 import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
-import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
+import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
+import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
 import "@skalenetwork/ima-interfaces/mainnet/DepositBoxes/IDepositBoxERC20.sol";
 
 import "../../Messages.sol";
@@ -47,6 +48,8 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
     using AddressUpgradeable for address;
     using DoubleEndedQueueUpgradeable for DoubleEndedQueueUpgradeable.Bytes32Deque;
     using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
+    using SafeERC20Upgradeable for IERC20MetadataUpgradeable;
+    using SafeERC20Upgradeable for IERC20Upgradeable;
 
     enum DelayedTransferStatus {
         DELAYED,
@@ -68,10 +71,9 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         mapping(address => uint256) bigTransferThreshold;
         EnumerableSetUpgradeable.AddressSet trustedReceivers;
         uint256 transferDelay;
-        uint256 arbitrageDuration;    
+        uint256 arbitrageDuration;
     }
 
-    address private constant _USDT_ADDRESS = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
     uint256 private constant _QUEUE_PROCESSING_LIMIT = 10;
 
     bytes32 public constant ARBITER_ROLE = keccak256("ARBITER_ROLE");
@@ -85,19 +87,19 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
 
     // exits delay configuration
     //   schainHash => delay config
-    mapping(bytes32 => DelayConfig) private _delayConfig;    
+    mapping(bytes32 => DelayConfig) private _delayConfig;
 
     uint256 public delayedTransfersSize;
     // delayed transfer id => delayed transfer
     mapping(uint256 => DelayedTransfer) public delayedTransfers;
     // receiver address => delayed transfers ids queue
-    mapping(address => DoubleEndedQueueUpgradeable.Bytes32Deque) public delayedTransfersByReceiver;    
+    mapping(address => DoubleEndedQueueUpgradeable.Bytes32Deque) public delayedTransfersByReceiver;
 
     /**
      * @dev Emitted when token is mapped in DepositBoxERC20.
      */
     event ERC20TokenAdded(string schainName, address indexed contractOnMainnet);
-    
+
     /**
      * @dev Emitted when token is received by DepositBox and is ready to be cloned
      * or transferred on SKALE chain.
@@ -108,11 +110,44 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
 
     event Escalated(uint256 id);
 
+    /**
+     * @dev Emitted when token transfer is skipped due to internal token error
+     */
+    event TransferSkipped(uint256 id);
+
+    /**
+     * @dev Emitted when big transfer threshold is changed
+     */
+    event BigTransferThresholdIsChanged(
+        bytes32 indexed schainHash,
+        address indexed token,
+        uint256 oldValue,
+        uint256 newValue
+    );
+
+    /**
+     * @dev Emitted when big transfer delay is changed
+     */
+    event BigTransferDelayIsChanged(
+        bytes32 indexed schainHash,
+        uint256 oldValue,
+        uint256 newValue
+    );
+
+    /**
+     * @dev Emitted when arbitrage duration is changed
+     */
+    event ArbitrageDurationIsChanged(
+        bytes32 indexed schainHash,
+        uint256 oldValue,
+        uint256 newValue
+    );
+
     /**
      * @dev Allows `msg.sender` to send ERC20 token from mainnet to schain
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - Schain name must not be `Mainnet`.
      * - Receiver account on schain cannot be null.
      * - Schain that receives tokens should not be killed.
@@ -133,7 +168,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         address contractReceiver = schainLinks[schainHash];
         require(contractReceiver != address(0), "Unconnected chain");
         require(
-            ERC20Upgradeable(erc20OnMainnet).allowance(msg.sender, address(this)) >= amount,
+            IERC20MetadataUpgradeable(erc20OnMainnet).allowance(msg.sender, address(this)) >= amount,
             "DepositBox was not approved for ERC20 token"
         );
         bytes memory data = _receiveERC20(
@@ -143,21 +178,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
             amount
         );
         _saveTransferredAmount(schainHash, erc20OnMainnet, amount);
-        if (erc20OnMainnet == _USDT_ADDRESS) {
-            // solhint-disable-next-line no-empty-blocks
-            try IERC20TransferVoid(erc20OnMainnet).transferFrom(msg.sender, address(this), amount) {} catch {
-                revert("Transfer was failed");
-            }
-        } else {
-            require(
-                ERC20Upgradeable(erc20OnMainnet).transferFrom(
-                    msg.sender,
-                    address(this),
-                    amount
-                ),
-                "Transfer was failed"
-            );
-        }
+        IERC20MetadataUpgradeable(erc20OnMainnet).safeTransferFrom(msg.sender, address(this), amount);
         messageProxy.postOutgoingMessage(
             schainHash,
             contractReceiver,
@@ -167,9 +188,9 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
 
     /**
      * @dev Allows MessageProxyForMainnet contract to execute transferring ERC20 token from schain to mainnet.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - Schain from which the tokens came should not be killed.
      * - Sender contract should be defined and schain name cannot be `Mainnet`.
      * - Amount of tokens on DepositBoxERC20 should be equal or more than transferred amount.
@@ -187,7 +208,10 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
     {
         Messages.TransferErc20Message memory message = Messages.decodeTransferErc20Message(data);
         require(message.token.isContract(), "Given address is not a contract");
-        require(ERC20Upgradeable(message.token).balanceOf(address(this)) >= message.amount, "Not enough money");
+        require(
+            IERC20MetadataUpgradeable(message.token).balanceOf(address(this)) >= message.amount,
+            "Not enough money"
+        );
         _removeTransferredAmount(schainHash, message.token, message.amount);
 
         uint256 delay = _delayConfig[schainHash].transferDelay;
@@ -196,19 +220,19 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
             && _delayConfig[schainHash].bigTransferThreshold[message.token] <= message.amount
             && !isReceiverTrusted(schainHash, message.receiver)
         ) {
-            _createDelayedTransfer(schainHash, message, delay);            
+            _createDelayedTransfer(schainHash, message, delay);
         } else {
-            _transfer(message.token, message.receiver, message.amount);
+            IERC20MetadataUpgradeable(message.token).safeTransfer(message.receiver, message.amount);
         }
     }
 
     /**
      * @dev Allows Schain owner to add an ERC20 token to DepositBoxERC20.
-     * 
+     *
      * Emits an {ERC20TokenAdded} event.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - Schain should not be killed.
      * - Only owner of the schain able to run function.
      */
@@ -223,11 +247,11 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
 
     /**
      * @dev Allows Schain owner to return each user their tokens.
-     * The Schain owner decides which tokens to send to which address, 
+     * The Schain owner decides which tokens to send to which address,
      * since the contract on mainnet does not store information about which tokens belong to whom.
      *
      * Requirements:
-     * 
+     *
      * - Amount of tokens on schain should be equal or more than transferred amount.
      * - msg.sender should be an owner of schain
      * - IMA transfers Mainnet <-> schain should be killed
@@ -241,10 +265,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         bytes32 schainHash = _schainHash(schainName);
         require(transferredAmount[schainHash][erc20OnMainnet] >= amount, "Incorrect amount");
         _removeTransferredAmount(schainHash, erc20OnMainnet, amount);
-        require(
-            ERC20Upgradeable(erc20OnMainnet).transfer(receiver, amount),
-            "Transfer was failed"
-        );
+        IERC20MetadataUpgradeable(erc20OnMainnet).safeTransfer(receiver, amount);
     }
 
     /**
@@ -254,7 +275,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
      * and can be canceled by a voting
      *
      * Requirements:
-     * 
+     *
      * - msg.sender should be an owner of schain
      */
     function setBigTransferValue(
@@ -267,7 +288,13 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         onlySchainOwner(schainName)
     {
         bytes32 schainHash = _schainHash(schainName);
-        _delayConfig[schainHash].bigTransferThreshold[token] = value;   
+        emit BigTransferThresholdIsChanged(
+            schainHash,
+            token,
+            _delayConfig[schainHash].bigTransferThreshold[token],
+            value
+        );
+        _delayConfig[schainHash].bigTransferThreshold[token] = value;
     }
 
     /**
@@ -277,7 +304,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
      * and can be canceled by a voting
      *
      * Requirements:
-     * 
+     *
      * - msg.sender should be an owner of schain
      */
     function setBigTransferDelay(
@@ -291,7 +318,8 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         bytes32 schainHash = _schainHash(schainName);
         // need to restrict big delays to avoid overflow
         require(delayInSeconds < 1e8, "Delay is too big"); // no more then ~ 3 years
-        _delayConfig[schainHash].transferDelay = delayInSeconds;   
+        emit BigTransferDelayIsChanged(schainHash, _delayConfig[schainHash].transferDelay, delayInSeconds);
+        _delayConfig[schainHash].transferDelay = delayInSeconds;
     }
 
     /**
@@ -299,7 +327,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
      * After escalation the transfer is locked for provided period of time.
      *
      * Requirements:
-     * 
+     *
      * - msg.sender should be an owner of schain
      */
     function setArbitrageDuration(
@@ -313,13 +341,14 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         bytes32 schainHash = _schainHash(schainName);
         // need to restrict big delays to avoid overflow
         require(delayInSeconds < 1e8, "Delay is too big"); // no more then ~ 3 years
-        _delayConfig[schainHash].arbitrageDuration = delayInSeconds;   
+        emit ArbitrageDurationIsChanged(schainHash, _delayConfig[schainHash].arbitrageDuration, delayInSeconds);
+        _delayConfig[schainHash].arbitrageDuration = delayInSeconds;
     }
 
     /**
      * @dev Add the address to a whitelist of addresses that can do big transfers without delaying
      * Requirements:
-     * 
+     *
      * - msg.sender should be an owner of schain
      * - the address must not be in the whitelist
      */
@@ -334,13 +363,13 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         require(
             _delayConfig[_schainHash(schainName)].trustedReceivers.add(receiver),
             "Receiver already is trusted"
-        );        
+        );
     }
 
     /**
      * @dev Remove the address from a whitelist of addresses that can do big transfers without delaying
      * Requirements:
-     * 
+     *
      * - msg.sender should be an owner of schain
      * - the address must be in the whitelist
      */
@@ -367,7 +396,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
      * @dev Initialize arbitrage of a suspicious big transfer
      *
      * Requirements:
-     * 
+     *
      * - msg.sender should be an owner of schain or have ARBITER_ROLE role
      * - transfer must be delayed and arbitrage must not be started
      */
@@ -390,7 +419,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
      * @dev Approve a big transfer and immidiately transfer tokens during arbitrage
      *
      * Requirements:
-     * 
+     *
      * - msg.sender should be an owner of schain
      * - arbitrage of the transfer must be started
      */
@@ -405,14 +434,14 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         require(transfer.status == DelayedTransferStatus.ARBITRAGE, "Arbitrage has to be active");
         transfer.status = DelayedTransferStatus.COMPLETED;
         delete transfer.untilTimestamp;
-        _transfer(transfer.token, transfer.receiver, transfer.amount);
+        IERC20MetadataUpgradeable(transfer.token).safeTransfer(transfer.receiver, transfer.amount);
     }
 
     /**
      * @dev Reject a big transfer and transfer tokens to SKALE chain owner during arbitrage
      *
      * Requirements:
-     * 
+     *
      * - msg.sender should be an owner of schain
      * - arbitrage of the transfer must be started
      */
@@ -428,7 +457,12 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         transfer.status = DelayedTransferStatus.COMPLETED;
         delete transfer.untilTimestamp;
         // msg.sender is schain owner
-        _transfer(transfer.token, msg.sender, transfer.amount);
+        IERC20MetadataUpgradeable(transfer.token).safeTransfer(msg.sender, transfer.amount);
+    }
+
+    function doTransfer(address token, address receiver, uint256 amount) external override {
+        require(msg.sender == address(this), "Internal use only");
+        IERC20Upgradeable(token).safeTransfer(receiver, amount);
     }
 
     /**
@@ -454,7 +488,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
     }
 
     /**
-     * @dev Should return true if token was added by Schain owner or 
+     * @dev Should return true if token was added by Schain owner or
      * added automatically after sending to schain if whitelist was turned off.
      */
     function getSchainToERC20(
@@ -470,7 +504,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
     }
 
     /**
-     * @dev Should return length of a set of all mapped tokens which were added by Schain owner 
+     * @dev Should return length of a set of all mapped tokens which were added by Schain owner
      * or added automatically after sending to schain if whitelist was turned off.
      */
     function getSchainToAllERC20Length(string calldata schainName) external view override returns (uint256) {
@@ -478,7 +512,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
     }
 
     /**
-     * @dev Should return an array of range of tokens were added by Schain owner 
+     * @dev Should return an array of range of tokens were added by Schain owner
      * or added automatically after sending to schain if whitelist was turned off.
      */
     function getSchainToAllERC20(
@@ -593,7 +627,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         bool retrieved = false;
         for (uint256 i = 0; i < transfersAmount; ++i) {
             uint256 transferId = uint256(delayedTransfersByReceiver[receiver].at(currentIndex));
-            DelayedTransfer memory transfer = delayedTransfers[transferId];            
+            DelayedTransfer memory transfer = delayedTransfers[transferId];
             ++currentIndex;
 
             if (transfer.status != DelayedTransferStatus.COMPLETED) {
@@ -616,12 +650,19 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
                         delayedTransfers[transferId].status = DelayedTransferStatus.COMPLETED;
                     }
                     retrieved = true;
-                    _transfer(transfer.token, transfer.receiver, transfer.amount);
+                    try
+                        this.doTransfer(transfer.token, transfer.receiver, transfer.amount)
+                    // solhint-disable-next-line no-empty-blocks
+                    {}
+                    catch {
+                        emit TransferSkipped(transferId);
+                    }
                 }
             } else {
                 // status is COMPLETED
                 if (currentIndex == 1) {
                     --currentIndex;
+                    retrieved = true;
                     _removeOldestDelayedTransfer(receiver);
                 }
             }
@@ -669,11 +710,11 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
 
     /**
      * @dev Allows DepositBoxERC20 to receive ERC20 tokens.
-     * 
+     *
      * Emits an {ERC20TokenReady} event.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - Amount must be less than or equal to the total supply of the ERC20 contract.
      * - Whitelist should be turned off for auto adding tokens to DepositBoxERC20.
      */
@@ -687,7 +728,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         returns (bytes memory data)
     {
         bytes32 schainHash = _schainHash(schainName);
-        ERC20Upgradeable erc20 = ERC20Upgradeable(erc20OnMainnet);
+        IERC20MetadataUpgradeable erc20 = IERC20MetadataUpgradeable(erc20OnMainnet);
         uint256 totalSupply = erc20.totalSupply();
         require(amount <= totalSupply, "Amount is incorrect");
         bool isERC20AddedToSchain = _schainToERC20[schainHash].contains(erc20OnMainnet);
@@ -714,11 +755,11 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
 
     /**
      * @dev Adds an ERC20 token to DepositBoxERC20.
-     * 
+     *
      * Emits an {ERC20TokenAdded} event.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - Given address should be contract.
      */
     function _addERC20ForSchain(string calldata schainName, address erc20OnMainnet) private {
@@ -749,9 +790,9 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
      * the element is added at back - depthLimit index
      */
     function _addToDelayedQueueWithPriority(
-        DoubleEndedQueueUpgradeable.Bytes32Deque storage queue, 
-        uint256 id, 
-        uint256 until, 
+        DoubleEndedQueueUpgradeable.Bytes32Deque storage queue,
+        uint256 id,
+        uint256 until,
         uint256 depthLimit
     )
         private
@@ -769,25 +810,6 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
         }
     }
 
-    /**
-     * @dev Transfer ERC20 token or USDT
-     */
-    function _transfer(address token, address receiver, uint256 amount) private {
-        // there is no other reliable way to determine USDT
-        // slither-disable-next-line incorrect-equality
-        if (token == _USDT_ADDRESS) {
-            // solhint-disable-next-line no-empty-blocks
-            try IERC20TransferVoid(token).transfer(receiver, amount) {} catch {
-                revert("Transfer was failed");
-            }
-        } else {
-            require(
-                ERC20Upgradeable(token).transfer(receiver, amount),
-                "Transfer was failed"
-            );
-        }
-    }
-
     /**
      * Create instance of DelayedTransfer and initialize all auxiliary fields.
      */
@@ -817,7 +839,7 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
     function _removeOldestDelayedTransfer(address receiver) private {
         uint256 transferId = uint256(delayedTransfersByReceiver[receiver].popFront());
         // For most cases the loop will have only 1 iteration.
-        // In worst case the amount of iterations is limited by _QUEUE_PROCESSING_LIMIT         
+        // In worst case the amount of iterations is limited by _QUEUE_PROCESSING_LIMIT
         // slither-disable-next-line costly-loop
         delete delayedTransfers[transferId];
     }
@@ -825,14 +847,18 @@ contract DepositBoxERC20 is DepositBox, IDepositBoxERC20 {
     /**
      * @dev Returns total supply of ERC20 token.
      */
-    function _getErc20TotalSupply(ERC20Upgradeable erc20Token) private view returns (uint256) {
+    function _getErc20TotalSupply(IERC20MetadataUpgradeable erc20Token) private view returns (uint256) {
         return erc20Token.totalSupply();
     }
 
     /**
      * @dev Returns info about ERC20 token such as token name, decimals, symbol.
      */
-    function _getErc20TokenInfo(ERC20Upgradeable erc20Token) private view returns (Messages.Erc20TokenInfo memory) {
+    function _getErc20TokenInfo(IERC20MetadataUpgradeable erc20Token)
+        private
+        view
+        returns (Messages.Erc20TokenInfo memory)
+    {
         return Messages.Erc20TokenInfo({
             name: erc20Token.name(),
             decimals: erc20Token.decimals(),
diff --git a/proxy/contracts/mainnet/MessageProxyForMainnet.sol b/proxy/contracts/mainnet/MessageProxyForMainnet.sol
index 8120c9540..e2c9ebe84 100644
--- a/proxy/contracts/mainnet/MessageProxyForMainnet.sol
+++ b/proxy/contracts/mainnet/MessageProxyForMainnet.sol
@@ -37,7 +37,7 @@ import "./CommunityPool.sol";
 /**
  * @title Message Proxy for Mainnet
  * @dev Runs on Mainnet, contains functions to manage the incoming messages from
- * `targetSchainName` and outgoing messages to `fromSchainName`. Every SKALE chain with 
+ * `targetSchainName` and outgoing messages to `fromSchainName`. Every SKALE chain with
  * IMA is therefore connected to MessageProxyForMainnet.
  *
  * Messages from SKALE chains are signed using BLS threshold signatures from the
@@ -103,6 +103,20 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
         uint256 newValue
     );
 
+    /**
+     * @dev Emitted when the schain is paused
+     */
+    event SchainPaused(
+        bytes32 indexed schainHash
+    );
+
+    /**
+     * @dev Emitted when the schain is resumed
+     */
+    event SchainResumed(
+        bytes32 indexed schainHash
+    );
+
     /**
      * @dev Emitted when reimbursed contract was added
      */
@@ -139,9 +153,9 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
 
     /**
      * @dev Allows `msg.sender` to connect schain with MessageProxyOnMainnet for transferring messages.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - Schain name must not be `Mainnet`.
      */
     function addConnectedChain(string calldata schainName) external override {
@@ -154,9 +168,9 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
 
     /**
      * @dev Allows owner of the contract to set CommunityPool address for gas reimbursement.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - `msg.sender` must be granted as DEFAULT_ADMIN_ROLE.
      * - Address of CommunityPool contract must not be null.
      */
@@ -168,9 +182,9 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
 
     /**
      * @dev Allows `msg.sender` to register extra contract for being able to transfer messages from custom contracts.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - `msg.sender` must be granted as EXTRA_CONTRACT_REGISTRAR_ROLE.
      * - Schain name must not be `Mainnet`.
      */
@@ -181,16 +195,16 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
             isSchainOwner(msg.sender, schainHash),
             "Not enough permissions to register extra contract"
         );
-        require(schainHash != MAINNET_HASH, "Schain hash can not be equal Mainnet");        
+        require(schainHash != MAINNET_HASH, "Schain hash can not be equal Mainnet");
         _registerExtraContract(schainHash, extraContract);
     }
 
     /**
      * @dev Allows `msg.sender` to remove extra contract,
      * thus `extraContract` will no longer be available to transfer messages from mainnet to schain.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - `msg.sender` must be granted as EXTRA_CONTRACT_REGISTRAR_ROLE.
      * - Schain name must not be `Mainnet`.
      */
@@ -256,10 +270,10 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
     }
 
     /**
-     * @dev Posts incoming message from `fromSchainName`. 
-     * 
+     * @dev Posts incoming message from `fromSchainName`.
+     *
      * Requirements:
-     * 
+     *
      * - `msg.sender` must be authorized caller.
      * - `fromSchainName` must be initialized.
      * - `startingCounter` must be equal to the chain's incoming message counter.
@@ -290,7 +304,7 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
             fromSchainName,
             _hashedArray(messages, startingCounter, fromSchainName), sign),
             "Signature is not verified");
-        uint additionalGasPerMessage = 
+        uint additionalGasPerMessage =
             (gasTotal - gasleft() + headerMessageGasCost + messages.length * messageGasCost) / messages.length;
         uint notReimbursedGas = 0;
         connectedChains[fromSchainHash].incomingMessageCounter += messages.length;
@@ -315,9 +329,9 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
 
     /**
      * @dev Sets headerMessageGasCost to a new value.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - `msg.sender` must be granted as CONSTANT_SETTER_ROLE.
      */
     function setNewHeaderMessageGasCost(uint256 newHeaderMessageGasCost) external override onlyConstantSetter {
@@ -327,9 +341,9 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
 
     /**
      * @dev Sets messageGasCost to a new value.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - `msg.sender` must be granted as CONSTANT_SETTER_ROLE.
      */
     function setNewMessageGasCost(uint256 newMessageGasCost) external override onlyConstantSetter {
@@ -339,9 +353,9 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
 
     /**
      * @dev Sets new version of contracts on mainnet
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - `msg.sender` must be granted DEFAULT_ADMIN_ROLE.
      */
     function setVersion(string calldata newVersion) external override {
@@ -352,9 +366,9 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
 
     /**
      * @dev Allows PAUSABLE_ROLE to pause IMA bridge unlimited
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - IMA bridge to current schain was not paused
      * - Sender should be PAUSABLE_ROLE
      */
@@ -363,13 +377,14 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
         require(hasRole(PAUSABLE_ROLE, msg.sender), "Incorrect sender");
         require(!pauseInfo[schainHash].paused, "Already paused");
         pauseInfo[schainHash].paused = true;
+        emit SchainPaused(schainHash);
     }
 
 /**
-     * @dev Allows DEFAULT_ADMIN_ROLE or schain owner to resume IMA bridge 
-     * 
+     * @dev Allows DEFAULT_ADMIN_ROLE or schain owner to resume IMA bridge
+     *
      * Requirements:
-     * 
+     *
      * - IMA bridge to current schain was paused
      * - Sender should be DEFAULT_ADMIN_ROLE or schain owner
      */
@@ -378,6 +393,7 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
         require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || isSchainOwner(msg.sender, schainHash), "Incorrect sender");
         require(pauseInfo[schainHash].paused, "Already unpaused");
         pauseInfo[schainHash].paused = false;
+        emit SchainResumed(schainHash);
     }
 
     /**
@@ -440,12 +456,12 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
 
     /**
      * @dev Checks whether chain is currently connected.
-     * 
-     * Note: Mainnet chain does not have a public key, and is implicitly 
+     *
+     * Note: Mainnet chain does not have a public key, and is implicitly
      * connected to MessageProxy.
-     * 
+     *
      * Requirements:
-     * 
+     *
      * - `schainName` must not be Mainnet.
      */
     function isConnectedChain(
@@ -484,7 +500,7 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
                 || isContractRegistered(targetChainHash, msg.sender)
                 || isSchainOwner(msg.sender, targetChainHash),
             "Sender contract is not registered"
-        );        
+        );
     }
 
     /**
@@ -514,7 +530,7 @@ contract MessageProxyForMainnet is SkaleManagerClient, MessageProxy, IMessagePro
     }
 
     /**
-     * @dev Checks whether balance of schain wallet is sufficient for 
+     * @dev Checks whether balance of schain wallet is sufficient for
      * for reimbursement custom message.
      */
     function _checkSchainBalance(bytes32 schainHash) internal view returns (bool) {
diff --git a/proxy/migrations/tools/gnosis-safe.ts b/proxy/migrations/tools/gnosis-safe.ts
index b4938e319..1e5013f79 100644
--- a/proxy/migrations/tools/gnosis-safe.ts
+++ b/proxy/migrations/tools/gnosis-safe.ts
@@ -9,6 +9,7 @@ type Ethers = typeof ethers & HardhatEthersHelpers;
 enum Network {
     MAINNET = 1,
     RINKEBY = 4,
+    GOERLI = 5,
     GANACHE = 1337,
     HARDHAT = 31337,
 }
@@ -19,6 +20,7 @@ const ADDRESSES = {
     multiSend: {
         [Network.MAINNET]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
         [Network.RINKEBY]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
+        [Network.GOERLI]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
     },
 }
 
@@ -26,10 +28,12 @@ const URLS = {
     safe_transaction: {
         [Network.MAINNET]: "https://safe-transaction.mainnet.gnosis.io",
         [Network.RINKEBY]: "https://safe-transaction.rinkeby.gnosis.io",
+        [Network.GOERLI]: "https://safe-transaction.goerli.gnosis.io",
     },
     safe_relay: {
         [Network.MAINNET]: "https://safe-relay.mainnet.gnosis.io",
         [Network.RINKEBY]: "https://safe-relay.rinkeby.gnosis.io",
+        [Network.GOERLI]: "https://safe-relay.goerli.gnosis.io",
     }
 }
 
@@ -40,6 +44,8 @@ function getMultiSendAddress(chainId: number, isSafeMock: boolean = false) {
         return ADDRESSES.multiSend[chainId];
     } else if (chainId === Network.RINKEBY) {
         return ADDRESSES.multiSend[chainId];
+    } else if (chainId === Network.GOERLI) {
+        return ADDRESSES.multiSend[chainId];
     } else if ([Network.GANACHE, Network.HARDHAT].includes(chainId)) {
         return ethers.constants.AddressZero;
     } else {
@@ -50,7 +56,9 @@ function getMultiSendAddress(chainId: number, isSafeMock: boolean = false) {
 export function getSafeTransactionUrl(chainId: number) {
     if (chainId === Network.MAINNET) {
         return URLS.safe_transaction[chainId];
-    } else if (chainId === 4) {
+    } else if (chainId === Network.RINKEBY) {
+        return URLS.safe_transaction[chainId];
+    } else if (chainId === Network.GOERLI) {
         return URLS.safe_transaction[chainId];
     } else {
         throw Error("Can't get safe-transaction url at network with chainId = " + chainId);
@@ -58,10 +66,8 @@ export function getSafeTransactionUrl(chainId: number) {
 }
 
 export function getSafeRelayUrl(chainId: number) {
-    if (chainId === 1) {
-        return URLS.safe_relay[chainId];
-    } else if (chainId === 4) {
-        return URLS.safe_relay[chainId];
+    if (Object.keys(URLS.safe_relay).includes(chainId.toString())) {
+        return URLS.safe_relay[chainId as keyof typeof URLS.safe_relay];
     } else {
         throw Error("Can't get safe-relay url at network with chainId = " + chainId);
     }
diff --git a/proxy/migrations/tools/verification.ts b/proxy/migrations/tools/verification.ts
index bc37440ff..6373c6741 100644
--- a/proxy/migrations/tools/verification.ts
+++ b/proxy/migrations/tools/verification.ts
@@ -12,7 +12,7 @@ export async function verify(contractName: string, contractAddress: string, cons
                 });
                 break;
             } catch (e: any) {
-                if (e.toString().includes("Already Verified")) {
+                if (e.toString().includes("Contract source code already verified")) {
                     console.log(chalk.grey(`${contractName} is already verified`));
                     return;
                 }
diff --git a/proxy/migrations/upgradeMainnet.ts b/proxy/migrations/upgradeMainnet.ts
index 1cdb09380..3ca1967c1 100644
--- a/proxy/migrations/upgradeMainnet.ts
+++ b/proxy/migrations/upgradeMainnet.ts
@@ -9,75 +9,10 @@ import hre from "hardhat";
 
 async function main() {
     await upgrade(
-        "1.3.2",
+        "1.3.4",
         contracts,
         async (safeTransactions, abi) => undefined,
-        async (safeTransactions, abi) => {
-            const proxyAdmin = await getManifestAdmin(hre);
-            const owner = await proxyAdmin.owner();
-            const communityPoolName = "CommunityPool";
-            const communityPoolFactory = await ethers.getContractFactory(communityPoolName);
-            const communityPoolAddress = abi[getContractKeyInAbiFile(communityPoolName) + "_address"];
-            let communityPool;
-            if (communityPoolAddress) {
-                communityPool = communityPoolFactory.attach(communityPoolAddress) as CommunityPool;
-                const constantSetterRole = await communityPool.CONSTANT_SETTER_ROLE();
-                const isHasRole = await communityPool.hasRole(constantSetterRole, owner);
-                if (!isHasRole) {
-                    console.log(chalk.yellow("Prepare transaction to grantRole CONSTANT_SETTER_ROLE to " + owner));
-                    safeTransactions.push(encodeTransaction(
-                        0,
-                        communityPoolAddress,
-                        0,
-                        communityPool.interface.encodeFunctionData("grantRole", [constantSetterRole, owner])
-                    ));
-                }
-                console.log(chalk.yellow("Prepare transaction to set multiplier to 3/2"));
-                safeTransactions.push(encodeTransaction(
-                    0,
-                    communityPoolAddress,
-                    0,
-                    communityPool.interface.encodeFunctionData("setMultiplier", [3, 2])
-                ));
-                console.log(chalk.yellow("Prepare transaction to set header message gas cost to 73800"));
-            } else {
-                console.log(chalk.red("CommunityPool was not found!"));
-                console.log(chalk.red("Check your abi!!!"));
-                process.exit(1);
-            }
-
-            const messageProxyForMainnet = (await ethers.getContractFactory("MessageProxyForMainnet"))
-            .attach(abi[getContractKeyInAbiFile("MessageProxyForMainnet") + "_address"]) as MessageProxyForMainnet;
-
-            if (! await messageProxyForMainnet.hasRole(await messageProxyForMainnet.CONSTANT_SETTER_ROLE(), owner)) {
-                console.log(chalk.yellow("Prepare transaction to grantRole CONSTANT_SETTER_ROLE to " + owner));
-                safeTransactions.push(encodeTransaction(
-                    0,
-                    messageProxyForMainnet.address,
-                    0,
-                    messageProxyForMainnet.interface.encodeFunctionData(
-                        "grantRole",
-                        [ await messageProxyForMainnet.CONSTANT_SETTER_ROLE(), owner ]
-                    )
-                ));
-            }
-
-            const newHeaderMessageGasCost = 92251;
-
-            console.log(chalk.yellow(
-                "Prepare transaction to set header message gas cost to",
-                newHeaderMessageGasCost.toString()
-            ));
-            safeTransactions.push(encodeTransaction(
-                0,
-                messageProxyForMainnet.address,
-                0,
-                messageProxyForMainnet.interface.encodeFunctionData(
-                    "setNewHeaderMessageGasCost",
-                    [ newHeaderMessageGasCost ]
-                )
-            ));
-        },
+        async (safeTransactions, abi) => undefined,
         "proxyMainnet"
     );
 }
diff --git a/proxy/migrations/upgradeSchain.ts b/proxy/migrations/upgradeSchain.ts
index 6f25a1d69..72ac293fc 100644
--- a/proxy/migrations/upgradeSchain.ts
+++ b/proxy/migrations/upgradeSchain.ts
@@ -19,7 +19,7 @@ async function main() {
     const pathToManifest: string = stringValue(process.env.MANIFEST);
     await manifestSetup( pathToManifest );
     await upgrade(
-        "1.3.2",
+        "1.3.4",
         contracts,
         async (safeTransactions, abi) => {
             // deploying of new contracts
diff --git a/proxy/package.json b/proxy/package.json
index 39716805a..0f94cda51 100644
--- a/proxy/package.json
+++ b/proxy/package.json
@@ -26,8 +26,8 @@
         "@openzeppelin/contracts-upgradeable": "^4.7.1",
         "@openzeppelin/hardhat-upgrades": "^1.9.0",
         "@skalenetwork/etherbase-interfaces": "^0.0.1-develop.20",
-        "@skalenetwork/ima-interfaces": "1.0.0-develop.24",
-        "@skalenetwork/skale-manager-interfaces": "1.0.0",
+        "@skalenetwork/ima-interfaces": "^1.1.0-develop.0",
+        "@skalenetwork/skale-manager-interfaces": "1.0.0-develop.1",
         "axios": "^0.21.4",
         "dotenv": "^10.0.0",
         "ethereumjs-tx": "2.1.2",
diff --git a/proxy/yarn.lock b/proxy/yarn.lock
index 3c930d361..4646476f0 100644
--- a/proxy/yarn.lock
+++ b/proxy/yarn.lock
@@ -757,17 +757,17 @@
   resolved "https://registry.yarnpkg.com/@skalenetwork/etherbase-interfaces/-/etherbase-interfaces-0.0.1-develop.20.tgz#33f61e18d695fd47063aa39dce4df335d26b9528"
   integrity sha512-j3xnuQtOtjvjAoUMJgSUFxRa9/Egkg1RyA8r6PjcEb33VksE4LWLBy0PNFUFehLZv48595JROTcViGeXXwg5HQ==
 
-"@skalenetwork/ima-interfaces@1.0.0-develop.24":
-  version "1.0.0-develop.24"
-  resolved "https://registry.yarnpkg.com/@skalenetwork/ima-interfaces/-/ima-interfaces-1.0.0-develop.24.tgz#20247612c602a90b6f5e4e0cce6e3eb8e64f2ed1"
-  integrity sha512-qVkcg4DBtH7lbVzTO6AcFILnvOTPesImyVvWqMlyzPOvfj4qUsXeqQqE6pDbhbWev68Ar7cvqWmfK+eEENp1pw==
+"@skalenetwork/ima-interfaces@^1.1.0-develop.0":
+  version "1.1.0-develop.0"
+  resolved "https://registry.yarnpkg.com/@skalenetwork/ima-interfaces/-/ima-interfaces-1.1.0-develop.0.tgz#a91a794affee2138e03c779f9f32cd34652a47b7"
+  integrity sha512-wy1DYbsgccYETqgayxyHh9Dh8+q13F0JrDCpHn7nodwnjxp95cAtjyD9KTN4Y4/Ix/pr5rFqQjBXtXQas36uBg==
   dependencies:
     "@skalenetwork/skale-manager-interfaces" "^0.1.2"
 
-"@skalenetwork/skale-manager-interfaces@1.0.0":
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/@skalenetwork/skale-manager-interfaces/-/skale-manager-interfaces-1.0.0.tgz#5456ecbb7e36300544296d24c2345be54c5c9218"
-  integrity sha512-TOgoQZXbYj5KPZnyzs+1L9g6RykSYxE6/9NnPfb8aDYy4Y5Pp/JIK7cy4qSnXetarCGhIGvNo/HBU8naq6dhEQ==
+"@skalenetwork/skale-manager-interfaces@1.0.0-develop.1":
+  version "1.0.0-develop.1"
+  resolved "https://registry.yarnpkg.com/@skalenetwork/skale-manager-interfaces/-/skale-manager-interfaces-1.0.0-develop.1.tgz#8a2d06872dcdc235c041448efc454c0d5f1e55cf"
+  integrity sha512-ZOSWEqvz76Ez5cuWE8n9+j/tnTVQg+t7vaZar/XME0hxnM1YwNFqy34nqjEC/SMhFXg/WnxdOVX9JWWXVZRlkQ==
 
 "@skalenetwork/skale-manager-interfaces@^0.1.2":
   version "0.1.2"


Test patch:
diff --git a/proxy/contracts/test/erc20/ERC20IncorrectTransfer.sol b/proxy/contracts/test/erc20/ERC20IncorrectTransfer.sol
new file mode 100644
index 000000000..2e1fe4b7d
--- /dev/null
+++ b/proxy/contracts/test/erc20/ERC20IncorrectTransfer.sol
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+
+pragma solidity 0.8.16;
+
+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
+        );
+    }
+}
diff --git a/proxy/contracts/test/erc20/ERC20TransferWithFalseReturn.sol b/proxy/contracts/test/erc20/ERC20TransferWithFalseReturn.sol
new file mode 100644
index 000000000..1dc3779fc
--- /dev/null
+++ b/proxy/contracts/test/erc20/ERC20TransferWithFalseReturn.sol
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+
+pragma solidity 0.8.16;
+
+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;
+    }
+}
diff --git a/proxy/contracts/test/erc20/ERC20TransferWithoutReturn.sol b/proxy/contracts/test/erc20/ERC20TransferWithoutReturn.sol
new file mode 100644
index 000000000..4ef8fe0ee
--- /dev/null
+++ b/proxy/contracts/test/erc20/ERC20TransferWithoutReturn.sol
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+
+pragma solidity 0.8.16;
+
+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
+        );
+    }
+}
diff --git a/proxy/contracts/test/erc20/ERC20WithoutTransfer.sol b/proxy/contracts/test/erc20/ERC20WithoutTransfer.sol
new file mode 100644
index 000000000..746fbaa00
--- /dev/null
+++ b/proxy/contracts/test/erc20/ERC20WithoutTransfer.sol
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+
+pragma solidity 0.8.16;
+
+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);
+    }
+}
diff --git a/proxy/contracts/test/erc20/RevertableERC20.sol b/proxy/contracts/test/erc20/RevertableERC20.sol
new file mode 100644
index 000000000..beb3f94b8
--- /dev/null
+++ b/proxy/contracts/test/erc20/RevertableERC20.sol
@@ -0,0 +1,62 @@
+// 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.16;
+
+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);
+    }
+}
\ No newline at end of file
diff --git a/proxy/test/DepositBoxERC20.ts b/proxy/test/DepositBoxERC20.ts
index c946af4ac..6b37c51ad 100644
--- a/proxy/test/DepositBoxERC20.ts
+++ b/proxy/test/DepositBoxERC20.ts
@@ -62,6 +62,7 @@ import { BigNumber, Wallet } from "ethers";
 import { assert, expect, use } from "chai";
 import { createNode } from "./utils/skale-manager-utils/nodes";
 import { currentTime, skipTime } from "./utils/time";
+import { join } from "path";
 
 const BlsSignature: [BigNumber, BigNumber] = [
     BigNumber.from("178325537405109593276798394634841698946852714038246117383766698579865918287"),
@@ -233,6 +234,39 @@ describe("DepositBoxERC20", () => {
             await depositBoxERC20.connect(schainOwner).addERC20TokenByOwner(schainName, erc20.address)
                 .should.be.eventually.rejectedWith("Schain is killed");
         });
+
+        it("should invoke `depositERC20` for non standard ERC20", async () => {
+            // preparation
+            // mint some quantity of ERC20 tokens for `deployer` address
+            const erc20TWR = await (await ethers.getContractFactory("ERC20TransferWithoutReturn")).deploy("Test", "TST");
+            const erc20TWFR = await (await ethers.getContractFactory("ERC20TransferWithFalseReturn")).deploy("Test", "TST");
+            const erc20IT = await (await ethers.getContractFactory("ERC20IncorrectTransfer")).deploy("Test", "TST");
+            const amount = 10;
+            await erc20TWR.connect(deployer).mint(deployer.address, amount);
+            await erc20TWFR.connect(deployer).mint(deployer.address, amount);
+            await erc20IT.connect(deployer).mint(deployer.address, amount);
+            await erc20.connect(deployer).mint(deployer.address, amount);
+            await erc20TWR.connect(deployer).approve(depositBoxERC20.address, amount);
+            await erc20TWFR.connect(deployer).approve(depositBoxERC20.address, amount);
+            await erc20IT.connect(deployer).approve(depositBoxERC20.address, amount);
+            await erc20.connect(deployer).approve(depositBoxERC20.address, amount);
+            // execution
+            await depositBoxERC20.connect(schainOwner).disableWhitelist(schainName);
+            await depositBoxERC20
+                .connect(deployer)
+                .depositERC20(schainName, erc20.address, 1);
+            await depositBoxERC20
+                .connect(deployer)
+                .depositERC20(schainName, erc20TWR.address, 1);
+            await depositBoxERC20
+                .connect(deployer)
+                .depositERC20(schainName, erc20IT.address, 1)
+                .should.be.eventually.rejectedWith("SafeERC20: low-level call failed");
+            await depositBoxERC20
+                .connect(deployer)
+                .depositERC20(schainName, erc20TWFR.address, 1)
+                .should.be.eventually.rejectedWith("SafeERC20: ERC20 operation did not succeed");
+        });
     });
 
     describe("tests for `postMessage` function", async () => {
@@ -330,6 +364,73 @@ describe("DepositBoxERC20", () => {
 
         });
 
+        it("should transfer non standard ERC20 token", async () => {
+            //  preparation
+            const erc20TWR = await (await ethers.getContractFactory("ERC20TransferWithoutReturn")).deploy("Test", "TST");
+            const amount = 10;
+            const to = user.address;
+            const senderFromSchain = deployer.address;
+            const wei = 1e18.toString();
+
+            const sign = {
+                blsSignature: BlsSignature,
+                counter: Counter,
+                hashA: HashA,
+                hashB: HashB,
+            };
+
+            const message = {
+                data: await messages.encodeTransferErc20Message(erc20.address, to, amount),
+                destinationContract: depositBoxERC20.address,
+                sender: senderFromSchain
+            };
+            const messageTWR = {
+                data: await messages.encodeTransferErc20Message(erc20TWR.address, to, amount),
+                destinationContract: depositBoxERC20.address,
+                sender: senderFromSchain
+            };
+
+            await initializeSchain(contractManager, schainName, schainOwner.address, 1, 1);
+            await setCommonPublicKey(contractManager, schainName);
+
+            await depositBoxERC20.connect(user).depositERC20(schainName, erc20.address, amount)
+                .should.be.eventually.rejectedWith("Unconnected chain");
+
+            await linker
+                .connect(deployer)
+                .connectSchain(schainName, [deployer.address, deployer.address, deployer.address]);
+
+            await communityPool
+                .connect(user)
+                .rechargeUserWallet(schainName, user.address, { value: wei });
+
+            await depositBoxERC20.connect(schainOwner).disableWhitelist(schainName);
+            await erc20.connect(deployer).mint(user.address, amount * 2);
+            await erc20TWR.connect(deployer).mint(user.address, amount * 2);
+
+            await erc20.connect(user).approve(depositBoxERC20.address, amount * 2);
+            await erc20TWR.connect(user).approve(depositBoxERC20.address, amount * 2);
+
+            await depositBoxERC20.connect(user).depositERC20(schainName, erc20.address, amount);
+            await depositBoxERC20.connect(user).depositERC20(schainName, erc20TWR.address, amount);
+
+            const balanceBefore = await deployer.getBalance();
+            await messageProxy.connect(nodeAddress).postIncomingMessages(schainName, 0, [message, messageTWR], sign);
+            const balance = await deployer.getBalance();
+            balance.should.be.least(balanceBefore);
+            balance.should.be.closeTo(balanceBefore, 10);
+
+            await depositBoxERC20.connect(user).depositERC20(schainName, erc20.address, amount);
+            await depositBoxERC20.connect(user).depositERC20(schainName, erc20TWR.address, amount);
+            await messageProxy.connect(nodeAddress).postIncomingMessages(schainName, 2, [message, messageTWR], sign);
+            expect(BigNumber.from(await depositBoxERC20.transferredAmount(schainHash, erc20.address)).toString()).to.be.equal(BigNumber.from(0).toString());
+            expect(BigNumber.from(await depositBoxERC20.transferredAmount(schainHash, erc20TWR.address)).toString()).to.be.equal(BigNumber.from(0).toString());
+
+            (await erc20.balanceOf(user.address)).toString().should.be.equal((amount * 2).toString());
+            (await erc20TWR.balanceOf(user.address)).toString().should.be.equal((amount * 2).toString());
+
+        });
+
         describe("When user deposited tokens", async () => {
             let token: ERC20OnChain;
             let token2: ERC20OnChain;
@@ -370,10 +471,19 @@ describe("DepositBoxERC20", () => {
                 await token2.connect(user).approve(depositBoxERC20.address, depositedAmount);
                 await depositBoxERC20.connect(user).depositERC20(schainName, token2.address, depositedAmount);
 
-                await depositBoxERC20.connect(schainOwner).setBigTransferValue(schainName, token.address, bigAmount);
+                await expect(
+                    depositBoxERC20.connect(schainOwner).setBigTransferValue(schainName, token.address, bigAmount)
+                ).to.emit(depositBoxERC20, "BigTransferThresholdIsChanged")
+                    .withArgs(schainHash, token.address, 0, bigAmount);
                 await depositBoxERC20.connect(schainOwner).setBigTransferValue(schainName, token2.address, bigAmount);
-                await depositBoxERC20.connect(schainOwner).setBigTransferDelay(schainName, timeDelay);
-                await depositBoxERC20.connect(schainOwner).setArbitrageDuration(schainName, arbitrageDuration);
+                await expect(
+                    depositBoxERC20.connect(schainOwner).setBigTransferDelay(schainName, timeDelay)
+                ).to.emit(depositBoxERC20, "BigTransferDelayIsChanged")
+                    .withArgs(schainHash, 0, timeDelay);
+                await expect(
+                    depositBoxERC20.connect(schainOwner).setArbitrageDuration(schainName, arbitrageDuration)
+                ).to.emit(depositBoxERC20, "ArbitrageDurationIsChanged")
+                    .withArgs(schainHash, 0, arbitrageDuration);
 
                 await depositBoxERC20.grantRole(await depositBoxERC20.ARBITER_ROLE(), deployer.address);
             });
@@ -512,6 +622,94 @@ describe("DepositBoxERC20", () => {
                     .should.be.equal(token1BalanceBefore.add(2 * amount + 3 * bigAmount));
             });
 
+            it("should not stuck after big amount of competed transfers", async () => {
+                const bigTransfer = {
+                    data: await messages.encodeTransferErc20Message(token.address, user.address, bigAmount),
+                    destinationContract: depositBoxERC20.address,
+                    sender: deployer.address
+                };
+
+                const token1BalanceBefore = await token.balanceOf(user.address);
+                const amountOfCompetedTransfers = 15;
+
+                // send `amountOfCompetedTransfers` + 1 big transfer
+                const batch = (await messageProxy.MESSAGES_LENGTH()).toNumber();
+                const fullBatches = Math.floor((amountOfCompetedTransfers + 1) / batch);
+                const rest = amountOfCompetedTransfers + 1 - fullBatches * batch;
+                for (let i = 0; i < fullBatches; ++i) {
+                    await messageProxy.connect(nodeAddress).postIncomingMessages(
+                        schainName,
+                        i * batch,
+                        Array(batch).fill(bigTransfer),
+                        randomSignature
+                    );
+                }
+                if (rest > 0) {
+                    await messageProxy.connect(nodeAddress).postIncomingMessages(
+                        schainName,
+                        fullBatches * batch,
+                        Array(rest).fill(bigTransfer),
+                        randomSignature
+                    );
+                }
+
+                (await token.balanceOf(user.address)).should.be.equal(token1BalanceBefore);
+
+                for (const completedTransfer of [...Array(amountOfCompetedTransfers).keys()]) {
+                    await depositBoxERC20.escalate(completedTransfer);
+                    await depositBoxERC20.connect(schainOwner).validateTransfer(completedTransfer);
+                }
+
+                (await token.balanceOf(user.address)).should.be.equal(token1BalanceBefore.add(bigAmount * amountOfCompetedTransfers));
+
+                await skipTime(timeDelay);
+
+                // first retrieve removes already completed transfers after an arbitrage from the queue
+                await depositBoxERC20.retrieveFor(user.address);
+                (await token.balanceOf(user.address)).should.be.equal(token1BalanceBefore.add(bigAmount * amountOfCompetedTransfers));
+
+                // second retrieve withdraws the rest
+                await depositBoxERC20.retrieveFor(user.address);
+                (await token.balanceOf(user.address)).should.be.equal(token1BalanceBefore.add(bigAmount * (amountOfCompetedTransfers + 1)));
+
+            });
+
+            it("should not stuck if a token reverts transfer", async () => {
+                const bigTransfer = {
+                    data: await messages.encodeTransferErc20Message(token.address, user.address, bigAmount),
+                    destinationContract: depositBoxERC20.address,
+                    sender: deployer.address
+                };
+
+                const badToken = await (await ethers.getContractFactory("RevertableERC20")).deploy("Test", "TST");
+                await badToken.mint(user.address, bigAmount);
+                await badToken.connect(user).approve(depositBoxERC20.address, bigAmount);
+                await depositBoxERC20.connect(user).depositERC20(schainName, badToken.address, bigAmount);
+
+                const badTokenBigTransfer = {
+                    data: await messages.encodeTransferErc20Message(badToken.address, user.address, bigAmount),
+                    destinationContract: depositBoxERC20.address,
+                    sender: deployer.address
+                };
+
+                await messageProxy.connect(nodeAddress).postIncomingMessages(
+                    schainName,
+                    0,
+                    [ badTokenBigTransfer, bigTransfer ],
+                    randomSignature
+                );
+
+                await skipTime(timeDelay);
+
+                await badToken.disable();
+                const balanceBefore = await token.balanceOf(user.address);
+                await expect(
+                    depositBoxERC20.retrieveFor(user.address)
+                ).to.emit(depositBoxERC20, "TransferSkipped")
+                    .withArgs(0);
+                (await token.balanceOf(user.address)).should.be.equal(balanceBefore.add(bigAmount));
+            });
+
             it("should not allow to set too big delays", async () => {
                 const tenYears = Math.round(60 * 60 * 24 * 365.25 * 10)
 
diff --git a/proxy/test/MessageProxy.ts b/proxy/test/MessageProxy.ts
index 57393830c..6d613f821 100644
--- a/proxy/test/MessageProxy.ts
+++ b/proxy/test/MessageProxy.ts
@@ -283,7 +283,10 @@ describe("MessageProxy", () => {
             const pauseableRole = await messageProxyForMainnet.PAUSABLE_ROLE();
 
             await messageProxyForMainnet.connect(deployer).grantRole(pauseableRole, client.address);
-            await messageProxyForMainnet.connect(client).pause(schainName);
+            await expect(
+                messageProxyForMainnet.connect(client).pause(schainName)
+            ).to.emit(messageProxyForMainnet, "SchainPaused")
+                .withArgs(schainHash);
             await messageProxyForMainnet.connect(client).pause(schainName).should.be.rejectedWith("Already paused");
 
             (await messageProxyForMainnet.isPaused(schainHash)).should.be.deep.equal(true);
@@ -296,7 +299,10 @@ describe("MessageProxy", () => {
                 .should.be.rejectedWith("IMA is paused");
 
             await messageProxyForMainnet.connect(client).resume(schainName).should.be.rejectedWith("Incorrect sender");
-            await messageProxyForMainnet.connect(schainOwner).resume(schainName);
+            await expect(
+                messageProxyForMainnet.connect(schainOwner).resume(schainName)
+            ).to.emit(messageProxyForMainnet, "SchainResumed")
+                .withArgs(schainHash);
             await messageProxyForMainnet.connect(deployer).resume(schainName).should.be.rejectedWith("Already unpaused");
 
             (await messageProxyForMainnet.isPaused(schainHash)).should.be.deep.equal(false);