Skip to content

Commit

Permalink
feat(zoe): Zoe release 0.7.0 (#1143)
Browse files Browse the repository at this point in the history
## Release v0.7.0 (29-June-2020)

Zoe Service changes:

* Instead of `zoe.makeInstance` returning an invite only, it now
  returns a record of `{ invite, instanceRecord }` such that
  information like the `instanceHandle` can be obtained directly from
  the instanceRecord. 
* `installationHandle`, the identifier for the code that is used to
  create a new running contract instance, is added to the extent of
  invites for contracts so that interested parties can easily check
  whether their invite is using the code they expect.
* `brandKeywordRecord` is added as a property of `instanceRecord`
  alongside `issuerKeywordRecord`. `brandKeywordRecord` is an object
  with keyword keys and brand values.
* `zoe.makeContract` now only accepts a single argument (`bundle`) and
  the old module format will error.


Zoe Contract Facet (zcf) changes:
* `zcf.reallocate` no longer accepts a third argument `sparseKeywords`
  and no longer expects the keywords for different offers to be the
  same. Within reallocate, the offer safety check compares the user's
  proposal to the user's allocation, and the rights conservation check
  adds up the amounts by brand to ensure the totals are the same.
  Neither of these checks requires that the keywords for the
  allocations be the same for different offers.
* `zcf.getCurrentAllocation` and `zcf.getCurrentAllocations` no longer
  take sparseKeywords as a parameter. Instead, brandKeywordRecord is
  an optional parameter. If omitted, amounts are returned only for
  brands for which an allocation currently exists.
* `zcf.getInstanceRecord()` no longer takes a parameter
* `brandKeywordRecord` is added as a property of `instanceRecord`
  alongside `issuerKeywordRecord`. `brandKeywordRecord` is an object
  with keyword keys and brand values.
* `zcf.getAmountMaths` has been subsumed by `zcf.getAmountMath` which
  takes a single brand parameter.
* `zcf.getBrandForIssuer` has been added. It synchronously returns the
  brand for a given issuer already known to Zoe.
* `zoe.getInstance`, which was deprecated earlier, has been removed.
* `cancelObj` and `cancelObj.cancel()`, which were deprecated earlier,
  have been removed.

Changes for Zoe contract developers:
* Zoe contracts are now expected to return only an invite as the
  result of `makeContract`. If the contracts want to have a
  `publicAPI`, they can do so through `zcf.initPublicAPI`.
* Contracts can allow different offers to use different keywords for
  the same issuers. For example, publicAuction, the second price
  auction contract, uses 'Asset' and 'Ask'
  for the seller keywords and 'Asset' and 'Bid' for the buyer
  keywords.


Built-in Zoe contract changes:
* We added more comments to the start of the built-in Zoe contracts.
* The operaTickets contract has been split into two contracts: a
  generic `sellItems` contract that sells fungible or nonfungible
  items at a set price for money, and a generic `mintAndSellNFT`
  contract that mints NFT tokens and then immediately creates a new
  `sellItems` instance to sell them. The original operaTicket tests
  are able to use these contracts. 
* The `getCurrentPrice` helper in `bondingCurves.js` has been renamed
  to `getInputPrice` and now only returns the `outputExtent`.
* A new built-in contract was added: barter-exchange.js. Barter
  Exchange takes offers for trading arbitrary goods for one another. 
* Autoswap now has different keywords for different actions that can
  be taken. For example, a swap should have the keywords 'In' and
  'Out'.
* Multipool Autoswap has new keywords for different actions that can
  be taken as well. For example, adding liquidity has the keywords:
  'SecondaryToken' and 'CentralToken' and returns a payout with
  keyword `Liquidity`.
* In Public Auction, the seller keywords are 'Asset' and 'Ask' and the
  buyer keywords are 'Asset' and 'Bid'.


ZoeHelpers changes:

Some helpers were removed, and others were added. The built-in Zoe contracts were rewritten to
  take advantage of these new helpers.
* `satisfies` was added. It checks whether an allocation would satisfy
  a single offer's wants if that was the allocation passed to
  `reallocate`. 
* `isOfferSafe` was added. It checks whether an
  allocation for a particular offer would satisfy offer safety. Any
  allocation that returns true under `satisfy` will also return true
  under `isOfferSafe`. (`isOfferSafe` is equivalent of `satisfies` ||
  gives a refund). 
* `trade` was added. `Trade` performs a trade between
  two offers given a declarative description of what each side loses
  and gains. 
* `Swap` remains but has slightly different behavior: any
  surplus in a trade remains with the original offer
* `canTradeWith`
  was removed and subsumed by `satisfies`.
* `inviteAnOffer` was already
  deprecated and was removed. 
* `assertNatMathHelpers` was added, which
  checks whether a particular keyword is associated with an issuer
  with natMathHelpers. 

ERTP changes:
* `purse.deposit()` now returns the amount of the deposit, rather than
  the purse's new balance.
* A deposit-only facet was added to purses, and can be created by
  calling `makeDepositFacet` on any purse.
  • Loading branch information
katelynsills authored Jun 30, 2020
1 parent dce1fd4 commit 4a14455
Show file tree
Hide file tree
Showing 59 changed files with 4,358 additions and 3,293 deletions.
7 changes: 7 additions & 0 deletions packages/ERTP/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
User-visible changes in ERTP:

## Release v0.6.0 (29-June-2020)

* `purse.deposit()` now returns the amount of the deposit, rather than
the purse's new balance.
* A deposit-only facet was added to purses, and can be created by
calling `makeDepositFacet` on any purse.

## Release v0.5.0 (26-Mar-2020)

Changed most ERTP methods to now accept promises for payments. The
Expand Down
3 changes: 2 additions & 1 deletion packages/ERTP/src/issuer.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ function produceIssuer(allegedName, mathHelpersName = 'nat') {
}),
);
assert(payments.length === 0, 'no payments should be returned');
return newPurseBalance;
return srcPaymentBalance;
},
withdraw: amount => {
amount = amountMath.coerce(amount);
Expand All @@ -295,6 +295,7 @@ function produceIssuer(allegedName, mathHelpersName = 'nat') {
},
getCurrentAmount: () => purseLedger.get(purse),
getAllegedBrand: () => brand,
makeDepositFacet: () => harden({ receive: purse.deposit }),
});
return purse;
};
Expand Down
62 changes: 49 additions & 13 deletions packages/ERTP/test/unitTests/test-issuerObj.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,31 +101,42 @@ test('issuer.makeEmptyPurse', t => {
.catch(e => t.assert(false, e));
});

