# Issuers and Mints

# Issuers

Issuer structure

Note: You should not create an Issuer in a deploy script. Deploy scripts are ephemeral, so any object created there dies as soon as the script stops.

Behind the scenes, an issuer maps minted digital assets to their location in a purse or payment. An issuer verifies, moves, and manipulates digital assets. Its special admin facet is a mint which it has a one-to-one relationship with. Only a mint can issue new digital assets; an issuer cannot.

An issuer also has a one-to-one relationship with a brand. So, if our brand is the imaginary currency Quatloos, only the issuer in the one-to-one relationship with the Quatloos brand can:

  • Create a new empty purse that can store Quatloos.
  • Manipulate a payment in Quatloos to be claimed, split, combined, burned, or have its amount gotten.

An issuer should be obtained from a trusted source and then relied upon as the authority as to whether an untrusted payment of the same brand is valid.

Issuer methods

Issuer methods:

  • Return information about an issuer.
  • Create a new issuer.
  • Create a new purse.
  • Operate on payment arguments.

The following is a brief description and example of each Issuer method. For more detail, click the method's name to go to its entry in the ERTP API Reference.

  • Create issuer operation
    • makeIssuerKit(allegedName, assetKind, displayInfo=)

    • Makes an issuer and its related mint and brand. Returns { mint, issuer, brand } The mint and brand are in unchangeable one-to-one relationships with the issuer and each other.

      The allegedName is available from the brand to describe assets, but should not be trusted.

      assetKind specifies if the associated asset is of kind AssetKind.NAT (nat) (the default value) or AssetKind.SET (set); see the AmountMath page for details.

      displayInfo is the number of places to the right of the decimal point to display of any values associated with the created brand. It defaults to undefined

      • import { AmountMath, makeIssuerKit, AssetKind } from '@agoric/ertp';
        const {
          issuer: quatloosIssuer,
          mint: quatloosMint,
          brand: quatloosBrand,
        } = makeIssuerKit('quatloos');
        // This is merely an amount, describing assets.
        // It does not create new assets.
        const quatloos2 = AmountMath.make(quatloosBrand, 2n);
        // Non-fungible asset, which needs an AssetKind
        // of AssetKind.SET
        const { mint: titleMint, issuer: titleIssuer } = makeIssuerKit(
          'alamedaCountyPropertyTitle',
          AssetKind.SET,
        );
  • Get information about the issuer operations
    • issuer.getBrand()
      • Returns the brand the issuer is in a one-to-one relationship with. The brand is not closely held, so it can be used by fake digital assets and amounts. Do not trust this method alone to ensure the issuer has the right brand.
      • const { issuer: quatloosIssuer, brand: quatloosBrand } = makeIssuerKit(
          'quatloos',
        );
        // myQuatloosBrand === quatloosBrand
        const myQuatloosBrand = quatloosIssuer.getBrand();
    • issuer.getAllegedName()
      • Returns the issuer/mint's allegedName, the non-trusted human-readable name of the issuer's associated brand.
      • const { issuer: quatloosIssuer } = makeIssuerKit('quatloos');
        const quatloosIssuerAllegedName = quatloosIssuer.getAllegedName();
        // quatloosIssuerAllegedName === 'quatloos'
    • issuer.getAssetKind()
      • Get the kind of asset for this issuer, either AssetKind.NAT (nat), or AssetKind.SET (set).
      • const { issuer: quatloosIssuer } = makeIssuerKit('quatloos');
        const kind = quatloosIssuer.getAssetKind(); // 'nat', the default value for makeIssuerKit()
  • Purse operation
    • issuer.makeEmptyPurse()
      • Returns a new empty purse for the brand associated with the issuer. The purse only accepts valid deposits of its associated brand, so you can retroactively identify a valid payment of that brand by successfully depositing it into this purse.
      • const { issuer: quatloosIssuer } = makeIssuerKit('quatloos');
        // The new empty purse contains 0 Quatloos
        const quatloosPurse = quatloosIssuer.makeEmptyPurse();
  • Payment operations
    • issuer.getAmountOf(payment)

      • Returns the payment balance, an amount. Using the issuer rather than the payment lets us trust the result even if someone we do not trust sent us the payment.
      • const quatloosPayment = quatloosMint.mintPayment(
          AmountMath.make(quatloosBrand, 100n),
        );
        // returns an amount with a value of 100 and the quatloos brand
        quatloosIssuer.getAmountOf(quatloosPayment);
    • issuer.burn(payment, optAmount)

      • Burns (destroys) all of the payment argument's digital assets and deletes all mention of the payment from the issuer. If optional argument optAmount is present, the payment balance must be equal to optAmount's value. If payment is a promise, the operation happens after the promise resolves. Returns the value of the burned payment.
      • const amountToBurn = AmountMath.make(quatloosBrand, 10n);
        const paymentToBurn = quatloosMint.mintPayment(amountToBurn);
        // burntAmount is 10 quatloos
        const burntAmount = await quatloosIssuer.burn(paymentToBurn, amountToBurn);
    • issuer.claim(payment, optAmount)

      • Transfer all digital assets from the payment argument to a new payment and burn the original so no other references to this payment survive. Returns the new payment If optional argument optAmount is present, the payment balance must be equal to optAmount's balance, otherwise it throws an error. If payment is a promise for a payment, the operation happens after the promise 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 payment. If any payment in paymentsArray is a promise for a payment, the operation happens after all promises resolve. Every payment is burned except for the returned one. If you try to combine payments of different brands, it throws an exception and each payment is unaffected.
      • // 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));
    • issuer.split(payment, paymentAmountA)

      • Split a single payment into two new payments, A and B, according to the paymentAmountA argument's value. In other words, the result has A equal to paymentAmountA and B equal to the original payment minus paymentAmountA. The original payment argument is burned. If the original payment is a promise, the operation happens when the promise 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, paymentAmountArray)

      • Returns multiple payments in an array from splitting its single payment argument. The resulting number of payments is specified as the length of the paymentAmountArray argument, with the newly split payments having amounts corresponding to those in paymentAmountArray. If the paymentAmountArray argument amounts don't add up to the value of the payment argument, the operation fails. If the operation is successful, the original payment is burned. If the operation fails, the original payment is not burned.
      • 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),
        );
        // Note that the total amount in the amountArray must equal the
        // amount in the original payment, in the above case, 100 Quatloos in each.
        
        const anotherQuatloosPayment = quatloosMint.mintPayment(
          AmountMath.make(quatloosBrand, 1000n),
        );
        // total amounts in badQuatloosAmounts equal 20, when it should equal 1000
        const badQuatloosAmounts = Array(2).fill(AmountMath.make(quatloosBrand, 10n));
        // throws error
        t.throwsAsync(
          () =>
            quatloosIssuer.splitMany(
              anotherQuatloosPayment,
              harden(badQuatloosAmounts),
            ),
          { message: /rights were not conserved/ },
        );
    • issuer.isLive(payment)

      • Returns true if the payment argument is still active (i.e. has not been used or burned and was issued by this issuer). If payment is a promise, the operation happens on its resolution.
      •   const isItLive = quatloosIssuer.isLive(payment);

