Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve contract typing #94

Merged
merged 5 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions agoric/.yarnrc

This file was deleted.

3 changes: 1 addition & 2 deletions agoric/contract/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { handleParamGovernance } from '@agoric/governance';

import { prepareKreadKit, provideKreadKitRecorderKits } from './kreadKit.js';
import { provide } from '@agoric/vat-data';
import { provideRecorderKits } from './utils.js';

/**
* This contract handles the mint of KREAd characters,
Expand All @@ -25,7 +24,6 @@ import { provideRecorderKits } from './utils.js';
/** @typedef {import('@agoric/time/src/types').Clock} Clock */
/** @typedef {import('./type-guards.js').RatioObject} RatioObject */

/** @type {ContractMeta} */
export const meta = {
privateArgsShape: M.splitRecord({
initialPoserInvitation: InvitationShape,
Expand Down Expand Up @@ -111,6 +109,7 @@ export const start = async (zcf, privateArgs, baggage) => {
powers.marshaller,
);

/** @type {KreadKitRecorderKits} */
const recorderKits = await provideKreadKitRecorderKits(
baggage,
powers.storageNode,
Expand Down
112 changes: 86 additions & 26 deletions agoric/contract/src/kreadKit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint-disable no-undef */
// @ts-check
import { updateCollectionMetrics } from './market-metrics.js';
import { assert, details as X } from '@agoric/assert';
import { assert } from '@agoric/assert';
import { AmountMath, BrandShape } from '@agoric/ertp';
import { prepareExoClassKit, M } from '@agoric/vat-data';
import { makeDurableZone } from '@agoric/zone/durable.js';
Expand Down Expand Up @@ -42,6 +42,8 @@ import { multiplyBy } from '@agoric/zoe/src/contractSupport/ratio.js';

import '@agoric/zoe/exported.js';

/** @typedef {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart} TransferPart */

/**
* this provides the exoClassKit for our upgradable KREAd contract
* Utilizes capabilities from the prepare function suchs as mints
Expand Down Expand Up @@ -69,14 +71,7 @@ import '@agoric/zoe/exported.js';
* itemMint: ZCFMint<"copyBag">
* clock: import('@agoric/time/src/types.js').Clock
* makeRecorderKit: import('@agoric/zoe/src/contractSupport').MakeRecorderKit,
* recorderKits: {
* characterKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<unknown>;
* itemKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<unknown>;
* marketCharacterKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<unknown>;
* marketItemKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<unknown>;
* marketCharacterMetricsKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<unknown>;
* marketItemMetricsKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<unknown>;
* };
* recorderKits: KreadKitRecorderKits
* }} powers
*/
export const prepareKreadKit = (
Expand Down Expand Up @@ -140,34 +135,41 @@ export const prepareKreadKit = (
const zone = makeDurableZone(baggage);
return {
character: harden({
/** @type {MapStore<string, CharacterEntry | string[]>} */
entries: zone.mapStore('characters', {
keyShape: M.string(),
valueShape: M.or(CharacterEntryGuard, M.arrayOf(M.string())),
}),
/** @type {MapStore<number, BaseCharacter>} */
bases: zone.mapStore('baseCharacters', {
keyShape: M.number(),
valueShape: BaseCharacterGuard,
}),
}),
item: harden({
/** @type {MapStore<number, ItemEntry} */
entries: zone.mapStore('items', {
keyShape: M.number(),
valueShape: ItemRecorderGuard,
}),
/** @type {MapStore<'common' | 'uncommonToLegendary', Item[]>} */
bases: zone.mapStore('baseItems', {
keyShape: RarityGuard,
valueShape: M.arrayOf(ItemGuard),
}),
}),
market: harden({
/** @type {MapStore<string, MarketEntry>} */
characterEntries: zone.mapStore('characterMarket', {
keyShape: M.string(),
valueShape: MarketEntryGuard,
}),
/** @type {MapStore<number, MarketEntry>} */
itemEntries: zone.mapStore('itemMarket', {
keyShape: M.number(),
valueShape: MarketEntryGuard,
}),
/** @type {MapStore<'character' | 'item', MarketMetrics>} */
metrics: zone.mapStore('marketMetrics', {
keyShape: M.or('character', 'item'),
valueShape: MarketMetricsGuard,
Expand Down Expand Up @@ -218,18 +220,35 @@ export const prepareKreadKit = (
);
return Array.from(characterState.bases.keys())[number];
},
/**
*
* @param {Array<[number, BaseCharacter]>} baseCharacters
*/
initializeBaseCharacters(baseCharacters) {
const { character: characterState } = this.state;
if (characterState.bases.getSize() > 0) return;
addAllToMap(characterState.bases, baseCharacters);
},
/**
* @param {string} path
*/
async makeInventoryRecorderKit(path) {
const node = await E(characterNode).makeChildNode(
`inventory-${path}`,
);
return makeRecorderKit(node, M.arrayOf([ItemGuard, M.nat()]));
return makeRecorderKit(
node,
/** @type {import('@agoric/zoe/src/contractSupport/recorder.js').TypedMatcher<Array<[Item, bigint]>>} */ (
M.arrayOf([ItemGuard, M.nat()])
),
);
},
mint() {
/**
* @param {ZCFSeat} seat
* @param {object} offerArgs
* @param {string} offerArgs.name
*/
const handler = async (seat, offerArgs) => {
const {
helper,
Expand Down Expand Up @@ -257,8 +276,7 @@ export const prepareKreadKit = (
!characterState.entries.get('names').includes(newCharacterName) ||
assert.fail(errors.nameTaken(newCharacterName));

characterState.bases.getSize() > 0 ||
assert.fail(errors.allMinted);
characterState.bases.getSize() > 0 || assert.fail(errors.allMinted);

const re = /^[a-zA-Z0-9_-]*$/;
(re.test(newCharacterName) && newCharacterName !== 'names') ||
Expand Down Expand Up @@ -405,6 +423,9 @@ export const prepareKreadKit = (
);
},
equip() {
/**
* @param {ZCFSeat} seat
*/
const handler = (seat) => {
const { character: characterFacet } = this.facets;
const { character: characterState } = this.state;
Expand Down Expand Up @@ -494,6 +515,9 @@ export const prepareKreadKit = (
);
},
unequip() {
/**
* @param {ZCFSeat} seat
*/
const handler = async (seat) => {
const { character: characterState } = this.state;

Expand All @@ -507,8 +531,7 @@ export const prepareKreadKit = (
// Find character record entry based on provided key
const characterRecord = characterState.entries.get(characterName);
const inventorySeat = characterRecord.inventory;
providedCharacterKey ||
assert.fail(errors.invalidCharacterKey);
providedCharacterKey || assert.fail(errors.invalidCharacterKey);

// Get reference to the wanted items and key
const { want } = seat.getProposal();
Expand Down Expand Up @@ -573,6 +596,9 @@ export const prepareKreadKit = (
);
},
swap() {
/**
* @param {ZCFSeat} seat
*/
const handler = (seat) => {
const { character: characterFacet } = this.facets;
const { character: characterState } = this.state;
Expand All @@ -589,8 +615,7 @@ export const prepareKreadKit = (
// Find character record entry based on provided key
const characterRecord = characterState.entries.get(characterName);
const inventorySeat = characterRecord.inventory;
providedCharacterKey ||
assert.fail(errors.invalidCharacterKey);
providedCharacterKey || assert.fail(errors.invalidCharacterKey);

const { want } = seat.getProposal();
const {
Expand Down Expand Up @@ -687,6 +712,9 @@ export const prepareKreadKit = (
);
},
unequipAll() {
/**
* @param {ZCFSeat} seat
*/
const handler = (seat) => {
const { character: characterState } = this.state;

Expand All @@ -700,8 +728,7 @@ export const prepareKreadKit = (
// Find character record entry based on provided key
const characterRecord = characterState.entries.get(characterName);
const inventorySeat = characterRecord.inventory;
providedCharacterKey ||
assert.fail(errors.invalidCharacterKey);
providedCharacterKey || assert.fail(errors.invalidCharacterKey);

// Get reference to the wanted item
const { want } = seat.getProposal();
Expand Down Expand Up @@ -764,6 +791,9 @@ export const prepareKreadKit = (
},
},
item: {
/**
* @param {Item[]} baseItems
*/
initializeBaseItems(baseItems) {
const { item: itemState } = this.state;
if (itemState.bases.getSize() > 0) return;
Expand All @@ -782,6 +812,9 @@ export const prepareKreadKit = (
]);
},
// Mints the default set of items to a seat that doesn't exit
/**
* @param {ZCFSeat} seat
*/
async mintDefaultBatch(seat) {
const { helper, market: marketFacet } = this.facets;
const { item: itemState } = this.state;
Expand Down Expand Up @@ -905,6 +938,9 @@ export const prepareKreadKit = (
return text.mintItemReturn;
},
mint() {
/**
* @param {ZCFSeat} seat
*/
const handler = async (seat) => {
const { helper, market: marketFacet } = this.facets;
const { item: itemState } = this.state;
Expand Down Expand Up @@ -1033,12 +1069,22 @@ export const prepareKreadKit = (
async makeMarketItemRecorderKit(id) {
const path = `item-${String(id)}`;
const node = await E(marketItemNode).makeChildNode(path);
return makeRecorderKit(node, MarketRecorderGuard);
return makeRecorderKit(
node,
/** @type {import('@agoric/zoe/src/contractSupport/recorder.js').TypedMatcher<MarketRecorder>}**/ (
MarketRecorderGuard
),
);
},
async makeMarketCharacterRecorderKit(id) {
const path = `character-${id}`;
const node = await E(marketCharacterNode).makeChildNode(path);
return makeRecorderKit(node, MarketRecorderGuard);
return makeRecorderKit(
node,
/** @type {import('@agoric/zoe/src/contractSupport/recorder.js').TypedMatcher<MarketRecorder>}**/ (
MarketRecorderGuard
),
);
},
/**
* Caveat assumes parent is either `marketCharacterNode` or
Expand All @@ -1062,6 +1108,9 @@ export const prepareKreadKit = (
await E(deletable).setValue('');
},
sellItem() {
/**
* @param {ZCFSeat} seat
*/
const handler = async (seat) => {
const { market } = this.state;
const { market: marketFacet } = this.facets;
Expand Down Expand Up @@ -1133,6 +1182,9 @@ export const prepareKreadKit = (
);
},
sellCharacter() {
/**
* @param {ZCFSeat} seat
*/
const handler = async (seat) => {
const { market } = this.state;
const { character: characterFacet, market: marketFacet } =
Expand All @@ -1144,6 +1196,8 @@ export const prepareKreadKit = (

paymentBrand === want.Price.brand ||
assert.fail(errors.incorrectPaymentBrand(paymentBrand));


const askingPrice = {
brand: want.Price.brand,
value: want.Price.value,
Expand Down Expand Up @@ -1208,14 +1262,18 @@ export const prepareKreadKit = (
);
},
buyItem() {
/**
* @param {ZCFSeat} buyerSeat
* @param {object} offerArgs
* @param {number} offerArgs.entryId
*/
const handler = async (buyerSeat, offerArgs) => {
const { market: marketFacet } = this.facets;
const { market } = this.state;

// Find store record based on wanted character
const sellRecord = market.itemEntries.get(offerArgs.entryId);
sellRecord ||
assert.fail(errors.itemNotFound(offerArgs.entryId));
sellRecord || assert.fail(errors.itemNotFound(offerArgs.entryId));

const result = await (sellRecord.isFirstSale
? marketFacet.buyFirstSaleItem(
Expand Down Expand Up @@ -1252,7 +1310,7 @@ export const prepareKreadKit = (
*
* @param {ZCFSeat} sellerSeat
* @param {ZCFSeat} buyerSeat
* @param {ItemMarketRecord} sellRecord
* @param {MarketEntry} sellRecord
* @returns {Promise<HelperFunctionReturn>}
*/
async buyFirstSaleItem(sellerSeat, buyerSeat, sellRecord) {
Expand Down Expand Up @@ -1373,7 +1431,7 @@ export const prepareKreadKit = (
*
* @param {ZCFSeat} sellerSeat
* @param {ZCFSeat} buyerSeat
* @param {ItemMarketRecord} sellRecord
* @param {MarketEntry} sellRecord
* @returns {Promise<HelperFunctionReturn>}
*/
async buySecondarySaleItem(sellerSeat, buyerSeat, sellRecord) {
Expand Down Expand Up @@ -1474,6 +1532,9 @@ export const prepareKreadKit = (
};
},
buyCharacter() {
/**
* @param {ZCFSeat} buyerSeat
*/
const handler = async (buyerSeat) => {
const { market: marketFacet } = this.facets;
const { market, character: characterState } = this.state;
Expand Down Expand Up @@ -1640,7 +1701,7 @@ export const prepareKreadKit = (
},
/**
*
* @param {Amount<nat>} price
* @param {Amount<"nat">} price
* @param {[Item, bigint][]} itemsToSell
*/
async publishItemCollection(price, itemsToSell) {
Expand Down Expand Up @@ -1794,7 +1855,6 @@ harden(prepareKreadKit);
* @param {import('@agoric/vat-data').Baggage} baggage
* @param {StorageNode} storageNode
* @param {import('@agoric/zoe/src/contractSupport/recorder.js').MakeRecorderKit} makeRecorderKit
* @returns
*/
export const provideKreadKitRecorderKits = (
baggage,
Expand Down
Loading
Loading