How can I sign a transaction offline? Is there any documentation on signing txs offline? I would like to know the operation group id before submitting the tx. Thanks!

1 Answer 1


To sign a transaction, you must create the forged bytes of that transaction. Like this:

import { TezosToolkit, OpKind } from '@taquito/taquito';
import { InMemorySigner } from '@taquito/signer';
import { RpcClient, ForgeOperationsParams } from '@taquito/rpc';

const tezos = new TezosToolkit('<some RPC URL>');
const rpc = new RpcClient('<some RPC URL>');

const forgeOp = async (senderSK: string, source: string, destination: string, amount: number) => {

    const sourceData = await rpc.getContract(source);
    const counter = (Number(sourceData.counter) + 1).toString();
    const block = await rpc.getBlock()

    // setting the provider before estimating
    tezos.setProvider({ signer: new InMemorySigner(senderSK) });
    const estimations = await tezos.estimate.transfer({ to: destination, amount: amount });

    const opObj: ForgeOperationsParams = {
        "branch": block.hash,
        "contents": [
                "kind": OpKind.TRANSACTION,
                "fee": estimations.suggestedFeeMutez.toString(),
                "gas_limit": estimations.gasLimit.toString(),
                "storage_limit": estimations.storageLimit.toString(),
                "amount": amount.toString(),
                "destination": destination,
                "source": source,
                "counter": counter

    const forgedBytes = await rpc.forgeOperations(opObj)
    return forgedBytes;

Now that we have the transaction prepared in the form of a byte string. We can sign it after concatenating 0x03 in the front. The prefix is used (1) to indicate that the representation is hexadecimal (0x), and (2) that it should start with 03, which is a watermark for operations in Tezos.

const signOp = async (bytes: string, privateKey: string) => {
    const forgedBytes = "0x03" + bytes;
    const signer = new InMemorySigner(privateKey);
    const signature = await signer.sign(forgedBytes);
    return ({
        "signature": signature.prefixSig,
        "opbytes": signature.sbytes.slice(4)

The opbytes is your signed transaction. It's in the form of forged bytes + signature (without the watermark). Now, you can simply inject it into the blockchain like this:

const injectOp = async (opBytes: string) => {
    const opHash = await rpc.injectOperation(opBytes);
    return opHash;

Read more here.

Driver code:

const main = async () => {
    const txBytes = await forgeOp("<sender's secret key>", '<sender's address>', '<receiver;s address>', <amount>);
    console.log(console.log("Transaction Bytes: ", txBytes));

    const signedOp = await signOp(txBytes, "<sender's secret key>");
    console.log(console.log("Signed Operation: ", signedOp));

    const opHash = await injectOp(signedOp.opbytes);
    console.log(console.log("Operation Hash: ", opHash));


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.