How do I compute a transaction id for a transaction retrieved via the block API?

I am submitting a transaction using algosdk like this:

const { txId } = await algodClient.sendRawTransaction(signedTxns).do();

I’d like to be able to using the resulting transaction id to look up the transaction in the most recent blocks.

Here’s how I’m getting the blocks:

const block = await algodClient.block(roundNumber).do();

I can iterate transactions like this:

for (const txn of block.block.txns) {
    // How do I compute the transaction id for txn?
}

I need to compute the transaction id for txn to be able to match it against the expected transaction id.

This must be possible, algosdk and the indexer computes this somehow.

Anyone know how?

If your requirement is to get the confirmed transaction, then the ideal way is to wait until the transaction is confirmed and then fetch the transaction by Id.

async waitForConfirmation(txId: string): Promise<A_PendingTransactionResponse> {
const status = await this.client.status().do();
let lastRound = status[“last-round”];
while (true) {
const pendingInfo = await this.client.pendingTransactionInformation(txId).do();
if (pendingInfo[“confirmed-round”] !== null && pendingInfo[“confirmed-round”] > 0) {
return pendingInfo as A_PendingTransactionResponse;
}
lastRound++;
await this.client.statusAfterBlock(lastRound).do();
}
};

how to use it:

const { txId } = await algodClient.sendRawTransaction(signedTxns).do();
await waitForConfirmation(txId);
const {transactions} = await this.indexer.searchForTransactions().txid(txId).do();
return transactions[0];

Ashley,

To me the best way to do this is to marshal it into a Transaction object and call the normal method to get the txid.

I wrote a (somewhat tested) block fetcher that should do that here:

Ben

Thanks that’s useful, but I’d prefer to do it without using the indexer.

That’s what I have been searching for! Thanks so much, I’ll try it out today.

Hi Ben,

Where does SignedTransactionWithAD come from? I can’t find it.

Cheers
Ash

Its defined in the source file I linked.

The blocks contain a structure SignedTransactionInBlock

The STIB contains a SignedTransactionWithAD

The source file redefines it in typescript and attempts to marshal the block transactions into it.

Oh thanks! I swear I searched that file and couldn’t find it, but there it is!

Thanks for pointing it out.

@Ben thanks so much for your help.

I’ve boiled your code down to the simplest thing that works for us:

//
// Computes a transaction id from a transaction in a block.
//
function computeTransactionId(gh: Buffer, gen: string, stib: any) {
	const t = stib.txn as algosdk.EncodedTransaction;

	// Manually add gh/gen to construct a correct transaction object
	t.gh = gh;
	t.gen = gen;

	const stxn = {
		txn: algosdk.Transaction.from_obj_for_encoding(t),
	} as algosdk.SignedTransaction;

	if ("sig" in stib) {
		stxn.sig = stib.sig;
	}

	if ("lsig" in stib) {
		stxn.lsig = stib.lsig;
	}

	if ("msig" in stib) {
		stxn.msig = stib.msig;
	}

	if ("sgnr" in stib) {
		stxn.sgnr = stib.sgnr;
	}
  
	return stxn.txn.txID();
}

Used like this:

const confirmedRound = ...
const block = await algodClient.block(confirmedRound).do();        

for (const stxn of block.block.txns) {
	const txId = computeTransactionId(block.block.gh, block.block.gen, stxn);
	console.log(txId);
}
2 Likes