Related Methods:

Note: None of these methods return a canonical result. If the issuer itself doesn't acknowledge that the mint, brand or purse are associated with it, then they're invalid. These methods help you find the right issuer, but aren't authoritative.

  • mint.getIssuer()
    • Return the associated issuer for the mint.
    • const { issuer: quatloosIssuer, mint: quatloosMint } = makeIssuerKit(
        'quatloos',
      );
      const quatloosMintIssuer = quatloosMint.getIssuer();
      // returns true
      const sameIssuer = quatloosIssuer === quatloosMintIssuer;
  • brand.isMyIssuer(issuer)
    • Returns true if the brand comes from this issuer.
    •   const isIssuer = quatloosBrand.isMyIssuer(quatloosIssuer);

# Mints

Mint methods

A mint issues new digital assets of its associated brand as a new payment object. These assets may be currency-like (our imaginary Quatloos currency), goods-like valuables (magic swords for games), or electronic rights (the right to participate in a contract). Only a holder of a mint object can create new assets from it.

In other words, let's say there are 1000 Quatloos in circulation. Only holders of the Quatloos associated mint can make any more Quatloos that'd boost the amount in circulation to, say, 2000.

Since these relationships are one-to-one and unchangeable:

  • A mint created to make an asset brand, say Quatloos, can only create that brand asset. For example, only Quatloos, not Moola or anything else.
  • A mint that creates an asset brand is the only mint that can create that brand. Only the one Quatloos mint can create new Quatloos.
  • A mint that creates an asset brand can never be changed to create a different brand. So a Quatloos mint can never become a Moola mint, or any other non-Quatloos asset.

