Is it possible to get the transaction hash before the transaction is sent using Taquito? If yes, could you suggest how?
Question from KStasi dev Slack.
Tezos Stack Exchange is a question and answer site for users, experts, and developers of the Tezos blockchain project. It only takes a minute to sign up.
Sign up to join this communityDo you mean the operation hash? If yes, I assume you mean if it’s possible to get an op hash without actually injecting the operation? The op hash is returned as part of the response object when an operation gets injected, so it’s not possible currently.
But however, we found some indirect workaround:
import {
Estimate,
ForgedBytes,
Forger,
InvalidEstimateValueError,
PreparedOperation,
TaquitoLocalForger,
TezosToolkit,
TransferParams,
} from '@taquito/taquito';
import {
encodeOpHash,
InvalidAddressError,
invalidDetail,
validateAddress,
ValidationResult,
} from '@taquito/utils';
import { InvalidAmountError } from '@taquito/core';
const MagicByte = new Uint8Array([3]);
export class TransferHelper {
forger: Forger;
constructor(private readonly tezos: TezosToolkit) {
this.forger = tezos.getFactory(TaquitoLocalForger)();
}
async getTransferHash(params: TransferParams) {
const toValidation = validateAddress(params.to);
if (toValidation !== ValidationResult.VALID) {
throw new InvalidAddressError(params.to, invalidDetail(toValidation));
}
const sourceValidation = validateAddress(params.source ?? '');
if (params.source && sourceValidation !== ValidationResult.VALID) {
throw new InvalidAddressError(
params.source,
invalidDetail(sourceValidation),
);
}
if (params.amount < 0) {
throw new InvalidAmountError(params.amount.toString());
}
const estimate = await this.estimate(
params,
this.tezos.estimate.transfer.bind(this.tezos.estimate),
);
const prepared = await this.tezos.prepare.transaction({
...params,
...estimate,
});
const forgedBytesOpBytes = await this.forge(prepared);
const signedOpBytes = await this.tezos.signer.sign(
forgedBytesOpBytes,
MagicByte,
);
return encodeOpHash(signedOpBytes.sbytes);
}
private async forge({
opOb: { branch, contents },
}: PreparedOperation): Promise<ForgedBytes['opbytes']> {
return await this.forger.forge({ branch, contents });
}
private async estimate<
T extends { fee?: number; gasLimit?: number; storageLimit?: number },
>(
{ fee, gasLimit, storageLimit, ...rest }: T,
estimator: (param: T) => Promise<Estimate>,
) {
let calculatedFee = fee;
let calculatedGas = gasLimit;
let calculatedStorage = storageLimit;
if (calculatedFee && calculatedFee % 1 !== 0) {
throw new InvalidEstimateValueError(
`Fee value must not be a decimal: ${calculatedFee}`,
);
}
if (calculatedGas && calculatedGas % 1 !== 0) {
throw new InvalidEstimateValueError(
`Gas Limit value must not be a decimal: ${calculatedGas}`,
);
}
if (calculatedStorage && calculatedStorage % 1 !== 0) {
throw new InvalidEstimateValueError(
`Storage Limit value must not be a decimal: ${calculatedStorage}`,
);
}
if (
fee === undefined ||
gasLimit === undefined ||
storageLimit === undefined
) {
const estimation = await estimator({
fee,
gasLimit,
storageLimit,
...(rest as any),
});
calculatedFee ??= estimation.suggestedFeeMutez;
calculatedGas ??= estimation.gasLimit;
calculatedStorage ??= estimation.storageLimit;
}
return {
fee: calculatedFee,
gasLimit: calculatedGas,
storageLimit: calculatedStorage,
};
}
}
Response from Davis Sawali & KStasi.