diff --git a/main/guides/zoe/assets/trade-offer-safety-1.mmd b/main/guides/zoe/assets/trade-offer-safety-1.mmd
index f0eb07c8b..d5af83605 100644
--- a/main/guides/zoe/assets/trade-offer-safety-1.mmd
+++ b/main/guides/zoe/assets/trade-offer-safety-1.mmd
@@ -10,9 +10,9 @@ sequenceDiagram
end
box skyblue Contract Instance
- participant C as gameAssetContract
+ participant C as offer-up.contract
end
A-)Zoe: getPublicFacet(instance)
A-)+Zoe: getTerms(instance)
- Zoe--)-A: { issuers, brands, joinPrice }
+ Zoe--)-A: { issuers, brands, tradePrice }
diff --git a/main/guides/zoe/assets/trade-offer-safety-1.svg b/main/guides/zoe/assets/trade-offer-safety-1.svg
index 7b70d2a53..e08405e5c 100644
--- a/main/guides/zoe/assets/trade-offer-safety-1.svg
+++ b/main/guides/zoe/assets/trade-offer-safety-1.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/main/guides/zoe/assets/trade-offer-safety-2.mmd b/main/guides/zoe/assets/trade-offer-safety-2.mmd
index 51afaeff5..3c95ecf26 100644
--- a/main/guides/zoe/assets/trade-offer-safety-2.mmd
+++ b/main/guides/zoe/assets/trade-offer-safety-2.mmd
@@ -10,9 +10,9 @@ sequenceDiagram
end
box skyblue Contract
- participant C as gameAssetContract
+ participant C as offer-up.contract
end
- A-)C: makeJoinInvitation()
+ A-)C: makeTradeInvitation()
A-)Zoe: offer(toJoin, proposal, { Price })
- A-)Zoe: E(seat).getPayout('Places')
+ A-)Zoe: E(seat).getPayout('Items')
diff --git a/main/guides/zoe/assets/trade-offer-safety-2.svg b/main/guides/zoe/assets/trade-offer-safety-2.svg
index 4ab44c82f..4edeba19e 100644
--- a/main/guides/zoe/assets/trade-offer-safety-2.svg
+++ b/main/guides/zoe/assets/trade-offer-safety-2.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/main/guides/zoe/assets/trade-offer-safety-3.mmd b/main/guides/zoe/assets/trade-offer-safety-3.mmd
index 25eb5b26a..5b215ef3f 100644
--- a/main/guides/zoe/assets/trade-offer-safety-3.mmd
+++ b/main/guides/zoe/assets/trade-offer-safety-3.mmd
@@ -10,12 +10,12 @@ sequenceDiagram
end
box skyblue Contract
- participant C as gameAssetContract
+ participant C as offer-up.contract
end
- A-)C: makeJoinInvitation()
+ A-)C: makeTradeInvitation()
activate C
- C--)Zoe: makeInvitation(joinHandler, ...)
+ C--)Zoe: makeInvitation(tradeHandler, ...)
deactivate C
activate Zoe
Zoe--)-C: invitation
@@ -26,4 +26,4 @@ sequenceDiagram
Zoe--)Zoe: escrow Price pmt
- Zoe--)-C: joinHandler(gameSeat)
+ Zoe--)-C: tradeHandler(buyerSeat)
diff --git a/main/guides/zoe/assets/trade-offer-safety-3.svg b/main/guides/zoe/assets/trade-offer-safety-3.svg
index 8323004f0..6a6ef2f24 100644
--- a/main/guides/zoe/assets/trade-offer-safety-3.svg
+++ b/main/guides/zoe/assets/trade-offer-safety-3.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/main/guides/zoe/assets/trade-offer-safety-4.mmd b/main/guides/zoe/assets/trade-offer-safety-4.mmd
index e3a07e24e..7297bdc7f 100644
--- a/main/guides/zoe/assets/trade-offer-safety-4.mmd
+++ b/main/guides/zoe/assets/trade-offer-safety-4.mmd
@@ -10,10 +10,10 @@ sequenceDiagram
end
box skyblue Contract
- participant C as gameAssetContract
+ participant C as offer-up.contract
end
- Zoe--)C: joinHandler(gameSeat)
+ Zoe--)C: tradeHandler(buyerSeat)
activate C
C--)C: check proposal
C--)Zoe: mintGains(want)
@@ -25,4 +25,4 @@ sequenceDiagram
Zoe--)Zoe: check offer safety
- C--)Zoe: playerSeat.exit(true)
\ No newline at end of file
+ C--)Zoe: buyerSeat.exit(true)
\ No newline at end of file
diff --git a/main/guides/zoe/assets/trade-offer-safety-4.svg b/main/guides/zoe/assets/trade-offer-safety-4.svg
index eb0b82e51..91e7d0bfb 100644
--- a/main/guides/zoe/assets/trade-offer-safety-4.svg
+++ b/main/guides/zoe/assets/trade-offer-safety-4.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/main/guides/zoe/assets/trade-offer-safety-5.mmd b/main/guides/zoe/assets/trade-offer-safety-5.mmd
index f8fc74ca8..a814c8319 100644
--- a/main/guides/zoe/assets/trade-offer-safety-5.mmd
+++ b/main/guides/zoe/assets/trade-offer-safety-5.mmd
@@ -10,11 +10,11 @@ sequenceDiagram
end
box skyblue Contract
- participant C as gameAssetContract
+ participant C as offer-up.contract
end
- A-)+Zoe: E(seat).getPayout('Places')
+ A-)+Zoe: E(seat).getPayout('Items')
Note over Zoe: ... many steps above ...
- Zoe--)-A: placesPayment
- A-)+Zoe: E(issuers.Place).getAmountOf(placesPayment)
- Zoe--)-A: { brand: Place brand, value: [['Park Place, 1n], ['Boardwalk', 1n]]
+ Zoe--)-A: itemsPayment
+ A-)+Zoe: E(issuers.Items).getAmountOf(itemsPayment)
+ Zoe--)-A: { brand: Item brand, value: [['map', 1n], ['scroll', 1n]]
diff --git a/main/guides/zoe/assets/trade-offer-safety-5.svg b/main/guides/zoe/assets/trade-offer-safety-5.svg
index 70b42dfe6..468fc5a6b 100644
--- a/main/guides/zoe/assets/trade-offer-safety-5.svg
+++ b/main/guides/zoe/assets/trade-offer-safety-5.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/main/guides/zoe/contract-upgrade.md b/main/guides/zoe/contract-upgrade.md
index bb2cd68e0..a3e1b5bc9 100644
--- a/main/guides/zoe/contract-upgrade.md
+++ b/main/guides/zoe/contract-upgrade.md
@@ -9,7 +9,7 @@ The `adminFacet` is defined by Zoe and includes methods to upgrade the contract.
Governance of the right to upgrade is a complex topic that we cover only briefly here.
- When [BLD staker governance](https://community.agoric.com/t/about-the-governance-category/15) makes a decision to start a contract using [swingset.CoreEval](../coreeval/),
- to date, the `adminFacet` is stored in the bootstrap vat, allowing
+ to date, the `adminFacet` is stored in the bootstrap [vat](/glossary/#vat), allowing
the BLD stakers to upgrade such a contract in a later `swingset.CoreEval`.
- The `adminFacet` reference can be discarded, so that noone can upgrade
the contract from within the JavaScript VM. (BLD staker governace
diff --git a/main/guides/zoe/contract-walkthru.md b/main/guides/zoe/contract-walkthru.md
index 1cda82ec2..9eaa4c6df 100644
--- a/main/guides/zoe/contract-walkthru.md
+++ b/main/guides/zoe/contract-walkthru.md
@@ -96,9 +96,9 @@ t.is(typeof installation, 'object');
The `installation` identifies the basic contract that we'll
go over in detail in the sections below.
-::: details gameAssetContract.js listing
+::: details offer-up.contract.js listing
-<<< @/../snippets/zoe/src/gameAssetContract.js#file
+<<< @/../snippets/zoe/src/offer-up.contract.js#file
:::
@@ -113,7 +113,7 @@ yarn ava test/test-contract.js -m 'Start the contract'
```
✔ Start the contract (652ms)
ℹ terms: {
- joinPrice: {
+ tradePrice: {
brand: Object @Alleged: PlayMoney brand {},
value: 5n,
},
@@ -130,7 +130,7 @@ the contract should use for its business:
```js{8}
const money = makeIssuerKit('PlayMoney');
const issuers = { Price: money.issuer };
-const terms = { joinPrice: AmountMath.make(money.brand, 5n) };
+const terms = { tradePrice: AmountMath.make(money.brand, 5n) };
t.log('terms:', terms);
/** @type {ERef>} */
@@ -146,14 +146,14 @@ _See also [E(zoe).startInstance(...)](/reference/zoe-api/zoe.md#e-zoe-startinsta
Let's take a look at what happens in the contract when it starts. A _facet_ of Zoe, the _Zoe Contract Facet_, is passed to the contract `start` function.
The contract uses this `zcf` to get its terms. Likewise it uses `zcf` to
-make a `gameSeat` where it can store assets that it receives in trade
-as well as a `mint` for making assets consisting of collections (bags) of Places:
+make a `proceeds` seat where it can store assets that it receives in trade
+as well as a `mint` for making assets consisting of collections (bags) of Items:
-<<< @/../snippets/zoe/src/gameAssetContract.js#start
+<<< @/../snippets/zoe/src/offer-up.contract.js#start
-It defines a `joinShape` and `joinHandler` but doesn't do anything with them yet. They will come into play later. It defines and returns its `publicFacet` and stands by.
+It defines a `proposalShape` and `tradeHandler` but doesn't do anything with them yet. They will come into play later. It defines and returns a [hardened](/glossary/#harden) `publicFacet` object and stands by.
-<<< @/../snippets/zoe/src/gameAssetContract.js#started
+<<< @/../snippets/zoe/src/offer-up.contract.js#started
## Trading with Offer Safety
@@ -164,7 +164,7 @@ yarn ava test/test-contract.js -m 'Alice trades*'
```
```
- ✔ Alice trades: give some play money, want some game places (674ms)
+ ✔ Alice trades: give some play money, want items (309ms)
ℹ Object @Alleged: InstanceHandle {}
ℹ Alice gives {
Price: {
@@ -172,15 +172,15 @@ yarn ava test/test-contract.js -m 'Alice trades*'
value: 5n,
},
}
- ℹ Alice payout brand Object @Alleged: Place brand {}
+ ℹ Alice payout brand Object @Alleged: Item brand {}
ℹ Alice payout value Object @copyBag {
payload: [
[
- 'Park Place',
+ 'scroll',
1n,
],
[
- 'Boardwalk',
+ 'map',
1n,
],
],
@@ -209,14 +209,14 @@ Alice starts by using the `instance` to get the contract's `publicFacet` and `te
<<< @/../snippets/zoe/contracts/alice-trade.js#queryInstance
-Then she constructs a _proposal_ to give the `joinPrice` in exchange
-for 1 Park Place and 1 Boardwalk, denominated in the game's `Place` brand; and she withdraws a payment from her purse:
+Then she constructs a _proposal_ to give the `tradePrice` in exchange
+for 1 map and 1 scroll, denominated in the game's `Item` brand; and she withdraws a payment from her purse:
<<< @/../snippets/zoe/contracts/alice-trade.js#makeProposal
She then requests an _invitation_ to join the game; makes an _offer_ with
(a promise for) this invitation, her proposal, and her payment;
-and awaits her **Places** payout:
+and awaits her **Items** payout:
@@ -241,33 +241,33 @@ when you are [creating an instance](#starting-a-contract-instance) or by using
:::
-The contract gets Alice's `E(publicFacet).makeJoinInvitation()` call and uses `zcf` to make an invitation with an associated handler, description, and proposal shape. Zoe gets Alice's `E(zoe).offer(...)` call, checks the proposal against the proposal shape, escrows the payment, and invokes the handler.
+The contract gets Alice's `E(publicFacet).makeTradeInvitation()` call and uses `zcf` to make an invitation with an associated handler, description, and proposal shape. Zoe gets Alice's `E(zoe).offer(...)` call, checks the proposal against the proposal shape, escrows the payment, and invokes the handler.
-<<< @/../snippets/zoe/src/gameAssetContract.js#makeInvitation
+<<< @/../snippets/zoe/src/offer-up.contract.js#makeInvitation
The offer handler is invoked with a _seat_ representing the party making the offer.
It extracts the `give` and `want` from the party's offer and checks that
-they are giving at least the `joinPrice` and not asking for too many
-places in return.
+they are giving at least the `tradePrice` and not asking for too many
+items in return.
With all these prerequisites met, the handler instructs `zcf` to mint the requested
-**Place** assets, allocate what the player is giving into its own `gameSeat`,
-and allocate the minted places to the player. Finally, it concludes its business with the player.
+**Item** assets, allocate what the player is giving into its own `proceeds` seat,
+and allocate the minted items to the player. Finally, it concludes its business with the player.
-<<< @/../snippets/zoe/src/gameAssetContract.js#handler
+<<< @/../snippets/zoe/src/offer-up.contract.js#handler
Zoe checks that the contract's instructions are consistent with
the offer and with conservation of assets. Then it allocates
-the escrowed payment to the contract's gameSeat and pays out
+the escrowed payment to the contract's proceeds seat and pays out
the place NFTs to Alice in response to the earlier `getPayout(...)` call.
-Alice asks the `Place` issuer what her payout is worth
+Alice asks the `Item` issuer what her payout is worth
and tests that it's what she wanted.
{
// #region queryInstance
const publicFacet = E(zoe).getPublicFacet(instance);
const terms = await E(zoe).getTerms(instance);
- const { issuers, brands, joinPrice } = terms;
+ const { issuers, brands, tradePrice } = terms;
// #endregion queryInstance
// #region makeProposal
- const choices = ['Park Place', 'Boardwalk'];
+ const choices = ['map', 'scroll'];
const choiceBag = makeCopyBag(choices.map(name => [name, 1n]));
const proposal = {
- give: { Price: joinPrice },
- want: { Places: AmountMath.make(brands.Place, choiceBag) },
+ give: { Price: tradePrice },
+ want: { Places: AmountMath.make(brands.Item, choiceBag) },
};
- const Price = await E(purse).withdraw(joinPrice);
+ const Price = await E(purse).withdraw(tradePrice);
t.log('Alice gives', proposal.give);
// #endregion makeProposal
// #region trade
- const toJoin = E(publicFacet).makeJoinInvitation();
+ const toJoin = E(publicFacet).makeTradeInvitation();
const seat = E(zoe).offer(toJoin, proposal, { Price });
- const places = await E(seat).getPayout('Places');
+ const items = await E(seat).getPayout('Items');
// #endregion trade
// #region payouts
- const actual = await E(issuers.Place).getAmountOf(places);
+ const actual = await E(issuers.Item).getAmountOf(items);
t.log('Alice payout brand', actual.brand);
t.log('Alice payout value', actual.value);
- t.deepEqual(actual, proposal.want.Places);
+ t.deepEqual(actual, proposal.want.Items);
// #endregion payouts
};
diff --git a/snippets/zoe/contracts/test-bundle-source.js b/snippets/zoe/contracts/test-bundle-source.js
index ae7258ee9..53e93318e 100644
--- a/snippets/zoe/contracts/test-bundle-source.js
+++ b/snippets/zoe/contracts/test-bundle-source.js
@@ -17,7 +17,7 @@ import { makeZoeKitForTest } from '@agoric/zoe/tools/setup-zoe.js';
// #region contractPath
const myRequire = createRequire(import.meta.url);
-const contractPath = myRequire.resolve(`../src/gameAssetContract.js`);
+const contractPath = myRequire.resolve(`../src/offer-up.contract.js`);
// #endregion contractPath
test('bundleSource() bundles the contract for use with zoe', async t => {
diff --git a/snippets/zoe/src/gameAssetContract.js b/snippets/zoe/src/gameAssetContract.js
deleted file mode 100644
index 80ad5b174..000000000
--- a/snippets/zoe/src/gameAssetContract.js
+++ /dev/null
@@ -1,80 +0,0 @@
-// #region file
-/** @file Contract to mint and sell Place NFTs for a hypothetical game. */
-// @ts-check
-
-import { Far } from '@endo/far';
-import { M, getCopyBagEntries } from '@endo/patterns';
-import { AmountMath, AssetKind } from '@agoric/ertp/src/amountMath.js';
-import { AmountShape } from '@agoric/ertp/src/typeGuards.js';
-import { atomicRearrange } from '@agoric/zoe/src/contractSupport/atomicTransfer.js';
-import '@agoric/zoe/exported.js';
-
-import { makeTracer } from './debug.js';
-
-const { Fail, quote: q } = assert;
-
-const trace = makeTracer('Game', true);
-
-/** @param {Amount<'copyBag'>} amt */
-const bagValueSize = amt => {
- /** @type {[unknown, bigint][]} */
- const entries = getCopyBagEntries(amt.value); // XXX getCopyBagEntries returns any???
- const total = entries.reduce((acc, [_place, qty]) => acc + qty, 0n);
- return total;
-};
-
-/**
- * @param {ZCF<{joinPrice: Amount}>} zcf
- */
-// #region start
-export const start = async zcf => {
- const { joinPrice } = zcf.getTerms();
-
- const { zcfSeat: gameSeat } = zcf.makeEmptySeatKit();
- const mint = await zcf.makeZCFMint('Place', AssetKind.COPY_BAG);
- // #endregion start
-
- // #region handler
- /** @param {ZCFSeat} playerSeat */
- const joinHandler = playerSeat => {
- const { give, want } = playerSeat.getProposal();
- trace('join', 'give', give, 'want', want.Places.value);
-
- AmountMath.isGTE(give.Price, joinPrice) ||
- Fail`${q(give.Price)} below joinPrice of ${q(joinPrice)}}`;
-
- bagValueSize(want.Places) <= 3n || Fail`only 3 places allowed when joining`;
-
- const tmp = mint.mintGains(want);
- atomicRearrange(
- zcf,
- harden([
- [playerSeat, gameSeat, give],
- [tmp, playerSeat, want],
- ]),
- );
-
- playerSeat.exit(true);
- return 'welcome to the game';
- };
- // #endregion handler
-
- // #region makeInvitation
- const joinShape = harden({
- give: { Price: AmountShape },
- want: { Places: AmountShape },
- exit: M.any(),
- });
-
- const publicFacet = Far('API', {
- makeJoinInvitation: () =>
- zcf.makeInvitation(joinHandler, 'join', undefined, joinShape),
- });
- // #endregion makeInvitation
-
- // #region started
- return { publicFacet };
- // #endregion started
-};
-harden(start);
-// #endregion file
diff --git a/snippets/zoe/src/offer-up.contract.js b/snippets/zoe/src/offer-up.contract.js
new file mode 100644
index 000000000..9462e92ff
--- /dev/null
+++ b/snippets/zoe/src/offer-up.contract.js
@@ -0,0 +1,97 @@
+// #region file
+/** @file Contract to mint and sell a few Item NFTs at a time. */
+// @ts-check
+
+import { Far } from '@endo/far';
+import { M, getCopyBagEntries } from '@endo/patterns';
+import { AssetKind } from '@agoric/ertp/src/amountMath.js';
+import { atomicRearrange } from '@agoric/zoe/src/contractSupport/atomicTransfer.js';
+import '@agoric/zoe/exported.js';
+
+const { Fail, quote: q } = assert;
+
+// #region bagUtils
+/** @type { (xs: bigint[]) => bigint } */
+const sum = xs => xs.reduce((acc, x) => acc + x, 0n);
+
+/**
+ * @param {import('@endo/patterns').CopyBag} bag
+ * @returns {bigint[]}
+ */
+const bagCounts = bag => {
+ const entries = getCopyBagEntries(bag);
+ return entries.map(([_k, ct]) => ct);
+};
+// #endregion bagUtils
+
+/**
+ * In addition to the standard `issuers` and `brands` terms,
+ * this contract is parameterized by terms for price and,
+ * optionally, a maximum number of items sold for that price (default: 3).
+ *
+ * @typedef {{
+ * tradePrice: Amount;
+ * maxItems?: bigint;
+ * }} OfferUpTerms
+ */
+
+// #region start
+/** @param {ZCF} zcf */
+export const start = async zcf => {
+ const { tradePrice, maxItems = 3n } = zcf.getTerms();
+
+ const itemMint = await zcf.makeZCFMint('Item', AssetKind.COPY_BAG);
+ // #endregion start
+ const { brand: itemBrand } = itemMint.getIssuerRecord();
+
+ /** a seat for allocating proceeds of sales */
+ const proceeds = zcf.makeEmptySeatKit().zcfSeat;
+ // #region handler
+ /** @type {OfferHandler} */
+ const tradeHandler = buyerSeat => {
+ // give and want are guaranteed by Zoe to match proposalShape
+ const { want } = buyerSeat.getProposal();
+
+ sum(bagCounts(want.Items.value)) <= maxItems ||
+ Fail`max ${q(maxItems)} items allowed: ${q(want.Items)}`;
+
+ const newItems = itemMint.mintGains(want);
+ atomicRearrange(
+ zcf,
+ harden([
+ // price from buyer to proceeds
+ [buyerSeat, proceeds, { Price: tradePrice }],
+ // new items to buyer
+ [newItems, buyerSeat, want],
+ ]),
+ );
+
+ buyerSeat.exit(true);
+ newItems.exit();
+ return 'trade complete';
+ };
+ // #endregion handler
+
+ // #region makeInvitation
+ const proposalShape = harden({
+ give: { Price: M.gte(tradePrice) },
+ want: { Items: { brand: itemBrand, value: M.bag() } },
+ exit: M.any(),
+ });
+
+ const makeTradeInvitation = () =>
+ zcf.makeInvitation(tradeHandler, 'buy items', undefined, proposalShape);
+
+ // Mark the publicFacet Far, i.e. reachable from outside the contract
+ const publicFacet = Far('Items Public Facet', {
+ makeTradeInvitation,
+ });
+ // #endregion makeInvitation
+
+ // #region started
+ return harden({ publicFacet });
+ // #endregion started
+};
+
+harden(start);
+// #endregion file