Script To Find Failed Swaps And Deposits

in #development9 hours ago (edited)

Due to Hive Engine outages, I had to research which transactions were not completed or refunded. Since I keep trading while waiting for the counterparty, and my code is a little bugged, there were many transactions to check.

I already wrote a simple page to research a transaction by its hash, but this time it was insufficient. I would have to do many searches with it.

So I wrote a NodeJS script for finding stuck deposits/withdrawals. It is rudimentary but it suits my needs.

Usage

To call the program:

node transactions-report.js your-account

You can specify a number of days (default 7):

node transactions-report.js your-account 30

Output

$ ./transactions-report.js arbitra 20
20 days
2 SWAP.HIVE transfers out, 15 transfers in
25 SWAP.HBD transfers out, 7 transfers in
155 native transfers out, 150 transfers in
182 transfers out
0771a2c4cd9c224ee8aea199a28ec2346ae1ae77  Sent: to hiveswap 0.669 HIVE  at 2025-10-11T23:11:45.000Z,  Received: 0.669 HIVE  in 0m
...
0d097478a72ccd10d0a0176b60785248f61cdff2  Sent: to hiveswap 881.074 HIVE  at 2025-10-11T13:52:45.000Z,  NOTHING RECEIVED
...
54183fbf7d439c3e4a0e1b88a9d6108381b569c2  Sent: to hiveswap 1054.775 HIVE  at 2025-09-29T21:16:48.000Z,  Received: 1050.0285125 SWAP.HIVE  in 0m
Sums: { hiveswap: { HIVE: 4167.956 }, 'honey-swap': { HIVE: 1977.421 } }

Installation

Save the Javascript code at http://droida.ch/hive/transactions-report.js in the directory of your choice.

Open a terminal (CTRL+ALT+T), and enter the directory:

cd /path/to/folder

The script runs with Node.js

Install libraries:

npm install https steem

Source Code

You are free to use, modify and distribute it. No attribution is required. There is no garantee.

#!/usr/bin/node

const swapServices=["sw4p", "hiveswap", "graphene-swap", "honey-swap"];

let days=7;
let account;

if (process.argv.length>2) {
    account=process.argv[2];
    if (process.argv.length>3) {
        days=parseFloat(process.argv[3]);
    }
} else {
    console.log("Usage: "+process.argv[0]+" "+process.argv[1]+" <ACCOUNT> [<DAYS>]");
    process.exit(1);
}

const https = require('https');

const chain=require("steem");
chain.api.setOptions({ url: 'https://api.hive.blog' });
chain.config.set('address_prefix','STM');
chain.config.set('chain_id','beeab0de00000000000000000000000000000000000000000000000000000000');
chain.config.set('alternative_api_endpoints', ['https://api.openhive.network', 'https://rpc.esteem.app']);
chain.config.set('rebranded_api', true);

const historyCount=100;
const engineHistoryCount=30;

const minFees=-.05;
const maxFees=.8;

const utcOffset=-new Date().getTimezoneOffset()/60;
const searchToTimestampMsUtc=(Date.now()+utcOffset*1000)-days*86400000;


main();