test('issuer.deposit', t => {
t.plan(2);
test('purse.deposit', async t => {
t.plan(4);
const { issuer, mint, amountMath } = produceIssuer('fungible');
const fungible0 = amountMath.getEmpty();
const fungible17 = amountMath.make(17);
const fungible25 = amountMath.make(25);
const fungibleSum = amountMath.add(fungible17, fungible25);

const purse = issuer.makeEmptyPurse();
const payment = mint.mintPayment(fungible25);

const checkDeposit = newPurseBalance => {
const payment17 = mint.mintPayment(fungible17);
const payment25 = mint.mintPayment(fungible25);

const checkDeposit = (
expectedOldBalance,
expectedNewBalance,
) => depositResult => {
const delta = amountMath.subtract(expectedNewBalance, expectedOldBalance);
t.ok(
amountMath.isEqual(newPurseBalance, fungible25),
`the balance returned is the purse balance`,
amountMath.isEqual(depositResult, delta),
`the balance changes by the deposited amount: ${delta.extent}`,
);
t.ok(
amountMath.isEqual(purse.getCurrentAmount(), fungible25),
`the new purse balance is the payment's old balance`,
amountMath.isEqual(purse.getCurrentAmount(), expectedNewBalance),
`the new purse balance ${depositResult.extent} is the expected amount: ${expectedNewBalance.extent}`,
);
};

E(purse)
.deposit(payment, fungible25)
.then(checkDeposit);
await E(purse)
.deposit(payment17, fungible17)
.then(checkDeposit(fungible0, fungible17));
await E(purse)
.deposit(payment25, fungible25)
.then(checkDeposit(fungible17, fungibleSum));
});

test('issuer.deposit promise', t => {
test('purse.deposit promise', t => {
t.plan(1);
const { issuer, mint, amountMath } = produceIssuer('fungible');
const fungible25 = amountMath.make(25);
Expand All @@ -141,6 +152,31 @@ test('issuer.deposit promise', t => {
);
});

test('purse.makeDepositFacet', t => {
t.plan(2);
const { issuer, mint, amountMath } = produceIssuer('fungible');
const fungible25 = amountMath.make(25);

const purse = issuer.makeEmptyPurse();
const payment = mint.mintPayment(fungible25);

const checkDeposit = newPurseBalance => {
t.ok(
amountMath.isEqual(newPurseBalance, fungible25),
`the balance returned is the purse balance`,
);
t.ok(
amountMath.isEqual(purse.getCurrentAmount(), fungible25),
`the new purse balance is the payment's old balance`,
);
};

E(purse)
.makeDepositFacet()
.then(({ receive }) => receive(payment))
.then(checkDeposit);
});

test('issuer.burn', t => {
t.plan(2);
const { issuer, mint, amountMath } = produceIssuer('fungible');
Expand Down
2 changes: 1 addition & 1 deletion packages/cosmic-swingset/test/test-home.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ test('home.wallet - receive zoe invite', async t => {
);
const bundle = await bundleSource(contractRoot);
const installationHandle = await E(zoe).install(bundle);
const invite = await E(zoe).makeInstance(installationHandle);
const { invite } = await E(zoe).makeInstance(installationHandle);

// Check that the wallet knows about the Zoe invite issuer and starts out
// with a default Zoe invite issuer purse.
Expand Down
222 changes: 197 additions & 25 deletions packages/cosmic-swingset/test/unitTests/test-lib-wallet.js

Large diffs are not rendered by default.

19 changes: 10 additions & 9 deletions packages/store/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ const harden = /** @type {<T>(x: T) => T} */ (rawHarden);

/**
* @template K,V
* @typedef {Object} Store A wrapper around a Map
* @property {(key: K) => boolean} has Check if a key exists
* @property {(key: K, value: V) => void} init Initialize the key only if it doesn't already exist
* @property {(key: K) => V?} get Return a value fo the key, or undefined
* @property {(key: K, value: V) => void} set Unconditionally set the key
* @property {(key: K) => void} delete Remove the key
* @property {() => K[]} keys Return an array of keys
* @property {() => V[]} values Return an array of values
* @property {() => [K, V][]} entries Return an array of entries
* @typedef {Object} Store - A wrapper around a Map
* @property {(key: K) => boolean} has - Check if a key exists
* @property {(key: K, value: V) => void} init - Initialize the key only if it doesn't already exist
* @property {(key: K) => V} get - Return a value for the key. Throws
* if not found.
* @property {(key: K, value: V) => void} set - Unconditionally set the key
* @property {(key: K) => void} delete - Remove the key
* @property {() => K[]} keys - Return an array of keys
* @property {() => V[]} values - Return an array of values
* @property {() => [K, V][]} entries - Return an array of entries
*/

/**
Expand Down
108 changes: 108 additions & 0 deletions packages/zoe/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,113 @@
User-visible changes in @agoric/zoe:

## Release v0.7.0 (29-June-2020)

Zoe Service changes:

* Instead of `zoe.makeInstance` returning an invite only, it now
returns a record of `{ invite, instanceRecord }` such that
information like the `instanceHandle` can be obtained directly from
the instanceRecord.
* `installationHandle`, the identifier for the code that is used to
create a new running contract instance, is added to the extent of
invites for contracts so that interested parties can easily check
whether their invite is using the code they expect.
* `brandKeywordRecord` is added as a property of `instanceRecord`
alongside `issuerKeywordRecord`. `brandKeywordRecord` is an object
with keyword keys and brand values.
* `zoe.makeContract` now only accepts a single argument (`bundle`) and
the old module format will error.


Zoe Contract Facet (zcf) changes:
* `zcf.reallocate` no longer accepts a third argument `sparseKeywords`
and no longer expects the keywords for different offers to be the
same. Within reallocate, the offer safety check compares the user's
proposal to the user's allocation, and the rights conservation check
adds up the amounts by brand to ensure the totals are the same.
Neither of these checks requires that the keywords for the
allocations be the same for different offers.
* `zcf.getCurrentAllocation` and `zcf.getCurrentAllocations` no longer
take sparseKeywords as a parameter. Instead, brandKeywordRecord is
an optional parameter. If omitted, amounts are returned only for
brands for which an allocation currently exists.
* `zcf.getInstanceRecord()` no longer takes a parameter
* `brandKeywordRecord` is added as a property of `instanceRecord`
alongside `issuerKeywordRecord`. `brandKeywordRecord` is an object
with keyword keys and brand values.
* `zcf.getAmountMaths` has been subsumed by `zcf.getAmountMath` which
takes a single brand parameter.
* `zcf.getBrandForIssuer` has been added. It synchronously returns the
brand for a given issuer already known to Zoe.
* `zoe.getInstance`, which was deprecated earlier, has been removed.
* `cancelObj` and `cancelObj.cancel()`, which were deprecated earlier,
have been removed.

Changes for Zoe contract developers:
* Zoe contracts are now expected to return only an invite as the
result of `makeContract`. If the contracts want to have a
`publicAPI`, they can do so through `zcf.initPublicAPI`.
* Contracts can allow different offers to use different keywords for
the same issuers. For example, publicAuction, the second price
auction contract, uses 'Asset' and 'Ask'
for the seller keywords and 'Asset' and 'Bid' for the buyer
keywords.


Built-in Zoe contract changes:
* We added more comments to the start of the built-in Zoe contracts.
* The operaTickets contract has been split into two contracts: a
generic `sellItems` contract that sells fungible or nonfungible
items at a set price for money, and a generic `mintAndSellNFT`
contract that mints NFT tokens and then immediately creates a new
`sellItems` instance to sell them. The original operaTicket tests
are able to use these contracts.
* The `getCurrentPrice` helper in `bondingCurves.js` has been renamed
to `getInputPrice` and now only returns the `outputExtent`.
* A new built-in contract was added: barter-exchange.js. Barter
Exchange takes offers for trading arbitrary goods for one another.
* Autoswap now has different keywords for different actions that can
be taken. For example, a swap should have the keywords 'In' and
'Out'.
* Multipool Autoswap has new keywords for different actions that can
be taken as well. For example, adding liquidity has the keywords:
'SecondaryToken' and 'CentralToken' and returns a payout with
keyword `Liquidity`.
* In Public Auction, the seller keywords are 'Asset' and 'Ask' and the
buyer keywords are 'Asset' and 'Bid'.


ZoeHelpers changes:

Some helpers were removed, and others were added. The built-in Zoe contracts were rewritten to
take advantage of these new helpers.
* `satisfies` was added. It checks whether an allocation would satisfy
a single offer's wants if that was the allocation passed to
`reallocate`.
* `isOfferSafe` was added. It checks whether an
allocation for a particular offer would satisfy offer safety. Any
allocation that returns true under `satisfy` will also return true
under `isOfferSafe`. (`isOfferSafe` is equivalent of `satisfies` ||
gives a refund).
* `trade` was added. `Trade` performs a trade between
two offers given a declarative description of what each side loses
and gains.
* `Swap` remains but has slightly different behavior: any
surplus in a trade remains with the original offer
* `canTradeWith`
was removed and subsumed by `satisfies`.
* `inviteAnOffer` was already
deprecated and was removed.
* `assertNatMathHelpers` was added, which
checks whether a particular keyword is associated with an issuer
with natMathHelpers.

ERTP changes:
* `purse.deposit()` now returns the amount of the deposit, rather than
the purse's new balance.
* A deposit-only facet was added to purses, and can be created by
calling `makeDepositFacet` on any purse.

## Release v0.6.0 (1-May-2020)

* We added `completeObj` with the method `complete` to what is given
Expand Down
1 change: 1 addition & 0 deletions packages/zoe/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@agoric/notifier": "^0.1.2",
"@agoric/produce-promise": "^0.1.2",
"@agoric/same-structure": "^0.0.7",
"@agoric/store": "^0.1.2",
"@agoric/transform-metering": "^1.2.5",
"@agoric/weak-store": "^0.0.7"
},
Expand Down
33 changes: 14 additions & 19 deletions packages/zoe/src/cleanProposal.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { arrayToObj, assertSubset } from './objArrayConversion';

// We adopt simple requirements on keywords so that they do not accidentally
// conflict with existing property names.
// We require keywords to be strings, ascii identifiers beggining with an
// We require keywords to be strings, ascii identifiers beginning with an
// upper case letter, and distinct from the stringified form of any number.
//
// Of numbers, only NaN and Infinity, when stringified to a property name,
Expand Down Expand Up @@ -50,21 +50,21 @@ export const getKeywords = keywordRecord =>
harden(Object.getOwnPropertyNames(keywordRecord));

export const coerceAmountKeywordRecord = (
amountMathKeywordRecord,
allKeywords,
getAmountMath,
allegedAmountKeywordRecord,
) => {
const sparseKeywords = cleanKeys(allKeywords, allegedAmountKeywordRecord);
// Check that each value can be coerced using the amountMath indexed
// by keyword. `AmountMath.coerce` throws if coercion fails.
const coercedAmounts = sparseKeywords.map(keyword =>
amountMathKeywordRecord[keyword].coerce(
allegedAmountKeywordRecord[keyword],
),
const keywords = getKeywords(allegedAmountKeywordRecord);
keywords.forEach(assertKeywordName);

const amounts = Object.values(allegedAmountKeywordRecord);
// Check that each value can be coerced using the amountMath
// indicated by brand. `AmountMath.coerce` throws if coercion fails.
const coercedAmounts = amounts.map(amount =>
getAmountMath(amount.brand).coerce(amount),
);

// Recreate the amountKeywordRecord with coercedAmounts.
return arrayToObj(coercedAmounts, sparseKeywords);
return arrayToObj(coercedAmounts, keywords);
};

export const cleanKeywords = keywordRecord => {
Expand Down Expand Up @@ -95,11 +95,7 @@ export const cleanKeywords = keywordRecord => {
// `exit`, if present, must be a record of one of the following forms:
// `{ waived: null }` `{ onDemand: null }` `{ afterDeadline: { timer
// :Timer, deadline :Number } }
export const cleanProposal = (
issuerKeywordRecord,
amountMathKeywordRecord,
proposal,
) => {
export const cleanProposal = (getAmountMath, proposal) => {
const rootKeysAllowed = ['want', 'give', 'exit'];
mustBeComparable(proposal);
assertKeysAllowed(rootKeysAllowed, proposal);
Expand All @@ -108,9 +104,8 @@ export const cleanProposal = (
let { want = harden({}), give = harden({}) } = proposal;
const { exit = harden({ onDemand: null }) } = proposal;

const allKeywords = getKeywords(issuerKeywordRecord);
want = coerceAmountKeywordRecord(amountMathKeywordRecord, allKeywords, want);
give = coerceAmountKeywordRecord(amountMathKeywordRecord, allKeywords, give);
want = coerceAmountKeywordRecord(getAmountMath, want);
give = coerceAmountKeywordRecord(getAmountMath, give);

// Check exit
assert(
Expand Down
20 changes: 10 additions & 10 deletions packages/zoe/src/contractSupport/auctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,21 @@ export const secondPriceLogic = (bidAmountMath, bidOfferHandles, bids) => {
};

export const closeAuction = (
zoe,
zcf,
{ auctionLogicFn, sellerOfferHandle, allBidHandles },
) => {
const { Bid: bidAmountMath, Asset: assetAmountMath } = zoe.getAmountMaths(
harden(['Bid', 'Asset']),
);
const { brandKeywordRecord } = zcf.getInstanceRecord();
const bidAmountMath = zcf.getAmountMath(brandKeywordRecord.Ask);
const assetAmountMath = zcf.getAmountMath(brandKeywordRecord.Asset);

// Filter out any inactive bids
const { active: activeBidHandles } = zoe.getOfferStatuses(
const { active: activeBidHandles } = zcf.getOfferStatuses(
harden(allBidHandles),
);

const getBids = amountsKeywordRecord => amountsKeywordRecord.Bid;
const bids = zoe.getCurrentAllocations(activeBidHandles).map(getBids);
const assetAmount = zoe.getOffer(sellerOfferHandle).proposal.give.Asset;
const bids = zcf.getCurrentAllocations(activeBidHandles).map(getBids);
const assetAmount = zcf.getOffer(sellerOfferHandle).proposal.give.Asset;

const { winnerOfferHandle, winnerBid, price } = auctionLogicFn(
bidAmountMath,
Expand All @@ -52,15 +52,15 @@ export const closeAuction = (
// price paid.
const winnerRefund = bidAmountMath.subtract(winnerBid, price);

const newSellerAmounts = { Asset: assetAmountMath.getEmpty(), Bid: price };
const newSellerAmounts = { Asset: assetAmountMath.getEmpty(), Ask: price };
const newWinnerAmounts = { Asset: assetAmount, Bid: winnerRefund };

// Everyone else gets a refund so their extents remain the
// same.
zoe.reallocate(
zcf.reallocate(
harden([sellerOfferHandle, winnerOfferHandle]),
harden([newSellerAmounts, newWinnerAmounts]),
);
const allOfferHandles = harden([sellerOfferHandle, ...activeBidHandles]);
zoe.complete(allOfferHandles);
zcf.complete(allOfferHandles);
};
Loading

0 comments on commit 4a14455

Please sign in to comment.