Script To Find Failed Swaps And Deposits

@arbitra · 2025-10-19 20:54 · development

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 } }

23t7AyTgz3zne2HTtfxpgqtGgvJr1sKaRvQs2ieCBX4dsQKnefiswLpbvyZWato173uvw.png

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]+"  []");
    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=.08;

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; itimeLimitUtc) {
                        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);
            }
        });
    });
}
#development #programming #dev #swap #hive-engine #leofinance #neoxian #javascript #js #stem
Payout: 0.000 HBD
Votes: 53
More interactions (upvote, reblog, reply) coming soon.