async function main() {
    console.log(days+" days");
    let swaps=[];

    let engineHiveTransfersOut=[];
    let engineHiveTransfersIn=[];
    const engineHiveTransfers=await requestEngineHistory("SWAP.HIVE", searchToTimestampMsUtc);
    for (let transfer of engineHiveTransfers) {
        if (transfer.from==account) {
            engineHiveTransfersOut.push(transfer);
            swaps.push(transfer);
        } else {
            engineHiveTransfersIn.push(transfer);
        }
    }
    console.log(engineHiveTransfersOut.length+" SWAP.HIVE transfers out, "+engineHiveTransfersIn.length+" transfers in");

    let engineHbdTransfersOut=[];
    let engineHbdTransfersIn=[];
    const engineHbdTransfers=await requestEngineHistory("SWAP.HBD", searchToTimestampMsUtc);
    for (let transfer of engineHbdTransfers) {
        if (transfer.from==account) {
            engineHbdTransfersOut.push(transfer);
            swaps.push(transfer);
        } else {
            engineHbdTransfersIn.push(transfer);
        }
    }
    console.log(engineHbdTransfersOut.length+" SWAP.HBD transfers out, "+engineHbdTransfersIn.length+" transfers in");

    const nativeTransfers=await requestHiveHistory(searchToTimestampMsUtc);
    let nativeTransfersOut=[];
    let nativeTransfersIn=[];
    for (let transfer of nativeTransfers) {
        if (transfer.from==account) {
            nativeTransfersOut.push(transfer);
            swaps.push(transfer);
        } else {
            nativeTransfersIn.push(transfer);
        }
    }
    console.log(nativeTransfersOut.length+" native transfers out, "+nativeTransfersIn.length+" transfers in");
    console.log(swaps.length+" transfers out");

    const handledTransfersIn=[];

    swap=swaps.sort(function(a, b) {
        return b.date-a.date;
    });

    for (let swap of swaps) {
        let id=swap.id;
        let to=swap.to;
        let quantity=swap.quantity;
        let symbol=swap.symbol;
        let memo=swap.memo;

        if (symbol=='HIVE' || symbol=='HBD') {
            // searching for a swap
            if (symbol=='HIVE') {
                for (let transfer of engineHiveTransfersIn) {
                    if (handledTransfersIn.indexOf(transfer.id)==-1 && transfer.from==to) {
                        let ratio=transfer.quantity/quantity;
                        let timeDiff=transfer.date-swap.date;
                        if (timeDiff>0 && ratio>=1-maxFees && ratio<=1-minFees) {
                            swap.receivedTransaction=transfer.id;
                            swap.receivedQuantity=transfer.quantity;
                            swap.receivedSymbol=transfer.symbol;
                            swap.receivedDate=transfer.date;
                            swap.receivedMemo=transfer.memo;
                            swap.delay=timeDiff;
                            handledTransfersIn.push(transfer.id);
                            break;
                        }
                    }
                }

            } else {
                for (let transfer of engineHbdTransfersIn) {
                    if (handledTransfersIn.indexOf(transfer.id)==-1 && transfer.from==to) {
                        let ratio=transfer.quantity/quantity;
                        let timeDiff=transfer.date-swap.date;
                        if (timeDiff>0 && ratio>=1-maxFees && ratio<=1-minFees) {
                            swap.receivedTransaction=transfer.id;
                            swap.receivedQuantity=transfer.quantity;
                            swap.receivedSymbol=transfer.symbol;
                            swap.receivedDate=transfer.date;
                            swap.receivedMemo=transfer.memo;
                            swap.delay=timeDiff;
                            handledTransfersIn.push(transfer.id);
                            break;
                        }
                    }
                }
            }

            // searching for a refund
            for (let transfer of nativeTransfersIn) {
                if (transfer.from==to) {
                    let timeDiff=transfer.date-swap.date;
                    if (handledTransfersIn.indexOf(transfer.id)==-1 && transfer.quantity==quantity && transfer.symbol==symbol && timeDiff>0) {
                        swap.receivedTransaction=transfer.id;
                        swap.receivedQuantity=transfer.quantity;
                        swap.receivedSymbol=transfer.symbol;
                        swap.receivedDate=transfer.date;
                        swap.receivedMemo=transfer.memo;
                        swap.delay=timeDiff;
                        handledTransfersIn.push(transfer.id);
                        break;
                    }
                }
            }

        } else if (symbol=='SWAP.HIVE' || symbol=='SWAP.HBD') {
            // searching for a swap
            for (let transfer of nativeTransfersIn) {
                if (transfer.from==to) {
                    let counterSymbol=symbol=='SWAP.HIVE' ? 'HIVE' : 'HBD';
                    let ratio=transfer.quantity/quantity;
                    let timeDiff=transfer.date-swap.date;
                    if (handledTransfersIn.indexOf(transfer.id)==-1 && ratio>=1-maxFees && ratio<=1-minFees && transfer.symbol==counterSymbol && timeDiff>0) {
                        swap.receivedTransaction=transfer.id;
                        swap.receivedQuantity=transfer.quantity;
                        swap.receivedSymbol=transfer.symbol;
                        swap.receivedDate=transfer.date;
                        swap.receivedMemo=transfer.memo;
                        swap.delay=timeDiff;
                        handledTransfersIn.push(transfer.id);
                        break;
                    }
                }
            }

            // searching for a refund
            if (symbol=='HIVE') {
                for (let transfer of engineHiveTransfersIn) {
                    if (handledTransfersIn.indexOf(transfer.id)==-1 && transfer.from==to) {
                        let timeDiff=transfer.date-swap.date;
                        if (timeDiff>0 && transfer.quantity==quantity) {
                            swap.receivedTransaction=transfer.id;
                            swap.receivedQuantity=transfer.quantity;
                            swap.receivedSymbol=transfer.symbol;
                            swap.receivedDate=transfer.date;
                            swap.receivedMemo=transfer.memo;
                            swap.delay=timeDiff;
                            handledTransfersIn.push(transfer.id);
                            break;
                        }
                    }
                }

            } else {
                for (let transfer of engineHbdTransfersIn) {
                    if (handledTransfersIn.indexOf(transfer.id)==-1 && transfer.from==to) {
                        let timeDiff=transfer.date-swap.date;
                        if (timeDiff>0 && transfer.quantity==quantity) {
                            swap.receivedTransaction=transfer.id;
                            swap.receivedQuantity=transfer.quantity;
                            swap.receivedSymbol=transfer.symbol;
                            swap.receivedDate=transfer.date;
                            swap.receivedMemo=transfer.memo;
                            swap.delay=timeDiff;
                            handledTransfersIn.push(transfer.id);
                            break;
                        }
                    }
                }
            }
        }
    }

    let sums;
    for (let swap of swaps) {
        let id=swap.id;
        let to=swap.to;
        let quantity=swap.quantity;
        let symbol=swap.symbol;
        let date=swap.date;

        if (swapServices.indexOf(to)>=0) {
            if (swap.receivedTransaction) {
                console.log(id+"  Sent: to "+to+" "+quantity+" "+symbol+"  at "+new Date(date).toISOString()+",  Received: "+swap.receivedQuantity+" "+swap.receivedSymbol+"  in "+(swap.delay/60000).toFixed(0)+"m");
            } else {
                console.log(id+"  Sent: to "+to+" "+quantity+" "+symbol+"  at "+new Date(date).toISOString()+",  NOTHING RECEIVED");

                if (!sums) {
                    sums={};
                }
                if (!sums[to] || sums[to].length==0) {
                    sums[to]={};
                }
                sums[to][symbol]=sums[to][symbol] ? sums[to][symbol]+quantity : quantity;
            }
        }
    }
    if (sums) {
        console.log("Missed swaps:", sums);
    }
}


