# Purses and Payments
Digital assets can be any of:
- Currency-like, such as our imaginary Quatloos currency.
- Goods-like digital assets, such as magic weapons for use in a game or theater tickets.
- Other kinds of rights, such as the right to participate in a particular contract.
In ERTP, digital assets exist in either a purse or a payment:
purse: Holds an amount of same-branded digital assets until part or all of them are withdrawn into apayment. A newpurseis created by anissuerand can only hold assets of thatissuer'sbrand.payment: Holds a quantity of same-branded digital assets to transfer to another party. A newpaymentis created either with new assets by amintor by withdrawing assets from apurse. It can only hold assets of the samebrandas thatmintorpurse.
For any brand, any number of purses or payments can hold assets
of that brand. Neither a purse nor a payment can ever change their
associated brand.
Each purse and payment object contains a specific single amount of digital assets,
which may be none at all (empty in AmountMath terms). In the same way
you might have separate bank accounts for different purposes,
you can have different purses for the same brand of digital asset.
One purse might hold 2 Quatloos and another purse might hold 3 Quatloos.
When you deposit assets into a purse or payment, they are added to
whatever assets already exist there. So a 3 Quatloos deposit
into a purse with 8 Quatloos, results in a purse with 11 Quatloos. Similarly,
then withdrawing 6 Quatloos from that purse into a new payment leaves the purse
with 5 Quatloos and the new payment has 6 Quatloos.
When adding a payment to a purse, you must add the entire payment. To
only add part of a payment, you must first use payment.split()
or payment.splitMany() to split the payment into two or more new payments.
mints create entirely new digital assets and put them in a new payment.
You also create a payment by withdrawing assets from a purse, by splitting an
existing payment, or by combining multiple payments into one new one. Note
the brand of the new payment is the same as the associated brand of its originating mint, purse or payment.
You don't transfer assets directly from one purse to
another purse. Instead, in ERTP you do something like these steps to send and receive assets. The actual send operation is up to you; ERTP does not
implement a way of sending object-containing messages between parties.
- Send assets:
- Withdraw assets described by an
amountfrom apurse, creating apayment. - Send this
paymentto a recipient as a message.
- Withdraw assets described by an
- Receive assets:
- If you don't already have one, create a
pursefor the assetbrandyou'll receive. - Receive the message with the
payment. - Deposit the
paymentin abrandappropriatepurse.
- If you don't already have one, create a
# Purses
You change a purse's balance by calling either deposit() (to add assets) or
withdraw() (to remove assets) on it. A purse can be empty, which for
fungible assets means it has a value of 0. For non-fungible
assets, such as theatre tickets, it doesn't have any tickets.
Unlike payments, purses are not meant to be sent to others. To transfer
digital assets, you should withdraw a payment from a purse and send
the payment to another party.
You can create a deposit facet for a purse. Deposit facets are either sent to other parties or made publicly known. Any party can deposit a payment into the
deposit facet, which deposits it into its associated purse. However, no one can
use a deposit facet to either make a withdrawal from its purse or get the purse's balance.
If you have a deposit facet, you make a deposit to its associated purse by calling
depositFacet.receive(payment);. Note that you add a payment to a purse with a deposit() method, while you add a payment to a depositFacet with a receive() method.
The payment must be the same brand as what
the associated purse object can contain. Otherwise it throws an error.
When sending a deposit facet object
to a party, you should tell them what assets brand it accepts.
The following is a brief description and example of each purse method. For
more detail, click the method's name to go to its entry in the ERTP
API Reference.
purse.getCurrentAmount()- Returns a description of the digital assets currently stored in the
purseas anamount. Note that apursecan be empty.const quatloosPurse = quatloosIssuer.makeEmptyPurse(); // Balance should be 0 Quatloos. const currentBalance = quatloosPurse.getCurrentAmount(); // Deposit a payment of 5 Quatloos quatloosPurse.deposit(quatloosPayment5); // Balance should be 5 Quatloos const newBalance = quatloosPurse.getCurrentAmount();
purse.withdraw(amount)- Withdraw the assets described by
amountfrom thispurseinto a newpayment. Returns the newpayment. // Withdraw 3 Quatloos from a purse. const newPayment = quatloosPurse.withdraw(AmountMath.make(brand, 3n));
- Withdraw the assets described by
purse.deposit(payment, optAmount)- Deposit the digital asset contents of the
paymentinto thispurse, returning a description of thepaymentbalance as anamount. If the optional argumentoptAmountdoes not equal thepaymentbalance, or ifpaymentis an unresolved promise, it throws an error. const quatloosPurse = quatloosIssuer.makeEmptyPurse(); const quatloos123 = AmountMath.make(quatloosBrand, 123n); const quatloosPayment = quatloosMint.mintPayment(quatloos123); // Deposit a payment for 123 quatloos into the purse. Ensure that this is the amount you expect. quatloosPurse.deposit(quatloosPayment, quatloos123); const secondPayment = quatloosMint.mintPayment( AmountMath.make(quatloosBrand, 100n), ); // Throws error since secondPayment is 100 Quatloos and quatloos123 is 123 Quatloos t.throws(() => quatloosPurse.deposit(secondPayment, quatloos123), { // To cope with version skew, this pattern is current and or of the old // message and the new message. TODO: nce the old message no longer needs to // be supported, delete it, simplifying the pattern. message: /(?:payment balance .* must equal amount .*)|(?:{"brand":"\[Alleged: quatloos brand\]","value":"\[100n\]"} - Must be equivalent to: {"brand":"\[Alleged: quatloos brand\]","value":"\[123n\]"})/, });
- Deposit the digital asset contents of the
purse.getDepositFacet()- Returns a deposit-only facet on the
pursethat can be given to other parties. This lets them make a deposit to thepurse, but not make withdrawals from it or get its balance. Note that the command to add apayment's assets via aDepositFacetis notdeposit()butreceive()as shown here. const depositOnlyFacet = purse.getDepositFacet(); // Give depositOnlyFacet to someone else. They can pass a payment // that will be deposited: depositOnlyFacet.receive(payment);
- Returns a deposit-only facet on the
In addition, the method to create a new, empty, purse is called on an issuer:
issuer.makeEmptyPurse()- Returns a new empty
pursefor storing digital assets of thebrandtheissueris associated with. const { issuer: quatloosIssuer } = makeIssuerKit('quatloos'); // The new empty purse contains 0 Quatloos const quatloosPurse = quatloosIssuer.makeEmptyPurse();
- Returns a new empty
# Payments
Payments hold digital assets intended to be transferred to another party.
They are linear, meaning that either a payment has its full
original balance, or it is used up entirely. It is impossible to
partially use a payment.
In other words, if you create a payment containing
10 Quatloos, the payment will always either contain
10 Quatloos or it will be deleted from its issuer records and no
longer have any value. While a payment can be either combined with others or
split into multiple payments, in both cases the original payment(s)
is/are deleted and the results put in one or more new payments.
A payment can be deposited in purses, split into multiple
payments, combined with other payments, and claimed (getting an exclusive payment and revoking access from anyone else).
A payment is often received from other parties. Since they are not
self-verifying, you cannot trust payments. To get the verified balance
of a payment, use the getAmountOf(payment) method on the trusted issuer
for the payment's brand.
To get the issuer for a brand you didn't create,
ask someone you trust. For example, the venue creating tickets for shows
can be trusted to give you the tickets' issuer. Or, a friend might have
a cryptocurrency they like, and, if you trust them, you might accept
that the issuer they give you is valid.
To convert a payment into a new purse:
- Get the
payment's trustedissuer. - Use the
issuerto create an emptypursefor thatbrand. - Deposit the
paymentinto the newpurse.purse.deposit(payment)deletes thepayment.
Payments have only one API method, but many methods for other ERTP components
have payments as arguments and effectively operate on a payment. The following is a
brief description and example of each payment-related method. For
more detail, click the method's name to go to its entry in the ERTP
API Reference.
payment.getAllegedBrand()- Returns a
brand, indicating what kind of digital asset thepaymentpurports to be. Since apaymentis not trusted, this result should be
treated with suspicion. Either verify the value, or check the result when you use it. Any successful operation by theissuerfor thatbranddone on thepaymentverifies thepayment.
- Returns a
Other objects' payment-related methods:
issuer.getAmountOf(payment)- Get the amount of digital assets in the
paymentas anamount. Thepaymentitself is not trusted, so you must use theissuermethod associated with itsbrandto be sure of getting the true value. const quatloosPayment = quatloosMint.mintPayment( AmountMath.make(quatloosBrand, 100n), ); // returns an amount with a value of 100 and the quatloos brand quatloosIssuer.getAmountOf(quatloosPayment);
- Get the amount of digital assets in the
issuer.burn(payment, optAmount)- Burn all of the digital assets in the
payment.optAmountis optional but if present, thepaymentbalance must be equal to it. Ifpaymentis a promise, the operation proceeds after it resolves. const amountToBurn = AmountMath.make(quatloosBrand, 10n); const paymentToBurn = quatloosMint.mintPayment(amountToBurn); // burntAmount is 10 quatloos const burntAmount = await quatloosIssuer.burn(paymentToBurn, amountToBurn);
- Burn all of the digital assets in the
issuer.claim(payment, optAmount)Transfer all assets from the
paymentto a returned newpaymentand delete the original from theissuer's records. Any references to the oldpaymentoutside theissuerwill still exist, but if anyone attempts to use the oldpayment, an error is thrown.If
optAmountis present, thepaymentbalance must be equal to it or it throws an error. Ifpaymentis a promise, the operation proceeds after it resolves.const amountToTransfer = AmountMath.make(quatloosBrand, 2n); const originalPayment = quatloosMint.mintPayment(amountToTransfer); const newPayment = await quatloosIssuer.claim( originalPayment, amountToTransfer, );
issuer.combine(paymentsArray)- Combine multiple
paymentsinto one, returned,payment. If anypaymentin the array is a promise, the operation proceeds after everypaymentresolves. Allpaymentsin the array are burned on successful completion. // create an array of 100 payments of 1 unit each const payments = []; for (let i = 0; i < 100; i += 1) { payments.push(quatloosMint.mintPayment(AmountMath.make(quatloosBrand, 1n))); } // combinedPayment equals 100 const combinedPayment = quatloosIssuer.combine(harden(payments));
- Combine multiple
issuer.split(payment, paymentAmountA)Split one
paymentinto two new ones, A and B, returned in an array.paymentAmountAdetermines A's value, and whatever is left of the originalpaymentafter subtracting A is B's value. IfpaymentAmountAhas a largervaluethanpayment, it throws an error.The
paymentargument is deleted from the issuer's records. Ifpaymentis a promise, the operation proceeds after the promise for a payment resolves.const oldPayment = quatloosMint.mintPayment( AmountMath.make(quatloosBrand, 30n), ); const [paymentA, paymentB] = await quatloosIssuer.split( oldPayment, AmountMath.make(quatloosBrand, 10n), ); // paymentA is 10 quatloos, payment B is 20 quatloos.
issuer.splitMany(payment, amountArray)- Split
paymentinto multiplepayments, returned as an array the same length asamountArrayand with itspaymentshaving the same values as specified foramountArray's elements. Ifpaymentis a promise for a payment, the operation proceeds after it resolves. If thepaymentvalue is not equal to the sum ofamountArray's values, the operation fails. On success, the originalpaymentis deleted from the issuer's records. const oldQuatloosPayment = quatloosMint.mintPayment( AmountMath.make(quatloosBrand, 100n), ); const goodQuatloosAmounts = Array(10).fill( AmountMath.make(quatloosBrand, 10n), ); const arrayOfNewPayments = await quatloosIssuer.splitMany( oldQuatloosPayment, harden(goodQuatloosAmounts), );
- Split
issuer.isLive(payment)- Returns
trueifpaymenthas value. Ifpaymentis a promise for payment, the operation proceeds upon resolution.
- Returns
mint.mintPayment(newAmount)- Returns a new
paymentcontaining the newly minted assets corresponding to thenewAmountargument. Note that unlike creating a newpaymentby withdrawing existing assets from apurse, this creates new digital assets of the specified innewAmountbrand. const { mint: quatloosMint, brand: quatloosBrand } = makeIssuerKit( 'quatloos', ); const quatloos1000 = AmountMath.make(quatloosBrand, 1000n); const newPayment = quatloosMint.mintPayment(quatloos1000);
- Returns a new
purse.deposit(payment, optAmount)- Deposit all of
paymentinto thispurse, returning the depositamountdescription. If optionaloptAmountdoes not equal thepayment's balance or ifpaymentis an unresolved promise, it throws an error. const quatloosPurse = quatloosIssuer.makeEmptyPurse(); const quatloos123 = AmountMath.make(quatloosBrand, 123n); const quatloosPayment = quatloosMint.mintPayment(quatloos123); // Deposit a payment for 123 quatloos into the purse. Ensure that this is the amount you expect. quatloosPurse.deposit(quatloosPayment, quatloos123); const secondPayment = quatloosMint.mintPayment( AmountMath.make(quatloosBrand, 100n), ); // Throws error since secondPayment is 100 Quatloos and quatloos123 is 123 Quatloos t.throws(() => quatloosPurse.deposit(secondPayment, quatloos123), { // To cope with version skew, this pattern is current and or of the old // message and the new message. TODO: nce the old message no longer needs to // be supported, delete it, simplifying the pattern. message: /(?:payment balance .* must equal amount .*)|(?:{"brand":"\[Alleged: quatloos brand\]","value":"\[100n\]"} - Must be equivalent to: {"brand":"\[Alleged: quatloos brand\]","value":"\[123n\]"})/, });
- Deposit all of
purse.withdraw(amount)- Withdraw the assets described by
amountfrom thispurseinto a newpayment. Returns the newpayment. // Withdraw 3 Quatloos from a purse. const newPayment = quatloosPurse.withdraw(AmountMath.make(brand, 3n));
- Withdraw the assets described by
purse.getDepositFacet()- Creates a deposit-only facet on the
pursethat can be given to other parties that lets them deposit apayment(but not withdraw) into thepurse. Note that you add a payment via a deposit facet by callingdepositFacet.receive(payment)instead ofdeposit(). const depositOnlyFacet = purse.getDepositFacet(); // Give depositOnlyFacet to someone else. They can pass a payment // that will be deposited: depositOnlyFacet.receive(payment);
- Creates a deposit-only facet on the
# purse and payment example
The following code creates a new purse for the quatloos brand, deposits
10 Quatloos into the purse, withdraws 3 Quatloos from the purse into a
payment, and finally returns an amount describing what's currently in the purse, 7 Quatloos.
// Create a purse with a balance of 10 Quatloos
const {
issuer: quatloosIssuer,
mint: quatloosMint,
brand: quatloosBrand,
} = makeIssuerKit('quatloos');
const quatloosPurse = quatloosIssuer.makeEmptyPurse();
const quatloos10 = AmountMath.make(quatloosBrand, 10n);
const quatloosPayment = quatloosMint.mintPayment(quatloos10);
// If the two arguments aren't equal (i.e. both need to be for 10 Quatloos),
// throws an error. But they are both for 10 Quatloos, so no problem.
quatloosPurse.deposit(quatloosPayment, quatloos10);
// Withdraw 3 Quatloos from the purse into a payment
const quatloos3 = AmountMath.make(quatloosBrand, 3n);
const withdrawalPayment = quatloosPurse.withdraw(quatloos3);
// The balance of the withdrawal payment is 3 Quatloos
await quatloosIssuer.getAmountOf(withdrawalPayment);
// The new balance of the purse is 7 Quatloos
quatloosPurse.getCurrentAmount();