# Ledger accounting migration guide (v1.34 LTS)

Ripple Custody version 1.34 migrates core balance tracking and transaction processing to a new event-driven accounting service. This guide describes the behavioral changes the new service introduces for API integrations and the one-time migration procedure for on-premise deployments.

The behavioral changes apply to all deployments. The migration procedure applies to on-premise deployments only; Ripple manages the migration for SaaS deployments.

## Behavioral changes

Review these changes and update your integrations before you migrate:

- **Use the `availableAmount` balance field.** The accounting service computes the available balance directly. The old formula, `totalAmount - reservedAmount - quarantinedAmount`, can produce incorrect results in some situations, such as an incoming transaction in the `Detected` state. Use the `availableAmount` field from [Get account balances](/pt-br/products/custody/v1.42/reference/api/openapi/accounts/getaccountbalances) instead.
- **Negative balances.** Starting in version 1.38, the `totalAmount` and `availableAmount` balance fields can hold values below zero, encoded as strings with a leading `-`. The `reservedAmount` and `quarantinedAmount` fields still accept only non-negative values. A negative balance represents an on-chain state of outstanding debt or liability, such as an XRPL Multi-Purpose Token (MPT) issuer's balance for its own asset. If your integration parses these fields as unsigned values, or validates that balances are non-negative, update it to accept negative values. For details, see [Ledger accounting refactor](/pt-br/products/custody/v1.42/support/change-history/v138#ledger-accounting-refactor) in the version 1.38 release notes.
- **Inbound processing status.** The processing status of inbound transactions is now tracked and populated, in addition to outbound transactions.
- **Concurrent processing.** Transactions are processed concurrently. There is no guarantee that transactions are processed in the order you submit them. If your integration depends on submission order, enforce the ordering on your side.
- **One sender per transfer.** The new accounting service supports one sender per transfer.
- **Outbound transaction processing pauses after 168 hours.** If an outbound transaction can't be funded, the accounting service stops attempting to reserve funds for it after 168 hours (7 days). The transaction then no longer progresses on its own — even if it reaches the chain — and no balance updates or transfers are created for it. To resume processing, contact Ripple Support; processing can be resumed for up to two months.
- **Replacement transactions are interrupted at finality.** When a transaction is [replaced](/pt-br/products/custody/v1.42/transactions/cancel-and-replace), its status changes to `Interrupted` only after the replacement transaction reaches finality. Previously, the status changed as soon as the replacement was detected, before finality.
- **Reserved balance tracking on UTXO chains.** On UTXO-based chains such as Bitcoin, the accounting service reserves the full value of the UTXOs the transaction will spend, which can exceed the transaction amount and fees. When the transaction is detected, the change amount — the reserved amount minus the transaction amount and fees — is released from the reservation. Previously, the entire reserved amount was released when the transaction reached the mempool.
- **Replaced outbound transactions deactivate their transfers.** When an outbound transaction is successfully replaced on-chain, its associated transfers are deactivated automatically. Previously, you had to handle those transfers manually by releasing quarantines.
- **Don't release quarantines for replaced inbound transactions.** When an inbound transaction is replaced, don't release the quarantines on the replaced transaction's transfers. Previous guidance was to release them so that they no longer counted toward the quarantined balance. The new accounting service might not allow those releases; contact Ripple Support to fail the replaced transaction instead.
- **Orders that spend quarantined funds can get stuck.** Previously, you could spend quarantined funds by submitting an order that doesn't clearly specify which funds it spends, such as a contract call order. After the migration, such a transaction still reaches the chain, but it can get stuck in the `Detected` state. Make sure your compliance checks skip or release quarantines before you submit such transactions.
- **Replacement transaction accounting.** When an outbound replacement transaction is detected — in the mempool for Bitcoin, or on-chain for other ledgers — the accounting service reverses all balance updates made by the replaced transaction before it registers the balance updates for the replacement transaction.


### Example: balance changes during a Bitcoin replacement

