Skip to main content

Balance Bounds

It is easy to limit an account's balance using either flags.debits_must_not_exceed_credits or flags.credits_must_not_exceed_debits.

What if you want an account's balance to stay between an upper and a lower bound?

This is possible to check atomically using a set of linked transfers. (Note: with the must_not_exceed flag invariants, an account is guaranteed to never violate those invariants. This maximum balance approach must be enforced per-transfer -- it is possible to exceed the limit simply by not enforcing it for a particular transfer.)

Preconditions

  1. Target Account Should Have a Limited Balance

The account whose balance you want to bound should have one of these flags set:

  1. Create a Control Account with the Opposite Limit

There must also be a designated control account.

As you can see below, this account will never actually take control of the target account's funds, but we will set up simultaneous transfers in and out of the control account to apply the limit.

This account must have the opposite limit applied as the target account:

  1. Create an Operator Account

The operator account will be used to fund the Control Account.

Executing a Transfer with a Balance Bounds Check

This consists of 5 linked transfers.

We will refer to two amounts:

  • The limit amount is upper bound we want to maintain on the target account's balance.
  • The transfer amount is the amount we want to transfer if and only if the target account's balance after a successful transfer would be within the bounds.

If the Target Account Has a Credit Balance

In this case, we are keeping the Destination Account's balance between the bounds.

TransferDebit AccountCredit AccountAmountPending IDFlags (Note: \| sets multiple flags)
1SourceDestinationTransfer0flags.linked
2ControlOperatorLimit0flags.linked
3DestinationControlAMOUNT_MAX0flags.linked | flags.balancing_debit | flags.pending
40003*flags.linked | flags.void_pending_transfer
5OperatorControlLimit00

*This must be set to the transfer ID of the pending transfer (in this example, it is transfer 3).

If the Target Account Has a Debit Balance

In this case, we are keeping the Destination Account's balance between the bounds.

TransferDebit AccountCredit AccountAmountPending IDFlags (Note \| sets multiple flags)
1DestinationSourceTransfer0flags.linked
2OperatorControlLimit0flags.linked
3ControlDestinationAMOUNT_MAX0flags.balancing_credit | flags.pending | flags.linked
40003*flags.void_pending_transfer | flags.linked
5ControlOperatorLimit00

*This must be set to the transfer ID of the pending transfer (in this example, it is transfer 3).

Understanding the Mechanism

Each of the 5 transfers is linked so that all of them will succeed or all of them will fail.

The first transfer is the one we actually want to send.

The second transfer sets the Control Account's balance to the upper bound we want to impose.

The third transfer uses a balancing_debit or balancing_credit to transfer the Destination Account's net credit balance or net debit balance, respectively, to the Control Account. This transfer will fail if the first transfer would put the Destination Account's balance above the upper bound.

The third transfer is also a pending transfer, so it won't actually transfer the Destination Account's funds, even if it succeeds.

If everything to this point succeeds, the fourth and fifth transfers simply undo the effects of the second and third transfers. The fourth transfer voids the pending transfer. And the fifth transfer resets the Control Account's net balance to zero.