Repository: skalenetwork/IMA (language: python)

Issue:
Fixed old-style event log scanner for IMA messages
- Fixed old-style event log scanner for IMA messages
- Added details about iterative event search
- No new APIs added
- No function arguments changed
- No new tests needed


Discussion / hints:

Fix patch:
diff --git a/agent/clpTools.mjs b/agent/clpTools.mjs
index 8bb30473c..811d02cfb 100644
--- a/agent/clpTools.mjs
+++ b/agent/clpTools.mjs
@@ -1314,21 +1314,14 @@ export function commandLineTaskTransferM2S() {
                 idxChainKnownForS2S: 0,
                 cntChainsKnownForS2S: 0
             };
-            const optsChainPair = {
-                "strDirection": "M2S",
-                "chainSrc": imaState.chainProperties.mn,
-                "chainDst": imaState.chainProperties.sc
-            };
             return await IMA.doTransfer( // main-net --> s-chain
                 "M2S",
                 joRuntimeOpts,
                 imaState.chainProperties.mn.ethersProvider,
                 imaState.joMessageProxyMainNet,
-                imaState.chainProperties.mn.joAbiIMA.message_proxy_mainnet_abi,
                 imaState.chainProperties.mn.joAccount,
                 imaState.chainProperties.sc.ethersProvider,
                 imaState.joMessageProxySChain,
-                imaState.chainProperties.sc.joAbiIMA.message_proxy_chain_abi,
                 imaState.chainProperties.sc.joAccount,
                 imaState.chainProperties.mn.strChainName,
                 imaState.chainProperties.sc.strChainName,
@@ -1343,8 +1336,7 @@ export function commandLineTaskTransferM2S() {
                 imaState.nBlockAgeM2S,
                 imaBLS.doSignMessagesM2S,
                 null,
-                imaState.chainProperties.sc.transactionCustomizer,
-                optsChainPair
+                imaState.chainProperties.sc.transactionCustomizer
             );
         }
     } );
@@ -1362,21 +1354,14 @@ export function commandLineTaskTransferS2M() {
                 idxChainKnownForS2S: 0,
                 cntChainsKnownForS2S: 0
             };
-            const optsChainPair = {
-                "strDirection": "S2M",
-                "chainSrc": imaState.chainProperties.sc,
-                "chainDst": imaState.chainProperties.mn
-            };
             return await IMA.doTransfer( // s-chain --> main-net
                 "S2M",
                 joRuntimeOpts,
                 imaState.chainProperties.sc.ethersProvider,
                 imaState.joMessageProxySChain,
-                imaState.chainProperties.sc.joAbiIMA.message_proxy_chain_abi,
                 imaState.chainProperties.sc.joAccount,
                 imaState.chainProperties.mn.ethersProvider,
                 imaState.joMessageProxyMainNet,
-                imaState.chainProperties.sc.joAbiIMA.message_proxy_mainnet_abi,
                 imaState.chainProperties.mn.joAccount,
                 imaState.chainProperties.sc.strChainName,
                 imaState.chainProperties.mn.strChainName,
@@ -1391,8 +1376,7 @@ export function commandLineTaskTransferS2M() {
                 imaState.nBlockAgeS2M,
                 imaBLS.doSignMessagesS2M,
                 null,
-                imaState.chainProperties.mn.transactionCustomizer,
-                optsChainPair
+                imaState.chainProperties.mn.transactionCustomizer
             );
         }
     } );
