Transaction Lifecycle
1. Construct Msg2. Sign Tx3. Broadcast4. Block Inclusion
Method A: CLI
The CLI handles message construction, signing, and broadcast in one command:
bash
govchaind tx <module> <command> [args...] \
--from <key-name> \
--chain-id govchain \
--keyring-backend test \
--gas auto --gas-adjustment 1.5 --yesMethod B: REST Broadcast
Use this when your app cannot reach the RPC port (26657) directly but can reach the REST API (1317), or when you want full control over the signing step.
Step 1: Create the signed transaction
Option A — CLI generate-only + sign offline
bash
# 1a. Generate the unsigned transaction JSON
govchaind tx procurement create-process \
--ocid ocds-abc123-ph-dpwh-2024-0001 \
--from alice \
--chain-id govchain --keyring-backend test \
--gas 200000 --fees 500stake \
--generate-only > unsigned_tx.json
# 1b. Sign the transaction (produces signed JSON)
govchaind tx sign unsigned_tx.json \
--from alice \
--chain-id govchain --keyring-backend test > signed_tx.json
# 1c. Encode to base64 for the REST broadcast endpoint
govchaind tx encode signed_tx.json > tx_bytes_b64.txt
# 1d. Broadcast
curl -X POST "$API/cosmos/tx/v1beta1/txs" \
-H "Content-Type: application/json" \
-d "{
\"tx_bytes\": \"$(cat tx_bytes_b64.txt)\",
\"mode\": \"BROADCAST_MODE_SYNC\"
}"Option B — CosmJS sign offline, broadcast via REST
typescript
import { SigningStargateClient, GasPrice } from "@cosmjs/stargate";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx";
// 1. Create wallet from mnemonic (offline -- no network needed)
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(MNEMONIC, {
prefix: "cosmos",
});
const [account] = await wallet.getAccounts();
// 2. Connect signing client (needs RPC only for account sequence)
const client = await SigningStargateClient.connectWithSigner(
"http://localhost:26657",
wallet,
{ gasPrice: GasPrice.fromString("0.025stake") }
);
// 3. Sign the transaction (does NOT broadcast yet)
const msg = {
typeUrl: "/govchain.procurement.v1.MsgCreateProcess",
value: { creator: account.address, ocid: "ocds-abc123-ph-dpwh-2024-0001" },
};
const fee = { amount: [{ denom: "stake", amount: "500" }], gas: "200000" };
const signedTx = await client.sign(account.address, [msg], fee, "");
// 4. Encode to bytes, then base64
const txBytes = TxRaw.encode(signedTx).finish();
const txBytesB64 = Buffer.from(txBytes).toString("base64");
// 5. Broadcast via REST (no RPC connection needed for this step)
const res = await fetch("http://localhost:1317/cosmos/tx/v1beta1/txs", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
tx_bytes: txBytesB64,
mode: "BROADCAST_MODE_SYNC",
}),
});
const result = await res.json();
if (result.tx_response.code !== 0) {
throw new Error(`Tx failed: ${result.tx_response.raw_log}`);
}
console.log("Tx Hash:", result.tx_response.txhash);Tip: If you cannot reach RPC at all, fetch the account number and sequence from /cosmos/auth/v1beta1/accounts/{address} via REST, then use wallet.signDirect() with a manually constructed SignDoc.
Step 2: Broadcast
bash
curl -X POST "$API/cosmos/tx/v1beta1/txs" \
-H "Content-Type: application/json" \
-d '{
"tx_bytes": "<base64-encoded-signed-tx>",
"mode": "BROADCAST_MODE_SYNC"
}'| Mode | Behavior |
|---|---|
| BROADCAST_MODE_SYNC | Waits for CheckTx validation, returns immediately |
| BROADCAST_MODE_ASYNC | Returns immediately without any check |
| BROADCAST_MODE_BLOCK | Waits for block inclusion (confirms success) |
Method C: CosmJS signAndBroadcast
typescript
import { SigningStargateClient, GasPrice } from "@cosmjs/stargate";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(MNEMONIC, {
prefix: "cosmos",
});
const [account] = await wallet.getAccounts();
const client = await SigningStargateClient.connectWithSigner(
"http://localhost:26657",
wallet,
{ gasPrice: GasPrice.fromString("0.025stake") }
);
const result = await client.signAndBroadcast(
account.address,
[msg], // array of messages
"auto", // auto gas estimation
"memo" // optional memo
);
console.log("Tx Hash:", result.transactionHash);
console.log("Height:", result.height);