There are two mint methods, and the method that creates new mints. Click the method's name to go to its entry in the ERTP API Reference.

  • mint.getIssuer()
    • Returns the issuer uniquely associated with the mint.
    • const { issuer: quatloosIssuer, mint: quatloosMint } = makeIssuerKit(
        'quatloos',
      );
      const quatloosMintIssuer = quatloosMint.getIssuer();
      // returns true
      const sameIssuer = quatloosIssuer === quatloosMintIssuer;
  • mint.mintPayment(newAmount)
    • Returns a new payment containing newly minted assets with a balance equal to newAmount. In other words, it mints newAmount of digital assets and creates a payment to hold those new assets. The assets are of the mint's associated brand.

      Important: mint.mintPayment() is the only way in ERTP to create new digital assets. There is no other way. The Zoe Contract Facet (zcf) can also create a mint in Zoe that can create new digital assets.

    • const { mint: quatloosMint, brand: quatloosBrand } = makeIssuerKit(
        'quatloos',
      );
      const quatloos1000 = AmountMath.make(quatloosBrand, 1000n);
      const newPayment = quatloosMint.mintPayment(quatloos1000);
  • makeIssuerKit(allegedName, assetKind, displayInfo)
    • While not a method called on a mint, clearly you should know how to create a new mint. makeIssuerKit() returns a new issuer, mint, and brand.
    • import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';
      
      // #region import
      import { AmountMath, makeIssuerKit, AssetKind } from '@agoric/ertp';
      // #endregion import
      
      test('ertp guide issuers and mints makeIssuerKit', async t => {
        // #region makeIssuerKit
        const {
          issuer: quatloosIssuer,
          mint: quatloosMint,
          brand: quatloosBrand,
        } = makeIssuerKit('quatloos');
        // This is merely an amount, describing assets.
        // It does not create new assets.
        const quatloos2 = AmountMath.make(quatloosBrand, 2n);
        // Non-fungible asset, which needs an AssetKind
        // of AssetKind.SET
        const { mint: titleMint, issuer: titleIssuer } = makeIssuerKit(
          'alamedaCountyPropertyTitle',
          AssetKind.SET,
        );
        // #endregion makeIssuerKit
      
        t.truthy(quatloosIssuer);
        t.truthy(quatloosMint);
        t.truthy(quatloosBrand);
        t.deepEqual(quatloos2, { brand: quatloosBrand, value: 2n });
        t.truthy(titleMint);
        t.truthy(titleIssuer);
      });
      
      test('ertp guide issuers and mints getBrand', async t => {
        // #region getBrand
        const { issuer: quatloosIssuer, brand: quatloosBrand } = makeIssuerKit(
          'quatloos',
        );
        // myQuatloosBrand === quatloosBrand
        const myQuatloosBrand = quatloosIssuer.getBrand();
        // #endregion getBrand
      
        t.is(quatloosBrand, myQuatloosBrand);
      });
      
      test('ertp guide issuers and mints getAllegedName', async t => {
        // #region getAllegedName
        const { issuer: quatloosIssuer } = makeIssuerKit('quatloos');
        const quatloosIssuerAllegedName = quatloosIssuer.getAllegedName();
        // quatloosIssuerAllegedName === 'quatloos'
        // #endregion getAllegedName
        t.is(quatloosIssuerAllegedName, 'quatloos');
      });
      
      test('ertp guide issuers and mints getAssetKind', async t => {
        // #region getAssetKind
        const { issuer: quatloosIssuer } = makeIssuerKit('quatloos');
        const kind = quatloosIssuer.getAssetKind(); // 'nat', the default value for makeIssuerKit()
        // #endregion getAssetKind
        t.is(kind, 'nat');
      });
      
      test('ertp guide issuers and mints makeEmptyPurse', async t => {
        // #region makeEmptyPurse
        const { issuer: quatloosIssuer } = makeIssuerKit('quatloos');
        // The new empty purse contains 0 Quatloos
        const quatloosPurse = quatloosIssuer.makeEmptyPurse();
        // #endregion makeEmptyPurse
        t.deepEqual(await quatloosPurse.getCurrentAmount(), {
          brand: quatloosIssuer.getBrand(),
          value: 0n,
        });
      });
      
      test('ertp guide issuers and mints payment methods', async t => {
        const {
          issuer: quatloosIssuer,
          brand: quatloosBrand,
          mint: quatloosMint,
        } = makeIssuerKit('quatloos');
      
        // #region getAmountOf
        const quatloosPayment = quatloosMint.mintPayment(
          AmountMath.make(quatloosBrand, 100n),
        );
        // returns an amount with a value of 100 and the quatloos brand
        quatloosIssuer.getAmountOf(quatloosPayment);
        // #endregion getAmountOf
      
        // #region burn
        const amountToBurn = AmountMath.make(quatloosBrand, 10n);
        const paymentToBurn = quatloosMint.mintPayment(amountToBurn);
        // burntAmount is 10 quatloos
        const burntAmount = await quatloosIssuer.burn(paymentToBurn, amountToBurn);
        // #endregion burn
        t.deepEqual(burntAmount, amountToBurn);
      
        // #region claim
        const amountToTransfer = AmountMath.make(quatloosBrand, 2n);
        const originalPayment = quatloosMint.mintPayment(amountToTransfer);
        const newPayment = await quatloosIssuer.claim(
          originalPayment,
          amountToTransfer,
        );
        // #endregion claim
        t.deepEqual(await quatloosIssuer.getAmountOf(newPayment), amountToTransfer);
        t.not(originalPayment, newPayment);
      
        // #region combine
        // 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));
        // #endregion combine
      
        t.deepEqual(await quatloosIssuer.getAmountOf(combinedPayment), {
          brand: quatloosBrand,
          value: 100n,
        });
      
        // #region split
        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.
        // #endregion split
        const paymentAAmount = await quatloosIssuer.getAmountOf(paymentA);
        const paymentBAmount = await quatloosIssuer.getAmountOf(paymentB);
        t.deepEqual(paymentAAmount, AmountMath.make(quatloosBrand, 10n));
        t.deepEqual(paymentBAmount, AmountMath.make(quatloosBrand, 20n));
      
        // #region splitMany
        // #region splitManyConcise
        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),
        );
        // #endregion splitManyConcise
        // Note that the total amount in the amountArray must equal the
        // amount in the original payment, in the above case, 100 Quatloos in each.
      
        const anotherQuatloosPayment = quatloosMint.mintPayment(
          AmountMath.make(quatloosBrand, 1000n),
        );
        // total amounts in badQuatloosAmounts equal 20, when it should equal 1000
        const badQuatloosAmounts = Array(2).fill(AmountMath.make(quatloosBrand, 10n));
        // throws error
        t.throwsAsync(
          () =>
            quatloosIssuer.splitMany(
              anotherQuatloosPayment,
              harden(badQuatloosAmounts),
            ),
          { message: /rights were not conserved/ },
        );
        // #endregion splitMany
      
        t.is(arrayOfNewPayments.length, 10);
        t.deepEqual(await quatloosIssuer.getAmountOf(arrayOfNewPayments[0]), {
          value: 10n,
          brand: quatloosBrand,
        });
      
        const payment = quatloosMint.mintPayment(
          AmountMath.make(quatloosBrand, 100n),
        );
        // #region isLive
        const isItLive = quatloosIssuer.isLive(payment);
        // #endregion isLive
        t.truthy(isItLive);
      
        // #region getIssuer
        const quatloosMintIssuer = quatloosMint.getIssuer();
        // returns true
        const sameIssuer = quatloosIssuer === quatloosMintIssuer;
        // #endregion
      
        t.truthy(sameIssuer);
      
        // #region isMyIssuer
        const isIssuer = quatloosBrand.isMyIssuer(quatloosIssuer);
        // #endregion isMyIssuer
      
        t.truthy(isIssuer);
      });
      
      test('ertp guide issuers and mints mint.getIssuer', async t => {
        // #region mintGetIssuer
        const { issuer: quatloosIssuer, mint: quatloosMint } = makeIssuerKit(
          'quatloos',
        );
        const quatloosMintIssuer = quatloosMint.getIssuer();
        // returns true
        const sameIssuer = quatloosIssuer === quatloosMintIssuer;
        // #endregion mintGetIssuer
        t.truthy(sameIssuer);
      });
      
      test('ertp guide issuers and mints mint.mintPayment', async t => {
        // #region mintMintPayment
        const { mint: quatloosMint, brand: quatloosBrand } = makeIssuerKit(
          'quatloos',
        );
        const quatloos1000 = AmountMath.make(quatloosBrand, 1000n);
        const newPayment = quatloosMint.mintPayment(quatloos1000);
        // #endregion mintMintPayment
      
        const issuer = quatloosMint.getIssuer();
        t.truthy(issuer.isLive(newPayment));
      });
      
      test('ertp guide mints makeIssuerKit', async t => {
        // #region makeIssuerKitMint
        const {
          issuer: quatloosIssuer,
          mint: quatloosMint,
          brand: quatloosBrand,
        } = makeIssuerKit('quatloos');
        // Mint a new 2 Quatloos payment
        const paymentQuatloos2 = quatloosMint.mintPayment(
          AmountMath.make(quatloosBrand, 2n),
        );
        // #endregion makeIssuerMint
        t.truthy(quatloosIssuer.isLive(paymentQuatloos2));
        t.truthy(quatloosIssuer);
        t.truthy(quatloosMint);
        t.truthy(quatloosBrand);
      });