diff --git a/main/glossary/index.md b/main/glossary/index.md index 946922bb9..b8be06b4d 100644 --- a/main/glossary/index.md +++ b/main/glossary/index.md @@ -14,13 +14,11 @@ A short form of [agoric-3-proposals](https://github.com/Agoric/agoric-3-proposal A command line interface for initializing, deploying, and starting Agoric projects, as well as installing dependencies. See the [Agoric CLI documentation](/guides/agoric-cli/) for more information. -## AllegedName +## Alleged -Human-readable name of a type of assets. The alleged name should -not be trusted as an accurate depiction, since it is provided by -the maker of the mint and could be deceptive, but is useful for debugging and double-checking. + -The AllegedName must be a string. +See [DebugName](#debugname). ## Allocation @@ -210,6 +208,20 @@ An [invitation](#invitation) optionally returned by [`E(zoe).startInstance(...)` creator can use. It is usually used in contracts where the creator immediately sells something (auctions, swaps, etc.). +## DebugName + +A label for debugging / diagnostic purposes. aka Alleged name. +Since debug names may be misleading, +**no code should rely on an Alleged / DebugName for correctness.** + +See: + +- [Exo tags](/guides/zoe/contract-details#tag-naming-kinds-of-objects), + which are used as debug names. +- [ERTP](/guides/ertp/) where Brand objects, not string names, + are used to reliably identify digital assets. +- [Remotable in @endo/pass-style](https://endojs.github.io/endo/functions/_endo_pass_style.Remotable.html), where labels are bound to objects. + ## Deposit Facet A [facet](#facet) of a [purse](#purse). Anyone with a reference to its deposit facet object can add diff --git a/main/guides/ertp/assets/alice-bob-ticket.png b/main/guides/ertp/assets/alice-bob-ticket.png new file mode 100644 index 000000000..8fbf1aaa3 Binary files /dev/null and b/main/guides/ertp/assets/alice-bob-ticket.png differ diff --git a/main/guides/ertp/assets/ertp-interfaces-1.mmd b/main/guides/ertp/assets/ertp-interfaces-1.mmd index 2ffcdbcd2..466e61f38 100644 --- a/main/guides/ertp/assets/ertp-interfaces-1.mmd +++ b/main/guides/ertp/assets/ertp-interfaces-1.mmd @@ -13,6 +13,12 @@ classDiagram } ertp --> IssuerKit : makeIssuerKit + class IssuerKit { + mint: Mint + issuer: Issuer + brand: Brand + } + class Mint { getIssuer() } diff --git a/main/guides/ertp/assets/ertp-interfaces-1.svg b/main/guides/ertp/assets/ertp-interfaces-1.svg index 2c916b5df..7c7f4c4a8 100644 --- a/main/guides/ertp/assets/ertp-interfaces-1.svg +++ b/main/guides/ertp/assets/ertp-interfaces-1.svg @@ -1 +1 @@ -ERTP: Mint, Issuer, Brand
1
1
1
1
1
1
makeIssuerKit
getIssuer
getBrand
IssuerKit
Mint
getIssuer()
Issuer
getBrand()
Brand
@agoric/ertp
...
makeIssuerKit() : IssuerKit
ERTP: Mint, Issuer, Brand
\ No newline at end of file +ERTP: Mint, Issuer, Brand
1
1
1
1
1
1
makeIssuerKit
getIssuer
getBrand
IssuerKit
mint: Mint
issuer: Issuer
brand: Brand
Mint
getIssuer()
Issuer
getBrand()
Brand
@agoric/ertp
...
makeIssuerKit() : IssuerKit
ERTP: Mint, Issuer, Brand
\ No newline at end of file diff --git a/main/guides/ertp/index.md b/main/guides/ertp/index.md index b53abf4a5..db2607f82 100644 --- a/main/guides/ertp/index.md +++ b/main/guides/ertp/index.md @@ -1,7 +1,134 @@ # ERTP Overview -ERTP (_Electronic Rights Transfer Protocol_) -is Agoric's token standard for transferring tokens and other digital assets in +ERTP (_Electronic Rights Transfer Protocol_) is Agoric's digital asset standard. + +ERTP is a uniform way of transferring tokens and other digital assets in JavaScript. All kinds of digital assets can be easily created, but importantly, they can be transferred in exactly the same ways, with exactly the same security properties. + +For example, let's suppose Alice wants to buy a concert ticket from Bob for 10 bucks. + +::: tip Watch: erights -- credibly transferable ownership (Oct 2024) +_25 minutes on ERTP at a conceptual level._ +
+ +[diagram of Alice, Bob, Ticket objects](https://www.youtube.com/watch?v=O8Bx_Abj9Qc&list=PLzDw4TTug5O1A-tkPJe4HVq0VBPcNOMHm) + +::: + +## Issuer, Brand, and Mint + +We start by using `makeIssuerKit` to make a `Mint`, `Brand`, and `Issuer` for **Bucks**. + +<<< @/../snippets/ertp/guide/test-readme.js#importErtp +<<< @/../snippets/ertp/guide/test-readme.js#declareShared +<<< @/../snippets/ertp/guide/test-readme.js#makeBucks + +The `bucks.brand` and `bucks.issuer` don't let anyone mint new assets, so sharing +them widely is normal. We must be careful to guard the`bucksMint`, so we keep it separate. + +::: tip see also ZCFMint (TODO) + +... + +::: + +## Amount: Asset Descriptions + +Next we combine the Bucks brand with a value to make an `Amount`: + +<<< @/../snippets/ertp/guide/test-readme.js#bucksAmount + +An Amount is a value labeled with a brand. +Amounts are descriptions of digital assets, +answering the questions "how much" and "of what kind". + +Amounts have no economic scarcity or intrinsic value. + +:::tip More on Asset Use versus Mention +_See also [The Settlers of Blockchain](https://agoric.com/blog/technology/the-settlers-of-blockchain) by Chris Hibbert, Jun 2021_ +::: + +## Minting Payments + +Next we use the mint to make a `Payment` of 100 bucks for Alice: + +<<< @/../snippets/ertp/guide/test-readme.js#bucksPayment100 + +Likewise, we make a **Tickets** issuer kit make payments of 10 **Tickets** and 100 **Bucks** +for Bob. + +<<< @/../snippets/ertp/guide/test-readme.js#amountMathProps +<<< @/../snippets/ertp/guide/test-readme.js#bobPayments + +Where Amounts only describe assets, Payments actually convey digital assets/rights. +Sending Payments must be done very carefully. + +## Making Purses + +Alice is acting as a buyer. +She can make her own empty purses using the shared issuers, which she relies on. +She depsits some **Bucks** that she is given into her Bucks purse. + +<<< @/../snippets/ertp/guide/test-readme.js#aliceBuyer1 + +Purses also hold digital assets/rights. +**Purses are normally not sent betwen parties.** + +## Credible Asset Transfer + +To buy a ticket, she withdraws a payment of 10 bucks and make a `buy` request +to some vendor she was given. + +<<< @/../snippets/ertp/guide/test-readme.js#aliceBuyer2 + +The seller has likewise created purses for **Bucks** and **Tickets** and made deposits. +When they get a `buy` request, the argument may be anything, so it's called `allegedPayment`. +But once they deposit it into their Bucks purse, they know it was +a valid Bucks payment, and they know the amount. +Provided the amount is sufficient, they withdraw a ticket (payment) and return it. + +<<< @/../snippets/ertp/guide/test-readme.js#bobSeller + +Now our buyer has an `allegedTicket`. +Once she deposits it in her **Tickets** purse, she knows it was +a valid payment and she knows its value. She can check that she +got at least 1 ticket. + +<<< @/../snippets/ertp/guide/test-readme.js#aliceBuyer3 + +To put it all together: + +<<< @/../snippets/ertp/guide/test-readme.js#aliceBuysFromBob + +## Non-Fungible and Semi-Fungible Assets + +::: tip: TODO: Non-Fungible and Semi-Fungible Assets + +... + +::: + +## ERTP Concepts Overview + +Each digital asset has Mint, Issuer, and Brand facets: + +![ERTP Interfaces 1](./assets/ertp-interfaces-1.svg){ width=200 height=200 } + +Use brands to make amounts. + +Use a Mint to create Payments. Use an Issuer to make Purses. +Deposit payments into purses and withdraw them back out. + +![ERTP makeIssuerKit API](./assets/ertp-interfaces-2.svg) + +Fungible and non-fungible kinds of assets are handled uniformly. + +![ERTP object relationships](./assets/ertp-interfaces-3.svg) + +# Obsolete material + +_aside from TODOs above_ + +token standard for transferring tokens and other digital assets in JavaScript. Using the [ERTP API](/reference/ertp-api/), you can easily create and use digital assets, all of which are transferred exactly the same way and with exactly the same security properties. @@ -12,8 +139,6 @@ object, it can call methods on that object. If it doesn't have a reference, it can't. For more on object capabilities, see [Chip Morningstar's post](http://habitatchronicles.com/2017/05/what-are-capabilities/). -## ERTP Concepts Overview - ### Asset There are three kinds of assets: diff --git a/snippets/ertp/guide/test-readme.js b/snippets/ertp/guide/test-readme.js index ae68e04b1..19c088fc2 100644 --- a/snippets/ertp/guide/test-readme.js +++ b/snippets/ertp/guide/test-readme.js @@ -1,5 +1,6 @@ /* eslint-disable import/order -- https://github.com/endojs/endo/issues/1235 */ import { test } from '../../prepare-test-env-ava.js'; +// @ts-check import { E } from '@endo/eventual-send'; @@ -8,7 +9,9 @@ import { E } from '@endo/eventual-send'; // eslint-disable-next-line import/no-extraneous-dependencies import { makeFakeBoard } from '@agoric/vats/tools/board-utils.js'; -import { AmountMath, makeIssuerKit, AssetKind } from '@agoric/ertp'; +// #region importErtp +import { makeIssuerKit, AmountMath, AssetKind } from '@agoric/ertp'; +// #endregion importErtp test('ertp guide readme', async t => { // #region makeIssuerKit @@ -100,3 +103,115 @@ test('ertp guide readme', async t => { t.truthy(allLive.every(a => a)); }); + +test('MarkM 2024-10 talk', t => { + /** @type {Record} */ + + // #region amountMathProps + const { make, isGTE } = AmountMath; + // #endregion amountMathProps + + // #region declareShared + const shared = {}; + // #endregion declareShared + + // #region aliceBuyer1 + /** @param {{ bucks: Payment, vendor: any }} some */ + const makeBuyer = some => { + const { bucks, tickets } = shared; + const my = { + bucks: bucks.issuer.makeEmptyPurse(), + tickets: tickets.issuer.makeEmptyPurse(), + }; + my.bucks.deposit(some.bucks); + // #endregion aliceBuyer1 + + // #region aliceBuyer2 + return harden({ + buyTicket() { + const pmt = my.bucks.withdraw(make(bucks.brand, 10n)); + const allegedTicket = some.vendor.buy(pmt); + // #endregion aliceBuyer2 + // #region aliceBuyer3 + const got = my.tickets.deposit(allegedTicket); + t.log('Alice got', got); + isGTE(got, make(tickets.brand, 1n)) || assert.fail(); + return got; + }, + getBalances: () => ({ + bucks: my.bucks.getCurrentAmount(), + tickets: my.tickets.getCurrentAmount(), + }), + }); + }; + // #endregion aliceBuyer3 + + // #region bobSeller + /** @param {{ bucks: Payment, tickets: Payment}} some */ + const makeSeller = some => { + const { bucks, tickets } = shared; + const my = { + bucks: bucks.issuer.makeEmptyPurse(), + tickets: tickets.issuer.makeEmptyPurse(), + }; + my.bucks.deposit(some.bucks); + my.tickets.deposit(some.tickets); + + return harden({ + /** @param {Payment} allegedPayment */ + buy(allegedPayment) { + const amt = my.bucks.deposit(allegedPayment); + isGTE(amt, make(bucks.brand, 10n)) || assert.fail(); + t.log('Bob got', amt); + return my.tickets.withdraw(make(tickets.brand, 1n)); + }, + getBalances: () => ({ + bucks: my.bucks.getCurrentAmount(), + tickets: my.tickets.getCurrentAmount(), + }), + }); + }; + // #endregion bobSeller + + // #region makeBucks + const bucksKit = makeIssuerKit('Bucks'); + const { mint: bucksMint, ...bucks } = bucksKit; + Object.assign(shared, { bucks }); + // #endregion makeBucks + + // #region bucksAmount + const bucks100 = AmountMath.make(bucks.brand, 100n); + // #endregion bucksAmount + + // #region bucksPayment100 + const paymentA = bucksMint.mintPayment(bucks100); + // #endregion bucksPayment100 + + // #region bobPayments + const { mint: ticketsMint, ...tickets } = makeIssuerKit('Tickets'); + Object.assign(shared, { tickets }); + + const paymentsB = { + bucks: bucksMint.mintPayment(make(bucks.brand, 200n)), + tickets: ticketsMint.mintPayment(make(tickets.brand, 50n)), + }; + // #endregion bobPayments + + // #region aliceBuysFromBob + const bob = makeSeller(paymentsB); + const alice = makeBuyer({ bucks: paymentA, vendor: bob }); + + const howMuch = (bv, tv) => ({ + bucks: make(bucks.brand, bv), + tickets: make(tickets.brand, tv), + }); + + t.deepEqual(alice.getBalances(), howMuch(100n, 0n)); + t.deepEqual(bob.getBalances(), howMuch(200n, 50n)); + + alice.buyTicket(); + + t.deepEqual(alice.getBalances(), howMuch(90n, 1n)); + t.deepEqual(bob.getBalances(), howMuch(210n, 49n)); + // #endregion aliceBuysFromBob +});