Build your own client

This guide provides recommendations to ensure Universal Ledger client implementations interact with a network securely and reliably. Learn how to:

  • Leverage the network topology for high availability.
  • Submit transactions and understand execution outcomes.
  • Implement polling, timeouts, and retry strategies.

Network topology and high availability

A Universal Ledger network comprises of a number of validators distributed amongst multiple zones and regions, thus ensuring a high availability for the network as a whole.

  • Zone failures: If a single validator fails within a Cloud zone, other validators within the same region will seamlessly continue to process requests.

  • Regional outages: If an entire region becomes unavailable, validators in other regions remain operational. To continue submitting transactions, it is safe for your client to fall back and attempt to resubmit transactions through a different endpoint.

Transaction execution characteristics

  • Atomic execution: Transactions either succeed entirely or fail entirely. Partial transaction execution—such as transferring only a portion of the requested funds because of an insufficient balance—never occurs. This includes transaction chains, allowing you to combine multiple arbitrary transactions into a single atomic sequence.

  • No automatic retries: The network attempts to execute a submitted transaction exactly once. If execution fails, the ledger does not queue or retry the transaction automatically.

  • Idempotent resubmission: Every submitted transaction generates a unique transaction ID based on its exact serialized payload (including sequence number and sender). Submitting the exact same payload multiple times produces the same transaction ID. The ledger ensures a transaction is executed at most once, making it safe to resubmit the same transaction payload to a different endpoint if one of them appears unavailable.

    For more details, see the SubmitTransactionRequest reference page.

Latency, polling, and retry strategy

When you submit a transaction, the validator will first perform a quick check ensuring its signatures and sequence numbers are valid. If successful, the Universal Ledger API will immediately respond with a SubmitTransactionResponse including its assigned transaction ID.

Most transactions are finalized within 3 seconds. To check the transaction status, poll the QueryTransactionState method using the provided transaction ID.

  • If the transaction status is still PENDING, retrieve the status again using an exponential backoff strategy. For example, query at intervals of 3 seconds, 9 seconds, 27 seconds, and 60 seconds.

  • If QueryTransactionState returns NOT_FOUND, resubmit the transaction.

Once a transaction attempt is FINALIZED, the response will also include a TransactionCertificate with various details such as:

  • The round ID at which the transaction was finalized.
  • The execution status (OK or failed).
  • Transaction events, including any transaction outputs.

Timeout protocol

In the unlikely event that a transaction remains in a PENDING or NOT_FOUND state after 60 seconds, assume that the validator serving your requests is failing to catch up with other validators in the network. Complete the following steps:

  1. Select a different endpoint in a different network region.
  2. Resubmit the exact same signed transaction payload.
  3. Report the issue using the 'Send feedback' button at the top of this page for the Universal Ledger team to investigate.

When checking transaction status, the response might include multiple TransactionAttempt messages if you submitted the same payload multiple times. At most one attempt will be finalized with an OK transaction status, found in the TransactionEffects, whereas other attempts will either be pending or have a failed status. This is expected behavior; the ledger successfully executed one of the transaction submissions and correctly rejected, or will eventually reject, the duplicated resubmissions.

Network consistency

  • Finalized transactions: Once a transaction is reported as finalized by one validator, all other validators on the same network are expected to eventually process the transaction and reproduce exactly the same outcomes. Submitting a QueryAccountRequest using the round ID at which the transaction was finalized, as it can be found in its TransactionCertificate, yields identical results on all synchronized validators.

  • Non-finalized transactions: Because transactions propagate across the network asynchronously, whether a transaction is reported as finalized can vary across different validators.

    A validator recovering from an outage or experiencing synchronization lag might temporarily report a finalized transaction as PENDING or NOT_FOUND. The transaction status will resolve once the validator catches up.

Client responsibilities and error handling

  • Transaction tracking: Client applications must track the state of all submitted transactions until they are finalized.

  • Error handling: If a transaction fails, retrieve and inspect the failure details included in the TransactionCertificate to trigger the appropriate business-level remediation.

What's next