async function requestEngineHistory(symbol, timeLimitUtc, offset=0) {
    return new Promise(async function(resolve) {
        let options = {
            hostname: "history.hive-engine.com",
            port: 443,
            path: "/accountHistory?account="+account+"&limit="+engineHistoryCount+"&offset="+offset+"&symbol="+symbol,
            method: 'GET',
        };

        let request=https.request(options, function(resp) {
            let data = '';
            
            resp.on('data', (chunk) => {
                data += chunk;
            });
            
            resp.on('end', async () => {
                let result=JSON.parse(data);
                let transfers=[];
                let timestampMsUtc;
                for (let i=0; i<result.length; i++) {
                    let timestampMs=result[i].timestamp*1000;
                    timestampMsUtc=timestampMs-utcOffset*3600000; // NOT UTC TIMESTAMPS !
                    if (timestampMsUtc>timeLimitUtc) {
                        if (result[i].operation=="tokens_transfer") {
                            let from=result[i].from;
                            let to=result[i].to;
                            let quantity=parseFloat(result[i].quantity);
                            let memo=result[i].memo;
                            if (timestampMsUtc>timeLimitUtc) {
                                transfers.push({
                                    id: result[i].transactionId,
                                    block: result[i].blockNumber,
                                    date: timestampMsUtc,
                                    from: from,
                                    to: to,
                                    quantity: quantity,
                                    symbol: result[i].symbol,
                                    memo: memo,
                                });
                            }
                        }
                    } else {
                        break;
                    }
                }

                if (timestampMsUtc>=timeLimitUtc) {
                    let nextItems=await requestEngineHistory(symbol, timeLimitUtc, offset+engineHistoryCount);
                    transfers.push(...nextItems);
                }

                resolve(transfers);
            });
            
        }).on("error", (err) => {
            console.log(err);
            resolve(false);
        });

        request.end();
    });
}


async function requestHiveHistory(timeLimitUtc, startNumber=-1) {
    return new Promise(async function(resolve) {
        let limit=historyCount;
        if (startNumber!=-1 && startNumber<=limit) {
            limit=startNumber;
        }
        chain.api.getAccountHistory(account, startNumber, limit, async function(err, result) {
            if (result && result.length>0) {
                let transfers=[];
                let dateUtc;
                for (let i=result.length-1; i>=0; i--) {
                    dateUtc=Date.parse(result[i][1].timestamp);
                    if (dateUtc>=timeLimitUtc) {
                        let block=result[i][1].block;
                        let id=result[i][1].trx_id;
                        let opType=result[i][1].op[0];
                        let opData=result[i][1].op[1];
                        if (opType=="transfer" && dateUtc>=timeLimitUtc) {
                            let from=opData.from;
                            let to=opData.to;
                            let quantity=parseFloat(opData.amount.split(" ")[0]);
                            let symbol=opData.amount.split(" ")[1];
                            let memo=opData.memo;
                            transfers.push({
                                id: id,
                                block: block,
                                date: dateUtc,
                                from: from,
                                to: to,
                                quantity: quantity,
                                symbol: symbol,
                                memo: memo,
                            });
                        }

                    } else {
                        break;
                    }
                }
                
                let number=0;
                if (result.length>0) {
                    number=result[0][0];
                }
                if (number>0 && dateUtc>=timeLimitUtc) {
                    let nextItems=await requestHiveHistory(timeLimitUtc, number-1);
                    transfers.push(...nextItems);
                }
                resolve(transfers);
                            
            } else {
                console.log(err);
                resolve(null);
            }
        });
    });
}
Sort:  

Congratulations @arbitra! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)

You distributed more than 10 upvotes.
Your next target is to reach 50 upvotes.
You received more than 10 upvotes.
Your next target is to reach 50 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP