# 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 newpurse
is created by anissuer
and can only hold assets of thatissuer
'sbrand
.payment
: Holds a quantity of same-branded digital assets to transfer to another party. A newpayment
is created either with new assets by amint
or by withdrawing assets from apurse
. It can only hold assets of the samebrand
as thatmint
orpurse
.
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
amount
from apurse
, creating apayment
. - Send this
payment
to a recipient as a message.
- Withdraw assets described by an
- Receive assets:
- If you don't already have one, create a
purse
for the assetbrand
you'll receive. - Receive the message with the
payment
. - Deposit the
payment
in abrand
appropriatepurse
.
- 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
purse
as anamount
. Note that apurse
can 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
amount
from thispurse
into 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
payment
into thispurse
, returning a description of thepayment
balance as anamount
. If the optional argumentoptAmount
does not equal thepayment
balance, or ifpayment
is 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
purse
that 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 aDepositFacet
is 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
purse
for storing digital assets of thebrand
theissuer
is 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
issuer
to create an emptypurse
for thatbrand
. - Deposit the
payment
into 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 thepayment
purports to be. Since apayment
is 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 theissuer
for thatbrand
done on thepayment
verifies thepayment
.
- Returns a
Other objects' payment
-related methods:
issuer.getAmountOf(payment)
- Get the amount of digital assets in the
payment
as anamount
. Thepayment
itself is not trusted, so you must use theissuer
method associated with itsbrand
to 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
.optAmount
is optional but if present, thepayment
balance must be equal to it. Ifpayment
is 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
payment
to a returned newpayment
and delete the original from theissuer
's records. Any references to the oldpayment
outside theissuer
will still exist, but if anyone attempts to use the oldpayment
, an error is thrown.If
optAmount
is present, thepayment
balance must be equal to it or it throws an error. Ifpayment
is 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
payments
into one, returned,payment
. If anypayment
in the array is a promise, the operation proceeds after everypayment
resolves. Allpayments
in 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
payment
into two new ones, A and B, returned in an array.paymentAmountA
determines A's value, and whatever is left of the originalpayment
after subtracting A is B's value. IfpaymentAmountA
has a largervalue
thanpayment
, it throws an error.The
payment
argument is deleted from the issuer's records. Ifpayment
is 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
payment
into multiplepayments
, returned as an array the same length asamountArray
and with itspayments
having the same values as specified foramountArray
's elements. Ifpayment
is a promise for a payment, the operation proceeds after it resolves. If thepayment
value is not equal to the sum ofamountArray
's values, the operation fails. On success, the originalpayment
is 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
true
ifpayment
has value. Ifpayment
is a promise for payment, the operation proceeds upon resolution.
- Returns
mint.mintPayment(newAmount)
- Returns a new
payment
containing the newly minted assets corresponding to thenewAmount
argument. Note that unlike creating a newpayment
by withdrawing existing assets from apurse
, this creates new digital assets of the specified innewAmount
brand
. 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
payment
into thispurse
, returning the depositamount
description. If optionaloptAmount
does not equal thepayment
's balance or ifpayment
is 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
amount
from thispurse
into 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
purse
that 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();