Unified Balance Kit: Designing for Pending and Funds-in-Motion States
.jpg)
Summary
Part two of the Unified Balance Kit series shows how to separate ready USDC from pending and in-motion funds so payment and treasury apps do not treat every visible balance as spendable.
In the lead post of this series, the main architectural shift was from source-chain-first design to available-USDC-first design. That model only works if the app stays precise about state.
Available USDC is not one undifferentiated number. Some funds are ready to spend now, while others are still finalizing or already committed to a transfer flow. If an app collapses those states into one balance concept, the result is harder to use in both the product and the operations layer.
Unified Balance Kit gives apps the tools to model those states explicitly, but your application still has to decide what each one means.
Modeling Confirmed and Pending Balance
One useful pattern when deposit finality timing matters is to query with includePending: true and explicitly separate confirmed spendable balance from pending inbound balance.
const balances = await kit.getBalances({
sources: [{ adapter: evmAdapter }],
networkType: "testnet",
includePending: true,
})
// totalConfirmedBalance is spendable now
// totalPendingBalance reflects deposits still finalizing onchain
const confirmedBalance = balances.totalConfirmedBalance
const pendingBalance = balances.totalPendingBalance ?? "0"
Separating those states affects spend readiness, balance reporting, and support visibility.
For most apps, it helps to separate ledger state from the application’s action state:
- confirmed: final and recognized by the system
- pending: observed but not yet finalized into confirmed balance
- funds in motion: already committed to a transfer or payout flow
- spendable: confirmed and currently usable for the requested action
Confirmed describes whether the balance is final. Spendable describes whether that confirmed balance is usable for a specific action right now. A transfer can also be validly underway and should no longer be shown as available inventory. The product model needs to keep those states separate.
For example, an application might derive its own UI or execution state from the balance response like this:
const confirmedBalance = balances.totalConfirmedBalance
const pendingBalance = balances.totalPendingBalance ?? "0"
const hasConfirmedBalance = confirmedBalance !== "0"
const hasPendingBalance = pendingBalance !== "0"
const state =
hasConfirmedBalance ? "spendable" :
hasPendingBalance ? "pending" :
"unavailable"
The useful part is the separation: confirmed balance, pending balance, and execution state do not have to collapse into one number or one status label.
Because the SDK returns decimal strings, the arithmetic layer matters too. The example scripts in this repo use parseFloat for simple threshold checks, which is fine for throwaway scripting. For production balance arithmetic, a decimal-safe representation is a better fit for the state precision this post is arguing for.
Confirmed balance is not the same as visible balance
Unified Balance Kit returns decimal-string balance values, and one common mistake is to reduce them into one total for display.
Confirmed balance is the portion the app can treat as ready now. Pending balance is operationally important, but it is not the same thing. It belongs in the experience as status, expectation setting, and support context, not as immediately spendable inventory.
For payment and payout apps, that usually means the payout draws only from confirmed balance while pending funds are shown as incoming value, not spendable value. For treasury apps, pending funds may still matter as part of the capital picture, but they are better kept separate from ready inventory for immediate execution.
Funds in Motion as a Separate State
Pending balance is about funds that are still becoming available. Funds in motion refers to funds that were spendable, then became part of a transfer flow that is still in progress. The app needs different messaging and status handling for each case.
When funds are pending, the main concern is readiness: the balance is still finalizing, and the action may need to wait before execution can begin. When funds are in motion, the main concern is progress: execution has already started, and the balance should no longer look available for a new action.
If both of those states are labeled as “processing,” users and operators lose the ability to reason about what is actually happening.
Deposit timing changes the product experience
This is also where chain timing matters, even though the product should not center chain selection as the first user decision.
Deposit finality is not uniform across chains. Some deposits settle quickly. Others can take much longer before they move from pending to confirmed. Arc can be one example of a faster-deposit source in the flow, while Ethereum, Base, Arbitrum, and OP-family routes can take much longer before funds become confirmed and spendable.
The product implication is straightforward: “available USDC” can still become available on different schedules depending on where the funds entered the system. The app does not need to lead with that chain detail, but it does need to absorb that timing difference into its readiness and messaging model.
UX patterns for pending and in-motion funds
The available-USDC model works best when the state is visible without becoming noisy. In practice, that often means:
- a primary spendable balance that reflects confirmed usable value
- a separate pending indicator that explains incoming value still finalizing
- a transfer status layer for actions already underway
- route-level messaging when a requested action is blocked by readiness rather than by total balance
The key is to avoid presenting a larger aggregate number that silently mixes spendable and non-spendable value. If the user has 100 USDC confirmed and 20 USDC pending, the app should not imply that 120 USDC is immediately ready for a payout flow that requires confirmed balance now.
const balances = await kit.getBalances({
sources: [{ adapter: evmAdapter }],
networkType: "testnet",
includePending: true,
})
const confirmed = Number(balances.totalConfirmedBalance)
const pending = Number(balances.totalPendingBalance ?? "0")
const payoutAmount = 120
const uiModel = {
spendableBalance: confirmed,
pendingBalance: pending,
totalVisibleBalance: confirmed + pending,
payoutReady: confirmed >= payoutAmount,
}
Operations and support implications
This state model is just as useful for internal tools as it is for the user-facing interface, because it helps support and operations teams diagnose issues faster.
Support teams still need to distinguish between “your funds have not finalized yet,” “your transfer has already started,” and “your balance is sufficient in total, but not spendable yet for this action.”
Operations tooling benefits from the same separation. Instead of one generic stuck-transfer bucket, the team can isolate whether the issue is deposit readiness, execution progress, or route recovery.
That is one of the practical benefits of using Unified Balance Kit at the app layer. The kit gives you a higher-level balance model, but the app still decides how clearly those states are represented.
Next: Routing and Fallback
The first post in this series introduced the shift from source-chain-first design to designing around available USDC. From there, the next challenge is what to do when confirmed balance exists but route conditions are imperfect. That is where partial liquidity, routing, and fallback behavior come in.
The next post in this series focuses on those patterns directly.
USDC is issued by regulated affiliates of Circle. See Circle’s list of regulatory authorizations (https://circle.com/legal/licenses).
Arc testnet is offered by Circle Technology Services, LLC ("CTS"). CTS is a software provider and does not provide regulated financial or advisory services. You are solely responsible for services you provide to users, including obtaining any necessary licenses or approvals and otherwise complying with applicable laws.
Arc has not been reviewed or approved by the New York State Department of Financial Services.
The product features described in these materials are for informational purposes only. All product features may be modified, delayed, or cancelled without prior notice, at any time and at the sole discretion of Circle Technology Services, LLC. Nothing herein constitutes a commitment, warranty, guarantee or investment advice.