@@ -1419,7 +1403,6 @@ export function commandLineTaskTransferS2S() {
                 skaleObserver,
                 imaState.chainProperties.sc.ethersProvider,
                 imaState.joMessageProxySChain,
-                imaState.chainProperties.sc.joAbiIMA.message_proxy_chain_abi,
                 imaState.chainProperties.sc.joAccount,
                 imaState.chainProperties.sc.strChainName,
                 imaState.chainProperties.sc.chainId,
diff --git a/agent/loop.mjs b/agent/loop.mjs
index ae8a64cc2..ab90ece44 100644
--- a/agent/loop.mjs
+++ b/agent/loop.mjs
@@ -220,21 +220,15 @@ async function singleTransferLoopPartM2S( optsLoop, strLogPrefix ) {
                 if( checkTimeFraming( null, "m2s", optsLoop.joRuntimeOpts ) ) {
                     imaState.loopState.m2s.isInProgress = true;
                     await pwa.notifyOnLoopStart( imaState, "m2s" );
-                    const optsChainPair = {
-                        "strDirection": "M2S",
-                        "chainSrc": imaState.chainProperties.mn,
-                        "chainDst": imaState.chainProperties.sc
-                    };
                     b1 = await IMA.doTransfer( // main-net --> s-chain
                         "M2S",
                         optsLoop.joRuntimeOpts,
+
                         imaState.chainProperties.mn.ethersProvider,
                         imaState.joMessageProxyMainNet,
-                        imaState.chainProperties.sc.joAbiIMA.message_proxy_mainnet_abi,
                         imaState.chainProperties.mn.joAccount,
                         imaState.chainProperties.sc.ethersProvider,
                         imaState.joMessageProxySChain,
-                        imaState.chainProperties.sc.joAbiIMA.message_proxy_chain_abi,
                         imaState.chainProperties.sc.joAccount,
                         imaState.chainProperties.mn.strChainName,
                         imaState.chainProperties.sc.strChainName,
@@ -249,8 +243,7 @@ async function singleTransferLoopPartM2S( optsLoop, strLogPrefix ) {
                         imaState.nBlockAgeM2S,
                         imaBLS.doSignMessagesM2S,
                         null,
-                        imaState.chainProperties.sc.transactionCustomizer,
-                        optsChainPair
+                        imaState.chainProperties.sc.transactionCustomizer
                     );
                     imaState.loopState.m2s.isInProgress = false;
                     await pwa.notifyOnLoopEnd( imaState, "m2s" );
@@ -306,21 +299,15 @@ async function singleTransferLoopPartS2M( optsLoop, strLogPrefix ) {
                 if( checkTimeFraming( null, "s2m", optsLoop.joRuntimeOpts ) ) {
                     imaState.loopState.s2m.isInProgress = true;
                     await pwa.notifyOnLoopStart( imaState, "s2m" );
-                    const optsChainPair = {
-                        "strDirection": "S2M",
-                        "chainSrc": imaState.chainProperties.sc,
-                        "chainDst": imaState.chainProperties.mn
-                    };
                     b2 = await IMA.doTransfer( // s-chain --> main-net
                         "S2M",
                         optsLoop.joRuntimeOpts,
+
                         imaState.chainProperties.sc.ethersProvider,
                         imaState.joMessageProxySChain,
-                        imaState.chainProperties.sc.joAbiIMA.message_proxy_chain_abi,
                         imaState.chainProperties.sc.joAccount,
                         imaState.chainProperties.mn.ethersProvider,
                         imaState.joMessageProxyMainNet,
-                        imaState.chainProperties.sc.joAbiIMA.message_proxy_mainnet_abi,
                         imaState.chainProperties.mn.joAccount,
                         imaState.chainProperties.sc.strChainName,
                         imaState.chainProperties.mn.strChainName,
@@ -335,8 +322,7 @@ async function singleTransferLoopPartS2M( optsLoop, strLogPrefix ) {
                         imaState.nBlockAgeS2M,
                         imaBLS.doSignMessagesS2M,
                         null,
-                        imaState.chainProperties.mn.transactionCustomizer,
-                        optsChainPair
+                        imaState.chainProperties.mn.transactionCustomizer
                     );
                     imaState.loopState.s2m.isInProgress = false;
                     await pwa.notifyOnLoopEnd( imaState, "s2m" );
@@ -385,7 +371,6 @@ async function singleTransferLoopPartS2S( optsLoop, strLogPrefix ) {
                 skaleObserver,
                 imaState.chainProperties.sc.ethersProvider,
                 imaState.joMessageProxySChain,
-                imaState.chainProperties.sc.joAbiIMA.message_proxy_chain_abi,
                 imaState.chainProperties.sc.joAccount,
                 imaState.chainProperties.sc.strChainName,
                 imaState.chainProperties.sc.chainId,
@@ -849,7 +834,7 @@ export async function ensureHaveWorkers( opts ) {
         };
         while( ! aClient.logicalInitComplete ) {
             if( log.verboseGet() >= log.verboseReversed().info )
-                log.write( "LOOP server is not initialized yet...\n" );
+                log.write( "LOOP server is not inited yet...\n" );
             await threadInfo.sleep( 1000 );
             aClient.send( jo );
         }
diff --git a/npms/skale-ima/imaEventLogScan.mjs b/npms/skale-ima/imaEventLogScan.mjs
index bf7da1fc9..a757c1fcd 100644
--- a/npms/skale-ima/imaEventLogScan.mjs
+++ b/npms/skale-ima/imaEventLogScan.mjs
@@ -31,12 +31,6 @@ import * as rpcCall from "../../agent/rpcCall.mjs";
 import * as imaHelperAPIs from "./imaHelperAPIs.mjs";
 import * as imaTransferErrorHandling from "./imaTransferErrorHandling.mjs";
 
-import * as childProcessModule from "child_process";
-import * as path from "path";
-import * as url from "url";
-
-const __dirname = path.dirname( url.fileURLToPath( import.meta.url ) );
-
 export function createProgressiveEventsScanPlan( details, nLatestBlockNumber ) {
     // assume Main Net mines 6 blocks per minute
     const blocksInOneMinute = 6;
@@ -92,13 +86,6 @@ export function createProgressiveEventsScanPlan( details, nLatestBlockNumber ) {
     return arrProgressiveEventsScanPlan;
 }
 
-export function extractEventArg( arg ) {
-    if( arg && typeof arg == "object" && "type" in arg && typeof arg.type == "string" &&
-        arg.type == "BigNumber" && "hex" in arg && typeof arg.hex == "string" )
-        return owaspUtils.toBN( arg.hex );
-    return arg;
-}
-
 function generateWhileTransferringLogMessageSuffix( optsChainPair ) {
     if( ! optsChainPair )
         return "";
@@ -116,64 +103,9 @@ function generateWhileTransferringLogMessageSuffix( optsChainPair ) {
         cc.debug( " transfer)" );
 }
 
-export async function safeGetPastEventsProgressiveExternal(
-    details, strLogPrefix, ethersProvider, attempts,
-    joContract, joABI, strEventName,
-    nBlockFrom, nBlockTo, joFilter, optsChainPair
-) {
-    if( joABI && typeof joABI == "object" ) {
-        const escapeShell = function( cmd ) {
-            return "\"" + cmd.replace( /(["'$`\\])/g,"\\$1" ) + "\"";
-        };
-        const joArg = {
-            "url": owaspUtils.ethersProviderToUrl( ethersProvider ),
-            "attempts": attempts,
-            "strEventName": strEventName,
-            "nBlockFrom": nBlockFrom,
-            "nBlockTo": nBlockTo,
-            "joFilter": joFilter,
-            "address": joContract.address,
-            "abi": joABI
-        };
-        const cmd = "node " + path.join( __dirname, "imaExternalLogScan.mjs" ) + " " +
-            escapeShell( JSON.stringify( joArg ) );
-        if( log.verboseGet() >= log.verboseReversed().trace ) {
-            details.write( strLogPrefix +
-                cc.debug( "Will run external command to search logs for event " ) +
-                cc.j( strEventName ) + cc.debug( " via URL " ) + cc.u( joArg.url ) +
-                generateWhileTransferringLogMessageSuffix( optsChainPair ) +
-                cc.debug( "..." ) + "\n" );
-        }
-        const res = childProcessModule.execSync( cmd );
-        if( "error" in res && res.error ) {
-            if( log.verboseGet() >= log.verboseReversed().error ) {
-                details.write( strLogPrefix +
-                    cc.error( "Got error from external command to search logs for event " ) +
-                    cc.j( strEventName ) + cc.error( " via URL " ) + cc.u( joArg.url ) +
-                    generateWhileTransferringLogMessageSuffix( optsChainPair ) +
-                    cc.error( ":" ) + cc.warning( owaspUtils.extractErrorMessage( err ) ) + "\n" );
-            }
-            throw new Error( res.error );
-        }
-        if( log.verboseGet() >= log.verboseReversed().trace ) {
-            details.write( strLogPrefix +
-                cc.debug( "Done running external command to search logs for event " ) +
-                cc.j( strEventName ) + cc.debug( " via URL " ) + cc.u( joArg.url ) +
-                generateWhileTransferringLogMessageSuffix( optsChainPair ) +
-                cc.debug( "." ) + "\n" );
-        }
-        return JSON.parse( res ).result;
-    }
-    return await safeGetPastEventsProgressive(
-        details, strLogPrefix, ethersProvider, attempts,
-        joContract, strEventName,
-        nBlockFrom, nBlockTo, joFilter );
-}
-
 export async function safeGetPastEventsProgressive(
-    details, strLogPrefix, ethersProvider, attempts,
-    joContract, strEventName,
-    nBlockFrom, nBlockTo, joFilter
+    details, strLogPrefix, ethersProvider, attempts, joContract, strEventName,
+    nBlockFrom, nBlockTo, joFilter, optsChainPair
 ) {
     if( ! imaTransferErrorHandling.getEnabledProgressiveEventsScan() ) {
         details.write( strLogPrefix +
@@ -188,6 +120,16 @@ export async function safeGetPastEventsProgressive(
             nBlockFrom, nBlockTo, joFilter
         );
     }
+    if( log.verboseGet() >= log.verboseReversed().information ) {
+        details.write( strLogPrefix +
+            cc.info( "Will run progressive event log search for event " ) +
+            cc.j( strEventName ) + cc.info( " via URL " ) +
+            cc.u( owaspUtils.ethersProviderToUrl( ethersProvider ) ) +
+            generateWhileTransferringLogMessageSuffix( optsChainPair ) +
+            cc.info( ", from block " ) + cc.notice( nBlockFrom ) +
+            cc.info( ", to block " ) + cc.notice( nBlockTo ) +
+            cc.info( "..." ) + "\n" );
+    }
     const nLatestBlockNumber = owaspUtils.toBN(
         await imaHelperAPIs.safeGetBlockNumber( details, 10, ethersProvider ) );
     const nLatestBlockNumberPlus1 = nLatestBlockNumber.add( owaspUtils.toBN( 1 ) );
diff --git a/npms/skale-ima/imaExternalLogScan.mjs b/npms/skale-ima/imaExternalLogScan.mjs
deleted file mode 100644
index 6c740d497..000000000
--- a/npms/skale-ima/imaExternalLogScan.mjs
+++ /dev/null
@@ -1,88 +0,0 @@
-// SPDX-License-Identifier: AGPL-3.0-only
-
-/**
- * @license
- * SKALE IMA
- *
- * 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/>.
- */
-
-/**
- * @file imaExternalLogScan.mjs
- * @copyright SKALE Labs 2019-Present
- */
-
-import * as cc from "../skale-cc/cc.mjs";
-import * as log from "../skale-log/log.mjs";
-import * as owaspUtils from "../skale-owasp/owaspUtils.mjs";
-import * as imaEventLogScan from "./imaEventLogScan.mjs";
-
-const gIsDebugLogging = false; // development option only, must be always false
-cc.enable( false );
-log.addStdout();
-
-// allow self-signed wss and https
-process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
-
-function finalizeOutput( jo ) {
-    if( ! jo )
-        return;
-    cc.enable( false );
-    process.stdout.write( cc.j( jo ) );
-}
-
-async function run() {
-    const details = log.createMemoryStream();
-    try {
-        if( gIsDebugLogging ) {
-            log.write( cc.debug( "Process startup arguments array is " ) +
-                cc.j( process.argv ) + "\n" );
-        }
-        if( process.argv.length != 3 )
-            throw new Error( "Wrong number of command line arguments" );
-
-        if( gIsDebugLogging ) {
-            log.write( cc.debug( "Main argument text is " ) +
-                cc.j( process.argv[2] ) + "\n" );
-        }
-        const joArg = JSON.parse( process.argv[2] );
-        if( gIsDebugLogging ) {
-            log.write( cc.debug( "Main argument JSON is " ) +
-                cc.j( joArg ) + "\n" );
-        }
-
-        const ethersProvider = owaspUtils.getEthersProviderFromURL( joArg.url );
-        const joContract = new owaspUtils.ethersMod.ethers.Contract(
-            joArg.address, joArg.abi, ethersProvider );
-
-        const arrLogRecordReferencesWalk = await imaEventLogScan.safeGetPastEventsProgressive(
-            details, "", ethersProvider, joArg.attempts,
-            joContract, joArg.strEventName,
-            joArg.nBlockFrom, joArg.nBlockTo, joArg.joFilter );
-
-        finalizeOutput( { "result": arrLogRecordReferencesWalk, "error": null } );
-        process.exit( 0 );
-    } catch ( err ) {
-        if( gIsDebugLogging ) {
-            log.write( cc.error( "Failed to create RPC call: " ) +
-        cc.j( err ) + "\n" );
-        }
-        finalizeOutput( {
-            "error": owaspUtils.extractErrorMessage( err ),
-            "output": details.toString()
-        } );
-        process.exit( 1 );
-    }
-}
-run();
diff --git a/npms/skale-ima/imaSgxExternalSigner.mjs b/npms/skale-ima/imaSgxExternalSigner.mjs
index 60fe93b9b..87715dcc4 100644
--- a/npms/skale-ima/imaSgxExternalSigner.mjs
+++ b/npms/skale-ima/imaSgxExternalSigner.mjs
@@ -6,7 +6,7 @@ import * as rpcCall from "../../agent/rpcCall.mjs";
 
 const gIsDebugLogging = false; // development option only, must be always false
 const isColors = owaspUtils.toBoolean( process.argv[2] );
-cc.enable( false );
+cc.enable( true );
 log.addStdout();
 
 // allow self-signed wss and https
diff --git a/npms/skale-ima/index.mjs b/npms/skale-ima/index.mjs
index 93b9ff712..2b3ccd659 100644
--- a/npms/skale-ima/index.mjs
+++ b/npms/skale-ima/index.mjs
@@ -44,14 +44,14 @@ const perMessageGasForTransfer = 1000000;
 const additionalS2MTransferOverhead = 200000;
 
 async function findOutReferenceLogRecord(
-    details, strLogPrefix, ethersProvider, joMessageProxy, joABI,
-    bnBlockId, nMessageNumberToFind, isVerbose, optsChainPair
+    details, strLogPrefix, ethersProvider, joMessageProxy,
+    bnBlockId, nMessageNumberToFind, isVerbose
 ) {
     const bnMessageNumberToFind = owaspUtils.toBN( nMessageNumberToFind.toString() );
     const strEventName = "PreviousMessageReference";
-    const arrLogRecords = await imaEventLogScan.safeGetPastEventsProgressiveExternal(
-        details, strLogPrefix, ethersProvider, 10, joMessageProxy, joABI, strEventName,
-        bnBlockId, bnBlockId, joMessageProxy.filters[strEventName](), optsChainPair
+    const arrLogRecords = await imaEventLogScan.safeGetPastEventsProgressive(
+        details, strLogPrefix, ethersProvider, 10, joMessageProxy, strEventName,
+        bnBlockId, bnBlockId, joMessageProxy.filters[strEventName]()
     );
     const cntLogRecord = arrLogRecords.length;
     if( isVerbose ) {
@@ -64,8 +64,8 @@ async function findOutReferenceLogRecord(
     for( let idxLogRecord = 0; idxLogRecord < cntLogRecord; ++ idxLogRecord ) {
         const joEvent = arrLogRecords[idxLogRecord];
         const eventValuesByName = {
-            "currentMessage": imaEventLogScan.extractEventArg( joEvent.args[0] ),
-            "previousOutgoingMessageBlockId": imaEventLogScan.extractEventArg( joEvent.args[1] )
+            "currentMessage": joEvent.args[0],
+            "previousOutgoingMessageBlockId": joEvent.args[1]
         };
         const joReferenceLogRecord = {
             "currentMessage": eventValuesByName.currentMessage,
@@ -96,7 +96,7 @@ async function findOutReferenceLogRecord(
 }
 
 async function findOutAllReferenceLogRecords(
-    details, strLogPrefix, ethersProvider, joMessageProxy, joABI,
+    details, strLogPrefix, ethersProvider, joMessageProxy,
     bnBlockId, nIncMsgCnt, nOutMsgCnt, isVerbose, optsChainPair
 ) {
     if( isVerbose ) {
@@ -126,7 +126,7 @@ async function findOutAllReferenceLogRecords(
     let nWalkBlockId = bnBlockId;
     for( ; nWalkMsgNumber >= nIncMsgCnt; -- nWalkMsgNumber ) {
         const joReferenceLogRecord = await findOutReferenceLogRecord(
-            details, strLogPrefix, ethersProvider, joMessageProxy, joABI,
+            details, strLogPrefix, ethersProvider, joMessageProxy,
             nWalkBlockId, nWalkMsgNumber, isVerbose, optsChainPair );
         if( joReferenceLogRecord == null )
             break;
@@ -278,15 +278,16 @@ async function doQueryOutgoingMessageCounter( optsTransfer ) {
                     optsTransfer.chainNameDst,
                     { from: optsTransfer.joAccountSrc.address() } ) );
         try {
-            optsTransfer.strActionName =
-                "in-getOutgoingMessagesCounter()--findOutAllReferenceLogRecords()";
-            optsTransfer.arrLogRecordReferences =
-                await findOutAllReferenceLogRecords( optsTransfer.details,
-                    optsTransfer.strLogPrefixShort, optsTransfer.ethersProviderSrc,
-                    optsTransfer.joMessageProxySrc, optsTransfer.joMessageProxySrcABI,
-                    bnBlockId, optsTransfer.nIncMsgCnt, optsTransfer.nOutMsgCnt, true,
-                    optsTransfer.optsChainPair );
-            return true; // success, finish at this point
+            if( bnBlockId ) {
+                optsTransfer.strActionName =
+                    "in-getOutgoingMessagesCounter()--findOutAllReferenceLogRecords()";
+                optsTransfer.arrLogRecordReferences =
+                    await findOutAllReferenceLogRecords( optsTransfer.details,
+                        optsTransfer.strLogPrefixShort, optsTransfer.ethersProviderSrc,
+                        optsTransfer.joMessageProxySrc, bnBlockId, optsTransfer.nIncMsgCnt,
+                        optsTransfer.nOutMsgCnt, true, optsTransfer.optsChainPair );
+                return true; // success, finish at this point
+            }
         } catch ( err ) {
             optsTransfer.arrLogRecordReferences = [];
             if( log.verboseGet() >= log.verboseReversed().error ) {
@@ -320,14 +321,13 @@ async function doQueryOutgoingMessageCounter( optsTransfer ) {
         ++ nWalkMsgNumber
     ) {
         const joFilter = optsTransfer.joMessageProxySrc.filters[strEventName](
-            owaspUtils.ethersMod.ethers.utils.id( optsTransfer.chainIdDst ),
+            owaspUtils.ethersMod.ethers.utils.id( optsTransfer.chainNameDst ),
             owaspUtils.toBN( nWalkMsgNumber )
         );
-        const arrLogRecordReferencesWalk = await imaEventLogScan
-            .safeGetPastEventsProgressiveExternal( optsTransfer.details,
-                optsTransfer.strLogPrefixShort, optsTransfer.ethersProviderSrc, attempts,
-                optsTransfer.joMessageProxySrc, optsTransfer.joMessageProxySrcABI, strEventName,
-                nBlockFrom, nBlockTo, joFilter, optsTransfer.optsChainPair );
+        const arrLogRecordReferencesWalk = await imaEventLogScan.safeGetPastEventsProgressive(
+            optsTransfer.details, optsTransfer.strLogPrefixShort,
+            optsTransfer.ethersProviderSrc, attempts, optsTransfer.joMessageProxySrc,
+            strEventName, nBlockFrom, nBlockTo, joFilter, optsTransfer.optsChainPair );
         optsTransfer.arrLogRecordReferences =
             optsTransfer.arrLogRecordReferences.concat( arrLogRecordReferencesWalk );
     }
@@ -354,11 +354,11 @@ async function analyzeGatheredRecords( optsTransfer, r ) {
                 cc.debug( " with data " ) + cc.j( joEvent ) + "\n" );
         }
         const eventValuesByName = {
-            "dstChainHash": imaEventLogScan.extractEventArg( joEvent.args[0] ),
-            "msgCounter": imaEventLogScan.extractEventArg( joEvent.args[1] ),
-            "srcContract": imaEventLogScan.extractEventArg( joEvent.args[2] ),
-            "dstContract": imaEventLogScan.extractEventArg( joEvent.args[3] ),
-            "data": imaEventLogScan.extractEventArg( joEvent.args[4] )
+            "dstChainHash": joEvent.args[0],
+            "msgCounter": joEvent.args[1],
+            "srcContract": joEvent.args[2],
+            "dstContract": joEvent.args[3],
+            "data": joEvent.args[4]
         };
         if( eventValuesByName.dstChainHash == strChainHashWeAreLookingFor ) {
             joValues = eventValuesByName;
@@ -407,16 +407,18 @@ async function gatherMessages( optsTransfer ) {
     for( let idxInBlock = 0; // inner loop wil create block of transactions
         optsTransfer.nIdxCurrentMsg < optsTransfer.nOutMsgCnt &&
             idxInBlock < optsTransfer.nTransactionsCountInBlock;
-        ++optsTransfer.nIdxCurrentMsg, ++idxInBlock, ++optsTransfer.cntAccumulatedForBlock
-    ) {
+        ++optsTransfer.nIdxCurrentMsg, ++idxInBlock, ++optsTransfer.cntAccumulatedForBlock ) {
         const idxProcessing = optsTransfer.cntProcessed + idxInBlock;
         if( idxProcessing > optsTransfer.nMaxTransactionsCount )
             break;
         let nBlockFrom = 0, nBlockTo = "latest";
         if( optsTransfer.arrLogRecordReferences.length > 0 ) {
             const joReferenceLogRecord = optsTransfer.arrLogRecordReferences.shift();
-            nBlockFrom = joReferenceLogRecord.currentBlockId;
-            nBlockTo = joReferenceLogRecord.currentBlockId;
+            if( joReferenceLogRecord && "currentBlockId" in joReferenceLogRecord &&
+                joReferenceLogRecord.currentBlockId ) {
+                nBlockFrom = joReferenceLogRecord.currentBlockId;
+                nBlockTo = joReferenceLogRecord.currentBlockId;
+            }
         }
         optsTransfer.strActionName = "src-chain->MessageProxy->scan-past-events()";
         const strEventName = "OutgoingMessage";
@@ -425,10 +427,10 @@ async function gatherMessages( optsTransfer ) {
                 cc.notice( optsTransfer.strActionName ) + cc.debug( " for " ) +
                 cc.info( strEventName ) + cc.debug( " event..." ) + "\n" );
         }
-        r = await imaEventLogScan.safeGetPastEventsProgressiveExternal( optsTransfer.details,
+        r = await imaEventLogScan.safeGetPastEventsProgressive( optsTransfer.details,
             optsTransfer.strLogPrefixShort, optsTransfer.ethersProviderSrc, 10,
-            optsTransfer.joMessageProxySrc, optsTransfer.joMessageProxySrcABI, strEventName,
-            nBlockFrom, nBlockTo, optsTransfer.joMessageProxySrc.filters[strEventName](
+            optsTransfer.joMessageProxySrc, strEventName, nBlockFrom, nBlockTo,
+            optsTransfer.joMessageProxySrc.filters[strEventName](
                 owaspUtils.ethersMod.ethers.utils.id( optsTransfer.chainNameDst ),
                 owaspUtils.toBN( optsTransfer.nIdxCurrentMsg )
             ), optsTransfer.optsChainPair );
@@ -442,9 +444,8 @@ async function gatherMessages( optsTransfer ) {
             try {
                 const transactionHash = r[0].transactionHash;
                 if( log.verboseGet() >= log.verboseReversed().debug ) {
-                    optsTransfer.details.write( optsTransfer.strLogPrefix +
-                        cc.debug( "Event transactionHash is " ) +
-                        cc.info( transactionHash ) + "\n" );
+                    optsTransfer.details.write( optsTransfer.strLogPrefix + cc.debug( "Event " +
+                        "transactionHash is " ) + cc.info( transactionHash ) + "\n" );
                 }
                 const blockNumber = r[0].blockNumber;
                 optsTransfer.details.write( optsTransfer.strLogPrefix +
@@ -452,18 +453,17 @@ async function gatherMessages( optsTransfer ) {
                 const nLatestBlockNumber = await imaHelperAPIs.safeGetBlockNumber(
                     optsTransfer.details, 10, optsTransfer.ethersProviderSrc );
                 if( log.verboseGet() >= log.verboseReversed().debug ) {
-                    optsTransfer.details.write( optsTransfer.strLogPrefix +
-                        cc.debug( "Latest blockNumber is " ) + cc.info( nLatestBlockNumber ) +
-                        "\n" );
+                    optsTransfer.details.write( optsTransfer.strLogPrefix + cc.debug( "Latest " +
+                    "blockNumber is " ) + cc.info( nLatestBlockNumber ) + "\n" );
                 }
                 const nDist = nLatestBlockNumber - blockNumber;
                 if( nDist < optsTransfer.nBlockAwaitDepth )
                     bSecurityCheckPassed = false;
                 if( log.verboseGet() >= log.verboseReversed().debug ) {
-                    optsTransfer.details.write( optsTransfer.strLogPrefix +
-                        cc.debug( "Distance by blockNumber is " ) + cc.info( nDist ) +
-                        cc.debug( ", await check is " ) + ( bSecurityCheckPassed
-                        ? cc.success( "PASSED" ) : cc.error( "FAILED" ) ) + "\n" );
+                    const cp = bSecurityCheckPassed ? cc.success( "PASSED" ) : cc.error( "FAILED" );
+                    optsTransfer.details.write( optsTransfer.strLogPrefix + cc.debug( "Distance " +
+                        "by blockNumber is " ) + cc.info( nDist ) + cc.debug( ", await check " +
+                        "is " ) + cp + "\n" );
                 }
             } catch ( err ) {
                 bSecurityCheckPassed = false;
@@ -471,9 +471,9 @@ async function gatherMessages( optsTransfer ) {
                     const strError = owaspUtils.extractErrorMessage( err );
                     const s = optsTransfer.strLogPrefix + cc.fatal( "CRITICAL ERROR:" ) +
                         cc.error( " Exception(evaluate block depth) while getting transaction " +
-                        "hash and block number during " + optsTransfer.strActionName + ": " ) +
-                        cc.error( strError ) + cc.error( ", stack is: " ) + "\n" +
-                        cc.stack( err.stack ) + "\n";
+                            "hash and block number during " + optsTransfer.strActionName + ": " ) +
+                        cc.error( strError ) + cc.error( ", stack is: " ) +
+                        "\n" + cc.stack( err.stack ) + "\n";
                     optsTransfer.details.write( s );
                     if( log.id != optsTransfer.details.id )
                         log.write( s );
@@ -504,9 +504,8 @@ async function gatherMessages( optsTransfer ) {
             try {
                 const transactionHash = r[0].transactionHash;
                 if( log.verboseGet() >= log.verboseReversed().debug ) {
-                    optsTransfer.details.write( optsTransfer.strLogPrefix +
-                        cc.debug( "Event transactionHash is " ) + cc.info( transactionHash ) +
-                        "\n" );
+                    optsTransfer.details.write( optsTransfer.strLogPrefix + cc.debug( "Event " +
+                        "transactionHash is " ) + cc.info( transactionHash ) + "\n" );
                 }
                 const blockNumber = r[0].blockNumber;
                 if( log.verboseGet() >= log.verboseReversed().debug ) {
@@ -563,25 +562,21 @@ async function gatherMessages( optsTransfer ) {
             optsTransfer.strActionName = "" + strActionNameOld;
             if( !bSecurityCheckPassed ) {
                 if( log.verboseGet() >= log.verboseReversed().warning ) {
-                    optsTransfer.details.write( optsTransfer.strLogPrefix +
-                        cc.warning( "Block age check was not passed, " +
-                            "canceling search for transfer events" ) + "\n" );
+                    optsTransfer.details.write( optsTransfer.strLogPrefix + cc.warning( "Block " +
+                    "age check was not passed, canceling search for transfer events" ) + "\n" );
                 }
                 break;
             }
         }
         if( log.verboseGet() >= log.verboseReversed().debug ) {
             optsTransfer.details.write( optsTransfer.strLogPrefix +
-                cc.success( "Got event optsTransfer.details from " ) +
-                cc.notice( "getPastEvents()" ) + cc.success( " event invoked with " ) +
-                cc.notice( "msgCounter" ) + cc.success( " set to " ) +
-                cc.info( optsTransfer.nIdxCurrentMsg ) + cc.success( " and " ) +
-                cc.notice( "dstChain" ) + cc.success( " set to " ) +
+                cc.success( "Got event optsTransfer.details from getPastEvents() event invoked " +
+                "with msgCounter set to " ) + cc.info( optsTransfer.nIdxCurrentMsg ) +
+                cc.success( " and " ) + cc.notice( "dstChain" ) + cc.success( " set to " ) +
                 cc.info( optsTransfer.chainNameDst ) + cc.success( ", event description: " ) +
                 cc.j( joValues ) + "\n" );
-            optsTransfer.details.write( optsTransfer.strLogPrefix +
-                cc.debug( "Will process message counter value " ) +
-                cc.info( optsTransfer.nIdxCurrentMsg ) + "\n" );
+            optsTransfer.details.write( optsTransfer.strLogPrefix + cc.debug( "Will process " +
+                "message counter value " ) + cc.info( optsTransfer.nIdxCurrentMsg ) + "\n" );
         }
         optsTransfer.arrMessageCounters.push( optsTransfer.nIdxCurrentMsg );
         const joMessage = {
@@ -590,8 +585,7 @@ async function gatherMessages( optsTransfer ) {
             "to": joValues.to,
             "amount": joValues.amount,
             "data": joValues.data,
-            "savedBlockNumberForOptimizations":
-                joValues.savedBlockNumberForOptimizations
+            "savedBlockNumberForOptimizations": joValues.savedBlockNumberForOptimizations
         };
         optsTransfer.jarrMessages.push( joMessage );
     }
@@ -889,7 +883,6 @@ async function handleAllMessagesSigning( optsTransfer ) {
 }
 
 async function checkOutgoingMessageEvent( optsTransfer, joSChain ) {
-    const sc = optsTransfer.imaState.chainProperties.sc;
     const cntNodes = joSChain.data.computed.nodes.length;
     const cntMessages = optsTransfer.jarrMessages.length;
     for( let idxMessage = 0; idxMessage < cntMessages; ++ idxMessage ) {
@@ -920,22 +913,25 @@ async function checkOutgoingMessageEvent( optsTransfer, joSChain ) {
                 let bEventIsFound = false;
                 try {
                     // eslint-disable-next-line dot-notation
-                    const ethersProviderNode =
-                        owaspUtils.getEthersProviderFromURL( strUrlHttp );
+                    const ethersProviderNode = owaspUtils.getEthersProviderFromURL( strUrlHttp );
                     const joMessageProxyNode = new owaspUtils.ethersMod.ethers.Contract(
-                        sc.joAbiIMA.message_proxy_chain_address,
-                        sc.joAbiIMA.message_proxy_chain_abi,
+                        optsTransfer.imaState.chainProperties.sc
+                            .joAbiIMA.message_proxy_chain_address,
+                        optsTransfer.imaState.chainProperties
+                            .sc.joAbiIMA.message_proxy_chain_abi,
                         ethersProviderNode );
-                    const joMessageProxyNodeABI = sc.joAbiIMA.message_proxy_chain_abi;
                     const strEventName = "OutgoingMessage";
-                    const node_r = await imaEventLogScan.safeGetPastEventsProgressiveExternal(
+                    const node_r = await imaEventLogScan.safeGetPastEventsProgressive(
                         optsTransfer.details, optsTransfer.strLogPrefixShort, ethersProviderNode,
-                        10, joMessageProxyNode, joMessageProxyNodeABI, strEventName,
+                        10, joMessageProxyNode, strEventName,
                         joMessage.savedBlockNumberForOptimizations,
                         joMessage.savedBlockNumberForOptimizations,
                         joMessageProxyNode.filters[strEventName](
                             owaspUtils.ethersMod.ethers.utils.id( optsTransfer.chainNameDst ),
-                            owaspUtils.toBN( idxImaMessage ) ), optsTransfer.optsChainPair );
+                            owaspUtils.toBN( idxImaMessage )
+                        ),
+                        optsTransfer.optsChainPair
+                    );
                     const cntEvents = node_r.length;
                     if( log.verboseGet() >= log.verboseReversed().trace ) {
                         optsTransfer.details.write( optsTransfer.strLogPrefix + cc.debug( "Got " ) +
@@ -947,11 +943,11 @@ async function checkOutgoingMessageEvent( optsTransfer, joSChain ) {
                     for( let idxEvent = 0; idxEvent < cntEvents; ++ idxEvent ) {
                         const joEvent = node_r[idxEvent];
                         const eventValuesByName = {
-                            "dstChainHash": imaEventLogScan.extractEventArg( joEvent.args[0] ),
-                            "msgCounter": imaEventLogScan.extractEventArg( joEvent.args[1] ),
-                            "srcContract": imaEventLogScan.extractEventArg( joEvent.args[2] ),
-                            "dstContract": imaEventLogScan.extractEventArg( joEvent.args[3] ),
-                            "data": imaEventLogScan.extractEventArg( joEvent.args[4] )
+                            "dstChainHash": joEvent.args[0],
+                            "msgCounter": joEvent.args[1],
+                            "srcContract": joEvent.args[2],
+                            "dstContract": joEvent.args[3],
+                            "data": joEvent.args[4]
                         };
                         if( owaspUtils.ensureStartsWith0x(
                             joMessage.sender ).toLowerCase() ==
@@ -1263,26 +1259,23 @@ let gIsOneTransferInProgressInThisThread = false;
 
 export async function doTransfer(
     strDirection, joRuntimeOpts,
-    ethersProviderSrc, joMessageProxySrc, joMessageProxySrcABI, joAccountSrc,
-    ethersProviderDst, joMessageProxyDst, joMessageProxyDstABI, joAccountDst,
+    ethersProviderSrc, joMessageProxySrc, joAccountSrc,
+    ethersProviderDst, joMessageProxyDst, joAccountDst,
     chainNameSrc, chainNameDst, chainIdSrc, chainIdDst,
     joDepositBoxMainNet, // for logs validation on mainnet
     joTokenManagerSChain, // for logs validation on s-chain
     nTransactionsCountInBlock,
     nTransferSteps, nMaxTransactionsCount, nBlockAwaitDepth, nBlockAge,
-    fnSignMessages, joExtraSignOpts, transactionCustomizerDst, optsChainPair
+    fnSignMessages, joExtraSignOpts, transactionCustomizerDst
 ) {
     const optsTransfer = {
         strDirection: strDirection,
         joRuntimeOpts: joRuntimeOpts,
-        optsChainPair: optsChainPair,
         ethersProviderSrc: ethersProviderSrc,
         joMessageProxySrc: joMessageProxySrc,
-        joMessageProxySrcABI: joMessageProxySrcABI,
         joAccountSrc: joAccountSrc,
         ethersProviderDst: ethersProviderDst,
         joMessageProxyDst: joMessageProxyDst,
-        joMessageProxyDstABI: joMessageProxyDstABI,
         joAccountDst: joAccountDst,
         chainNameSrc: chainNameSrc,
         chainNameDst: chainNameDst,
@@ -1461,7 +1454,6 @@ export async function doAllS2S( // s-chain --> s-chain
     skaleObserver,
     ethersProviderDst,
     joMessageProxyDst,
-    joMessageProxyDstABI,
     joAccountDst,
     chainNameDst,
     chainIdDst,
@@ -1486,8 +1478,7 @@ export async function doAllS2S( // s-chain --> s-chain
     }
     for( let idxSChain = 0; idxSChain < cntSChains; ++ idxSChain ) {
         const joSChain = arrSChainsCached[idxSChain];
-        const joPickResult = skaleObserver.pickRandomSChainIndexAndNodeAndUrl( joSChain );
-        const urlSrc = joPickResult.strURL;
+        const urlSrc = skaleObserver.pickRandomSChainUrl( joSChain );
         const ethersProviderSrc = owaspUtils.getEthersProviderFromURL( urlSrc );
         const joAccountSrc = joAccountDst; // ???
         const chainNameSrc = "" + joSChain.data.name;
@@ -1516,8 +1507,6 @@ export async function doAllS2S( // s-chain --> s-chain
                             imaState.chainProperties.sc.joAbiIMA.message_proxy_chain_abi,
                             ethersProviderSrc
                         );
-                    const joMessageProxySrcABI =
-                        imaState.chainProperties.sc.joAbiIMA.message_proxy_chain_abi;
                     const joDepositBoxSrc =
                         new owaspUtils.ethersMod.ethers.Contract(
                             imaState.chainProperties.sc.joAbiIMA.message_proxy_chain_address,
@@ -1542,28 +1531,15 @@ export async function doAllS2S( // s-chain --> s-chain
                     imaState.loopState.s2s.isInProgress = true;
                     await pwa.notifyOnLoopStart( imaState, "s2s", nIndexS2S );
 
-                    const optsChainPair = {
-                        "strDirection": strDirection,
-                        "chainSrc": null,
-                        "chainDst": imaState.chainProperties.sc,
-                        "optsSpecificS2S": {
-                            "joSChain": joSChain,
-                            "idxSChain": idxSChain,
-                            "urlSrc": urlSrc,
-                            "idxNode": joPickResult.idxNode,
-                            "joNode": joPickResult.joNode
-                        }
-                    };
-                    bOK = await doTransfer(
+                    bOK =
+                    await doTransfer(
                         strDirection,
                         joRuntimeOpts,
                         ethersProviderSrc,
                         joMessageProxySrc,
-                        joMessageProxySrcABI,
                         joAccountSrc,
                         ethersProviderDst,
                         joMessageProxyDst,
-                        joMessageProxyDstABI,
                         joAccountDst,
                         chainNameSrc,
                         chainNameDst,
@@ -1578,8 +1554,7 @@ export async function doAllS2S( // s-chain --> s-chain
                         nBlockAge,
                         fnSignMessages,
                         joExtraSignOpts,
-                        transactionCustomizerDst,
-                        optsChainPair
+                        transactionCustomizerDst
                     );
                     imaState.loopState.s2s.isInProgress = false;
                     await pwa.notifyOnLoopEnd( imaState, "s2s", nIndexS2S );
diff --git a/npms/skale-observer/observer.mjs b/npms/skale-observer/observer.mjs
index c9146cf9c..47364448e 100644
--- a/npms/skale-observer/observer.mjs
+++ b/npms/skale-observer/observer.mjs
@@ -1333,7 +1333,7 @@ export async function ensureHaveWorker( opts ) {
     };
     while( ! gClient.logicalInitComplete ) {
         if( log.verboseGet() >= log.verboseReversed().info )
-            log.write( "SNB server is not initialized yet...\n" );
+            log.write( "SNB server is not inited yet...\n" );
 
         await threadInfo.sleep( 1000 );
         gClient.send( jo );
@@ -1513,19 +1513,6 @@ export function pickRandomSChainUrl( joSChain ) {
     return "" + joNode["http_endpoint_ip"];
 }
 
-export function pickRandomSChainIndexAndNodeAndUrl( joSChain ) {
-    const idxNode = pickRandomSChainNodeIndex( joSChain );
-    const joNode = joSChain.data.computed.nodes[idxNode];
-    // eslint-disable-next-line dot-notation
-    const strURL = "" + joNode["http_endpoint_ip"];
-    const joPickResult = {
-        "strURL": strURL,
-        "joNode": joNode,
-        "idxNode": idxNode
-    };
-    return joPickResult;
-}
-
 export async function discoverChainId( strURL ) {
     let ret = null;
     const rpcCallOpts = null;


Test patch:
diff --git a/agent/test/agentUnitTests.js b/agent/test/agentUnitTests.js
index d142e5487..00158e3bd 100644
--- a/agent/test/agentUnitTests.js
+++ b/agent/test/agentUnitTests.js
@@ -490,8 +490,6 @@ describe( "tests for `npms/skale-ima` 3", function() {
 
     it( "should return `false` invoke `doTransfer`", async function() {
         let joMessageProxySrc; // for `false` output
-        const joMessageProxySrcABI = null;
-        const joMessageProxyDstABI = null;
         const chainNameSrc = "test";
         const chainNameDst = "test";
         const nTransactionsCountInBlock = 4;
@@ -510,11 +508,9 @@ describe( "tests for `npms/skale-ima` 3", function() {
             joRuntimeOpts,
             ethersProviderSrc,
             joMessageProxySrc,
-            joMessageProxySrcABI,
             joAccountSrc,
             ethersProviderDst,
             joMessageProxyDst,
-            joMessageProxyDstABI,
             joAccountDst,
             chainNameSrc,
             chainNameDst,
@@ -546,19 +542,15 @@ describe( "tests for `npms/skale-ima` 3", function() {
             idxChainKnownForS2S: 0,
             cntChainsKnownForS2S: 0
         };
-        const joMessageProxySrcABI = null;
-        const joMessageProxyDstABI = null;
         // eslint-disable-next-line no-unused-expressions
         expect( await IMA.doTransfer(
             "M2S",
             joRuntimeOpts,
             ethersProviderSrc,
             joMessageProxySrc,
-            joMessageProxySrcABI,
             joAccountSrc,
             ethersProviderDst,
             joMessageProxyDst,
-            joMessageProxyDstABI,
             joAccountDst,
             chainNameSrc,
             chainNameDst,