Fixed fee & un-splittable cents

Last updated: May 10, 2024

A splitted transaction is created, starting with a fixed amount of $0,07 to @payment_provider followed by a percentage to the same @payment_provider, then a percentage to @franchise_fee and remaining to @store.

send $value (
  source = @world
  destination = {
    7/1999 to @payment_provider
    0.6% to @payment_provider
    0.5% to @franchise_fee
    remaining to @store
  }

Problem: in the end, a fixed fee amount of 8 cents is sent instead of 7 expected

it’s essentially due to two un-splittable cents, and the multi-pass resolution operated by the send statement. This mechanism is made to avoid introducing or losing cents in allocations (it’s essentially an implementation of the monetary allocation pattern).

First pass:

  1. $7/1999 * 1999 = 7$ are allocated to @payment_provider

  2. $0.6/100 * 1999 = 11.994 = 11$ are allocated to @payment_provider (the machine recognizes that 0.994 do not make for a full AUD/2 and cannot allocate it to the account here — it keeps it on the side for a second pass)

  3. $0.5/100 * 1999 = 9.995 = 9$ are allocated to @franchise_fee (same mechanism)

  4. $1999 - 7 - 11 - 9 - ceil(0.994+0.995) = 1970$ (the machine doesn’t attribute the amounts fragments from above at this stage — it removes them from the “remaining” value)

Second pass:

  1. The machine recognizes that it has distributed 1999 - ceil(0.994+0.995) = 1997 out of the original 1999 of the send and hence has a remaining 2 of AUD/2 to distribute

  2. It distributes them as evenly as possible, from top to bottom

  3. First position @payment_provider gets 1 more AUD/2, topping this posting value from 7 to 8. Then at the second position, @payment_provider again, gets 1 more AUD/2, topping this posting value from 11 to 12.

Solution: to deal with this unexpected addition on fixed fee, we can move down the fixed-fee statement. This will move our percentage computations upstack and make them first receivers of the cents fragments. Knowing that no percentage can yield more than 1 cent fragment and that we only have two real percentage computations here with the potential for fragments, we can know that no additional extra cent will reach the @payment_provider on third destination line

send [AUD/2 1999] (
  source = @world
  destination = @sales:1234
)

send [AUD/2 *] (
  source = @sales:1234
  destination = {
    0.6% to @payment_provider
    0.5% to @franchise_fee
    7/1999 to @payment_provider
    remaining to @store
  }
)