In this example scenario, Bitcoin transaction `T2` replaces `T1` with a higher fee, and both transactions over-reserve. The principal is 50, the fee on `T1` is 10, the fee on `T2` is 20, and each transaction reserves 100.

While a transaction is detected but not yet confirmed, the spent amount is deducted from `totalAmount` and is still counted in `reservedAmount`. As a result, `reservedAmount` can exceed `totalAmount`, and `availableAmount` is not `totalAmount` - `reservedAmount`. This is another reason to read `availableAmount` directly.

| Step | `totalAmount` | `reservedAmount` | `availableAmount` |
|  --- | --- | --- | --- |
| Initial state | 100 | 0 | 100 |
| `T1` reserved, over-reserving | 100 | 100 | 0 |
| `T1` detected: change of 40 released, 60 spent | 40 | 60 | 40 |
| `T2` reserved: no additional reservation required | 40 | 60 | 40 |
| `T2` detected: `T1` reversed, then `T2` reserved and detected | 30 | 70 | 30 |
| `T2` confirmed | 30 | 0 | 30 |


## Prerequisites

Before starting the migration, confirm that:

- Your release package includes the new accounting services and their Helm charts.
- You have a maintenance window: the migration requires you to stop accepting new requests until it completes.
- You have a current backup of your database.


## Migration procedure

Migrating to the new accounting flow is a one-time process:

1. Deploy the release with `HMZ_TEMPORAL_MIGRATION_ENABLED` and `HMZ_ACCOUNTING_SERVER_ENABLED` set to `false`, and deploy all the new services with Helm. Confirm the system operates normally before continuing — this isolates the accounting migration from the release's unrelated changes.
2. Make sure there are no in-flight outgoing transactions, and stop accepting new requests (such as new outgoing transactions or quarantine releases) until the migration completes.
3. Set `HMZ_TEMPORAL_MIGRATION_ENABLED` and `HMZ_ACCOUNTING_SERVER_ENABLED` to `true` and restart all services.
4. Wait for the balance migration to complete. It starts automatically the first time Ripple Custody runs with the new accounting flow enabled, and validates account balances as part of the same run — you don't need to trigger or validate it yourself. To follow its progress, monitor the `txnprocessor` pod logs for:

```text
Balance migration converged. Saving COMPLETED.
```
5. Resume normal operations. If account balances are found out of sync after migration, run a [force balance refresh](/pt-br/products/custody/v1.42/reference/api/openapi/accounts/forceupdateaccountbalances) to sync on-chain balances with the accounting service.


## Known issues and limitations

- **IBM s390x**: Ledger Accounting does not support IBM s390x architecture. This affects non-secure components only; the notary and vault continue to be supported on s390x architecture.
- **Staking balance sync**: During Substrate staking operations, balances can go out of sync; run a [force balance refresh](/pt-br/products/custody/v1.42/reference/api/openapi/accounts/forceupdateaccountbalances) after the operations complete. A fix follows the LTS release. Balances also go out of sync when staking rewards are paid out on Cardano and Ethereum — as in previous releases — so run force balance refresh after rewards payouts.
- **ERC20 transfer event accuracy**: The platform tracks token movements through ERC20 `Transfer` events. If a buggy or non-compliant contract emits `Transfer` events without moving balances, transaction history shows transfers that didn't move funds. This is an inherent blockchain limitation, unchanged from previous releases, and compliant contracts are not affected. After interacting with such a contract, run a [force balance refresh](/pt-br/products/custody/v1.42/reference/api/openapi/accounts/forceupdateaccountbalances) to sync balances.


## Related documentation

- [Ripple Custody 1.34 release notes](/pt-br/products/custody/v1.42/support/change-history/v134)
- [Get account balances](/pt-br/products/custody/v1.42/reference/api/openapi/accounts/getaccountbalances)
- [Transaction processing](/pt-br/products/custody/v1.42/transactions/processing)


## Support

For additional assistance with the migration, contact your Customer Partner Engineer (CPE) or Ripple Support.