4th Street Bar Hive-Bar

Hive-Bar Powered by Hive beta-fdb5b5b

Community post

Script To Find Failed Swaps And Deposits

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

https://files.peakd.com/file/peakd-hive/arbitra/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]+" <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=.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; 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);
	        }
	    });
	});
}
53 upvotes $0.45

Replies (5)

Review before signing

Posting as . Signing with . Keychain permission: Posting. Hive Keychain will ask you to approve this action next.


  
Technical details

Operation fingerprint: