Transactions with multiple sources
Sometimes you need to split a payment from multiple sources. For example:
- At times there might not be enough money in an account you want to pay from. You want to specify a backup account.
- You need to spread costs across multiple accounts. For example, you might have a special marketing fund that partially covers certain payments.
- Your game players want to go in together on a shared purchase.
Make sure that you're set up properly: Read through the prerequisites first!. Otherwise the examples below won't work.
Basic transaction splitting
Since we're in the game land of Cones of Dunshire, let's consider that last case. Two players donnameagle
and tomhaverford
have decided to treat themselves to a pony, which they will buy from us. The pony costs 75 coins, and they want to split the price evenly between themselves.
We can describe the transaction using Numscript. Create a file called treat.num
with:
send [COIN 75] (
source = {
50% from @player:donnameagle
remaining from @player:tomhaverford
}
destination = @centralbank
)
And run it with
fctl ledger transactions num treat.num
Are you seeing
FATA[0000] account had insufficient funds
in your output? The source accounts need to have enough funds before you run this example. Make sure you're set up properly.
Once you've run that transaction succesfully, let's have a look at donnameagle
's balance:
You should be able to see, as in the image above, that donnameagle
paid 38 coins, and tomhaverford
paid 37 coins.
What's going on here?
First, we're telling Formance Ledger to take half of the 75 coin payment from donnameagle
. Notice that 75, being an odd number, doesn't split evenly in half. Numscript is smart about this, and rounds amounts in a way that avoids rounding errors. Because donnameagle
is listed first, they will pay the coin that remains after evenly subdividing: They will pay 38 coin.
Floating point numbers are too imprecise for finance. Numscript avoids this problem by only using integer math for dividing payments up. The reference docs have more detail on about Numscript's rounding algorithm.
Second, we're avoiding mistakes in our own calculations by telling Formance Ledger to pay whatever remains from tomhaverford
, after donnameagle
pays their share, by using the remaining
keyword. Since donnameagle
paid 38 coin, that leaves 37 coin for tomhaverford
to pay.
Specifying backup accounts
Splitting payments isn't the only use for specifying multiple source accounts. Sometimes you can't be sure that one account will have enough money to pay for a transaction, and you want to be able to specify a backup account to guarantee transaction success.
Suppose that andydwyer
owes aprilludgate
100 coins for medical services after falling into a pit. But imagine moreover that andydwyer
isn't sure that he has enough coin in his pocket, but he knows he has some stashed in a treasure chest. Here's how we can describe that transaction.
Create a file called bills.num
with:
send [COIN 100] (
source = {
@player:andydwyer
@player:andydwyer:chest
}
destination = @player:aprilludgate
)
and run it with
fctl ledger transactions num bills.num
Are you seeing
FATA[0000] account had insufficient funds
in your output? The source accounts need to have enough funds before you run this example. Make sure you're set up properly.
Run this script, and then look at andydwyer
's balance.
You should be able to see, as in the image above, that 50 coin came from andydwyer
's primary account, and another 50 from the secondary account listed.
What's going on here?
In this case, we have only specified a list of accounts as the source, without any apportionment. Formance Ledger will attempt to pay the entire amount from the first account listed. If there are insufficient funds in that account, then the remainder will be applied against the next account listed, and so on. You can list as many accounts as you like to draw funds from like this.
If the total in all the listed accounts remains insufficient to cover the movement, the transaction will fail and no money will be moved.
So, since there were only 50 coins in the first account, Formance Ledger looked at the second account, and took the remaining balance of 50 coins from it to complete the transaction.
Nested sources
donnameagle
and tomhaverford
are not satisfied with just one pony—they want a second pony. But this time, donnameagle
knows she has some upcoming transactions for which she needs to set aside some coin. She is only willing to pay 10 coin from her pocket. But she has some coin saved in a chest that she is willing to spend.
We could specify this new transaction as such.
send [COIN 75] (
source = {
max [COIN 10] from @player:donnameagle
37% from @player:donnameagle:chest
remaining from @player:tomhaverford
}
destination = @centralbank
)
We use the keyword max
to indicate that no more than the specified amount should be taken from donnameagle
's regular account.
But the remainder of the Numscript is annoyingly tedious: We had to compute that the share of the purchase price from donnameagle
's chest would be 37%. And if she doesn't have 10 coin in her pocket, that figure won't be correct.
There is a better way: Nested sources. Create a file called pony2.num
with the following:
send [COIN 75] (
source = {
50% from {
max [COIN 10] from @player:donnameagle
@player:donnameagle:chest
}
remaining from @player:tomhaverford
}
destination = @centralbank
)
and run it with
fctl ledger transactions num pony2.num
Are you seeing
FATA[0000] account had insufficient funds
in your output? The source accounts need to have enough funds before you run this example. Make sure you're set up properly.
After running this script, and then look at donnameagle
's balance.
What's going on here?
Just like in the original pony purchase, we are asking Formance Ledger to split the cost 50-50 between donnameagle
and tomhaverford
. But, we've added a nested constraint on how we spend donnameagle
's coin. First, we use the max
keyword to indicate that no more than 10 coins should be taken from her main account. Then we add a backup account—her treasure chest—to take the remainder of her share from.
Going further
Numscript offers several different mechanisms for indicating how a transaction should be split amfrom different sources. This guide has just been a small taste of what's possible.
Want to learn more about all the different ways to transact from multiple sources? The reference docs have you covered!