On Sui, a transaction is identified by a base58 digest, and it becomes final only when it is committed inside a certified checkpoint identified by a monotonically increasing sequence number. The sui_getTransactionBlock method takes that digest plus options such as showEffects and showObjectChanges, and returns the transaction block together with a checkpoint field and an inclusionProof. You can then use the returned checkpoint sequence number to read the checkpoint and confirm the digest appears in its transactions. This article separates documented protocol behavior from provider-specific behavior, gives runnable Node.js examples for both the success and not-found paths, and provides a results table you fill against your own endpoint.
Sui Transaction Identity: Digest Versus Checkpoint Sequence
Sui separates two identifiers that are easy to conflate. A transaction is identified by a base58 digest, which explorers often label a "Tx hash"; this is the value you paste into a lookup box. A checkpoint is identified by a monotonically increasing sequence number, and it is the container in which one or more transactions are committed. The digest answers "which transaction", while the checkpoint sequence answers "where and when it landed".
The Sui checkpoint concepts documentation describes checkpoints as the unit of commitment: transactions are ordered, batched, and certified, and certification is what makes inclusion durable. That is why "confirmed" on Sui should be read as "included in a checkpoint that has been certified", not merely "the node accepted my submission". Provisional execution can happen before certification, and a transaction that has executed but is not yet in a certified checkpoint is not yet final in the same sense.
This distinction drives the whole resolution workflow. You resolve by digest, read the checkpoint field from the response, then reconcile that sequence number against the checkpoint itself. For a broader orientation to the network and its endpoints, see the Sui network page.
- Digest: base58 transaction identifier, the lookup key for sui_getTransactionBlock.
- Checkpoint sequence: monotonically increasing integer identifying the committed container.
- Certification: the property that makes checkpoint inclusion durable, per the Sui checkpoint concepts documentation.
- Provisional execution: execution that may precede certified inclusion and should not be treated as final.
What sui_getTransactionBlock Accepts and Returns
The Sui JSON-RPC API reference documents sui_getTransactionBlock as taking a digest plus an options object. The options control which parts of the transaction block are populated: showEffects, showInput, showEvents, showObjectChanges, and showBalanceChanges. Fields that correspond to an option are present only when that option is set, so the response shape is partly a function of your request.
The response includes the digest, a checkpoint field, timestampMs, and the transaction body, plus effects, events, and objectChanges when requested. The checkpoint field carries the sequence number of the checkpoint that committed the transaction, and the response also carries an inclusionProof whose digest is the transaction digest. Treat the checkpoint field as the bridge to the checkpoint read path.
Because options change the payload, they also change the response size and the work the node does to assemble it. Requesting everything on every call is convenient but heavier; request only the options you actually consume. The transaction effects and object changes article covers interpreting effects and object changes once you have them.
- showEffects: populates effects, including status and gas used.
- showInput: populates the transaction input, including sender and gas data.
- showEvents: populates emitted events.
- showObjectChanges: populates created, mutated, and deleted object summaries.
- showBalanceChanges: populates balance deltas per address and coin type.
Resolving a Digest with a Runnable Node.js Example
The example below uses the @mysten/sui client to resolve a digest with effects and object changes, then prints the checkpoint sequence number and the inclusionProof digest. It is deliberately small so you can paste it into a scratch file and point it at the endpoint you use. The client library is a convenience wrapper; the underlying call is the same JSON-RPC method documented in the Sui API reference.
Run it with a real digest from an explorer or from your own submission. If you prefer raw JSON-RPC, the same request can be sent over fetch with a JSON-RPC 2.0 envelope, which the JSON-RPC 2.0 specification defines as a jsonrpc version field, a method, params, and an id.
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
const client = new SuiClient({ url: getFullnodeUrl('mainnet') });
async function resolve(digest) {
const tx = await client.getTransactionBlock({
digest,
options: {
showEffects: true,
showInput: true,
showObjectChanges: true,
},
});
console.log('digest :', tx.digest);
console.log('checkpoint :', tx.checkpoint);
console.log('timestampMs :', tx.timestampMs);
console.log('status :', tx.effects?.status?.status);
console.log('inclusionProof:', tx.inclusionProof?.digest);
console.log('objectChanges :', tx.objectChanges?.length ?? 0);
return tx;
}
resolve(process.argv[2]).catch((err) => {
console.error('resolve failed:', err.message);
process.exitCode = 1;
});Reconciling the Digest Against Its Checkpoint
Once you have the checkpoint sequence number, read the checkpoint and confirm the transaction digest appears in its transactions list. This is the reconciliation step: it converts "the node told me a checkpoint number" into "I observed the digest inside that checkpoint". The Sui checkpoint concepts documentation explains that checkpoints are the commitment unit, so this check is the meaningful inclusion test.
The legacy sui_getCheckpoint method is documented as deprecated in favour of the checkpoint read path, so verify the current method name against the Sui docs before you hard-code it. Provider support for any given method is documented / varies by provider, so confirm availability on the endpoint you use rather than assuming parity across all nodes.
The example below reads the checkpoint and checks membership. It uses the same client and assumes you already have a checkpoint sequence number from the previous step.
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
const client = new SuiClient({ url: getFullnodeUrl('mainnet') });
async function reconcile(digest, checkpointSeq) {
const cp = await client.getCheckpoint({ id: String(checkpointSeq) });
const digests = (cp.transactions ?? []).map((t) =>
typeof t === 'string' ? t : t.digest,
);
const included = digests.includes(digest);
console.log('checkpoint seq :', cp.sequenceNumber);
console.log('tx count :', digests.length);
console.log('included :', included);
return included;
}
reconcile(process.argv[2], process.argv[3]).catch((err) => {
console.error('reconcile failed:', err.message);
process.exitCode = 1;
});Digest Resolution Versus Checkpoint and Cursor Enumeration
Resolving by digest and enumerating by checkpoint or cursor answer different questions. sui_getTransactionBlock is a point lookup: you already know the digest and want its details and its checkpoint. suix_queryTransactionBlocks is an enumeration: you want a page of transactions filtered by criteria, walked forward with a cursor. Choosing the wrong one leads to awkward code, such as paging through thousands of transactions to find one you already have the digest for.
Use digest resolution when you have a specific transaction to inspect, when you are reconciling a user-reported hash, or when you are confirming inclusion for a single submission. Use checkpoint reads when you want everything committed in a known container. Use cursor enumeration when you are building a feed, backfilling history, or scanning by filter. The queryTransactionBlocks cursor pagination article covers the enumeration path in detail, and the checkpoint stream and ledger service article covers consuming checkpoints as a stream.
- Point lookup by digest: sui_getTransactionBlock, best for a known transaction.
- Container read: checkpoint read path, best for everything in a known checkpoint.
- Filtered enumeration: suix_queryTransactionBlocks with a cursor, best for feeds and backfills.
- Streaming: checkpoint stream, best for continuous ingestion.
Polling a Just-Submitted Transaction with a Bound
A just-submitted transaction may return not-found until a checkpoint includes it. This is expected, not necessarily an error: the digest exists from the moment the transaction is submitted, but the node may not yet be able to resolve it into a committed transaction block. The correct pattern is to poll by digest with a bound, backing off between attempts and giving up after a deadline rather than looping forever.
Bound the poll with a maximum attempt count and a delay, and treat a persistent not-found as a signal to check the digest string, the network, and the endpoint. If the transaction was submitted to a different network than the one you are querying, it will never resolve on the wrong network. The Sui RPC guide is a useful companion for endpoint selection and method availability.
- Poll with a fixed delay and a maximum attempt count.
- Treat not-found as provisional until the deadline, then investigate.
- Confirm the digest is the full base58 value, not a truncated explorer label.
- Confirm the network and endpoint match the submission target.
Handling the Not-Found Case for an Unknown Digest
A made-up digest should produce a not-found error rather than a transaction block. This is the negative test that proves your resolution code distinguishes a real transaction from a fabricated one. The JSON-RPC 2.0 specification defines error semantics for method failures, so a well-formed request for an unknown digest returns an error object rather than a success payload.
The example below wraps the resolve call and prints a clear message for the not-found path. Run it with a deliberately invalid digest to confirm your error handling behaves as expected before you rely on it in production.
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
const client = new SuiClient({ url: getFullnodeUrl('mainnet') });
async function tryResolve(digest) {
try {
const tx = await client.getTransactionBlock({
digest,
options: { showEffects: true },
});
console.log('resolved:', tx.digest, 'checkpoint:', tx.checkpoint);
return tx;
} catch (err) {
console.log('not found or error for', digest);
console.log('message:', err.message);
return null;
}
}
// A fabricated digest should not resolve.
tryResolve('0x' + '0'.repeat(64));Results Table for Verified-by-Reader Measurement
The table below is a template, not a set of published numbers. Fill it in against your own endpoint and your own digests so the values reflect the environment you actually operate. Record the digest, the checkpoint sequence, the timestampMs, whether the digest appeared in the checkpoint transactions, and the final status.
Because provider behavior is documented / varies by provider, your table may differ from someone else's on a different endpoint. That is the point: the table makes the variation visible and reproducible rather than asserted. Keep the raw responses alongside the table so you can re-check any row later.
- digest: the full base58 transaction digest you resolved.
- checkpoint seq: the checkpoint field returned by sui_getTransactionBlock.
- timestampMs: the timestampMs returned with the transaction block.
- included-in-checkpoint: yes or no, from the reconciliation step.
- status: the effects status, such as success or failure.
- endpoint: the RPC URL you queried, so rows are attributable.
Limitations, Tradeoffs, and Provider Variation
Several honest limitations apply. The digest must be the full base58 value; a truncated or mis-copied digest will not resolve. Options change the payload size and the cost of assembling the response, so requesting every option on every call is a tradeoff between convenience and overhead. The checkpoint field can be null for a transaction that is not yet certified, which is why the reconciliation step matters rather than trusting the field alone.
Method availability is documented / varies by provider. The legacy sui_getCheckpoint method is documented as deprecated in favour of the checkpoint read path, so verify the current method name against the Sui docs before depending on it. None of this is a substitute for reading the primary sources: the Sui JSON-RPC API reference for method semantics and the Sui checkpoint concepts documentation for commitment semantics.
For object-level reasoning that often accompanies transaction resolution, such as versioning and ordering, see the object versions and Lamport ordering article.
- Full base58 digest required; truncated values fail.
- Options increase payload size and assembly cost.
- checkpoint can be null before certification.
- Deprecated methods may be removed; verify current names.
- Provider support varies; confirm on your endpoint.
Troubleshooting Digest, Checkpoint, and Method Errors
Digest not found is the most common symptom. Check that the digest is the full base58 value, that you are querying the network where the transaction was submitted, and that enough time has passed for a checkpoint to include it. If it persists past your polling deadline, treat it as a real failure to resolve rather than a timing artifact.
A null checkpoint means the transaction is not yet certified in a checkpoint. Do not treat a null checkpoint as a successful final resolution; poll again with a bound, or reconcile later. A wrong method name, especially around the deprecated checkpoint method, produces a method-not-found error; verify the current name against the Sui docs and confirm your provider supports it.
For endpoint-level questions and method availability, the Sui RPC guide and the API service pages are the right starting points.
- Digest not found: verify full base58 value, network, and elapsed time.
- Null checkpoint: not yet certified; poll with a bound or reconcile later.
- Wrong method name: verify against Sui docs; confirm provider support.
- Unexpected payload: check which options you set.
- Persistent failure: capture the raw JSON-RPC error object for support.