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

fix Payment type #10828

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
42 changes: 26 additions & 16 deletions packages/ERTP/src/issuerKit.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import { AssetKind, assertAssetKind } from './amountMath.js';
import { coerceDisplayInfo } from './displayInfo.js';
import { preparePaymentLedger } from './paymentLedger.js';

/** @import {AdditionalDisplayInfo, RecoverySetsOption, IssuerKit, PaymentLedger} from './types.js' */
/** @import {ShutdownWithFailure} from '@agoric/swingset-vat' */
/**
* @import {AdditionalDisplayInfo, RecoverySetsOption, IssuerKit, PaymentLedger} from './types.js';
* @import {ShutdownWithFailure} from '@agoric/swingset-vat';
* @import {TypedPattern} from '@agoric/internal';
*/

/**
* @template {AssetKind} K
Expand Down Expand Up @@ -224,6 +227,7 @@ harden(makeDurableIssuerKit);
* Used to either revive a predecessor issuerKit, or to make a new durable one
* if it is absent, and to place it in baggage for the next successor.
*
* @template {IssuerOptionsRecord} O
* @template {AssetKind} K The name becomes part of the brand in asset
* descriptions. The name is useful for debugging and double-checking
* assumptions, but should not be trusted wrt any external namespace. For
Expand All @@ -247,8 +251,10 @@ harden(makeDurableIssuerKit);
* unit of computation, like the enclosing vat, can be shutdown before
* anything else is corrupted by that corrupted state. See
* https://github.com/Agoric/agoric-sdk/issues/3434
* @param {IssuerOptionsRecord} [options]
* @returns {IssuerKit<K>}
* @param {O} [options]
* @returns {O['elementShape'] extends TypedPattern<infer T>
* ? IssuerKit<K, T>
* : IssuerKit<K>}
*/
export const prepareIssuerKit = (
issuerBaggage,
Expand All @@ -257,6 +263,7 @@ export const prepareIssuerKit = (
assetKind = AssetKind.NAT,
displayInfo = harden({}),
optShutdownWithFailure = undefined,
// @ts-expect-error could have a different subtype of IssuerOptionsRecord
options = {},
) => {
if (hasIssuer(issuerBaggage)) {
Expand Down Expand Up @@ -285,6 +292,7 @@ export const prepareIssuerKit = (
optShutdownWithFailure,
options,
);
// @ts-expect-error cast to the type parameter
return issuerKit;
}
};
Expand All @@ -299,39 +307,41 @@ harden(prepareIssuerKit);
* Currently used for testing only. Should probably continue to be used for
* testing only.
*
* @template {AssetKind} [K='nat'] The name becomes part of the brand in asset
* @template {AssetKind} [K='nat']
* @template {IssuerOptionsRecord} [O={}]
* @param {string} name The name becomes part of the brand in asset
* descriptions. The name is useful for debugging and double-checking
* assumptions, but should not be trusted wrt any external namespace. For
* example, anyone could create a new issuer kit with name 'BTC', but it is
* not bitcoin or even related. It is only the name according to that issuer
* and brand.
*
* The assetKind will be used to import a specific mathHelpers from the
* mathHelpers library. For example, natMathHelpers, the default, is used for
* basic fungible tokens.
*
* `displayInfo` gives information to the UI on how to display the amount.
* @param {string} name
* @param {K} [assetKind]
* @param {AdditionalDisplayInfo} [displayInfo]
* @param {K} [assetKind] The assetKind will be used to import a specific
* mathHelpers from the mathHelpers library. For example, natMathHelpers, the
* default, is used for basic fungible tokens.
* @param {AdditionalDisplayInfo} [displayInfo] `displayInfo` gives information
* to the UI on how to display the amount.
* @param {ShutdownWithFailure} [optShutdownWithFailure] If this issuer fails in
* the middle of an atomic action (which btw should never happen), it
* potentially leaves its ledger in a corrupted state. If this function was
* provided, then the failed atomic action will call it, so that some larger
* unit of computation, like the enclosing vat, can be shutdown before
* anything else is corrupted by that corrupted state. See
* https://github.com/Agoric/agoric-sdk/issues/3434
* @param {IssuerOptionsRecord} [options]
* @returns {IssuerKit<K, any>}
* @param {O} [options]
* @returns {O['elementShape'] extends TypedPattern<infer T>
* ? IssuerKit<K, T>
* : IssuerKit<K>}
Comment on lines +331 to +333
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OMG cool!

*/
export const makeIssuerKit = (
name,
// @ts-expect-error K could be instantiated with a different subtype of AssetKind
assetKind = AssetKind.NAT,
displayInfo = harden({}),
optShutdownWithFailure = undefined,
// @ts-expect-error O could be instantiated with a different subtype
{ elementShape = undefined, recoverySetsOption = undefined } = {},
) =>
// @ts-expect-error cast
makeDurableIssuerKit(
makeScalarBigMapStore('dropped issuer kit', { durable: true }),
name,
Expand Down
24 changes: 15 additions & 9 deletions packages/ERTP/src/legacy-payment-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AmountMath } from './amountMath.js';

/**
* @import {ERef} from '@endo/far';
* @import {Key} from '@endo/patterns';
* @import {Amount, AssetKind, Payment, Purse} from './types.js';
*/

Expand All @@ -25,19 +26,19 @@ import { AmountMath } from './amountMath.js';
*/

/**
* @template {Payment} P
* @param {ERef<Purse>} recoveryPurse
* @param {ERef<P>} srcPaymentP
* @template {AssetKind} K
* @template {Key} T type of values in set-like kind
* @param {ERef<Purse<K, T>>} recoveryPurse
* @param {ERef<Payment<K, T>>} srcPaymentP
* @param {Pattern} [optAmountShape]
* @returns {Promise<P>}
* @returns {Promise<Payment<K, T>>}
*/
export const claim = async (
recoveryPurse,
srcPaymentP,
optAmountShape = undefined,
) => {
const srcPayment = await srcPaymentP;
// @ts-expect-error XXX could be instantiated with a different subtype
return E.when(E(recoveryPurse).deposit(srcPayment, optAmountShape), amount =>
E(recoveryPurse).withdraw(amount),
);
Expand All @@ -53,10 +54,11 @@ harden(claim);
* origin.
*
* @template {AssetKind} K
* @param {ERef<Purse<K>>} recoveryPurse
* @param {ERef<Payment<K>>[]} srcPaymentsPs
* @template {Key} T type of values in set-like kind
* @param {ERef<Purse<K, T>>} recoveryPurse
* @param {ERef<Payment<K, T>>[]} srcPaymentsPs
* @param {Pattern} [optTotalAmount]
* @returns {Promise<Payment<K>>}
* @returns {Promise<Payment<K, T>>}
*/
export const combine = async (
recoveryPurse,
Expand All @@ -69,7 +71,11 @@ export const combine = async (
E(brandP).getDisplayInfo(),
...srcPaymentsPs,
]);
const emptyAmount = AmountMath.makeEmpty(brand, displayInfo.assetKind);

// XXX Brand lacks M
const emptyAmount = /** @type {Amount<K, T>} */ (
AmountMath.makeEmpty(brand, displayInfo.assetKind)
);
const amountPs = srcPayments.map(srcPayment =>
E(recoveryPurse).deposit(srcPayment),
);
Expand Down
3 changes: 2 additions & 1 deletion packages/ERTP/src/payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { initEmpty } from '@agoric/store';
* @param {string} name
* @param {Brand<K>} brand
* @param {import('@endo/patterns').InterfaceGuard<any>} PaymentI
* @returns {() => Payment<K>}
* @returns {() => Payment<K, any>}
*/
export const preparePaymentKind = (issuerZone, name, brand, PaymentI) => {
const makePayment = issuerZone.exoClass(
Expand All @@ -24,6 +24,7 @@ export const preparePaymentKind = (issuerZone, name, brand, PaymentI) => {
},
},
);
// @ts-expect-error [tag] for tagged type not defined in runtime
return makePayment;
};
harden(preparePaymentKind);
26 changes: 16 additions & 10 deletions packages/ERTP/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { LatestTopic } from '@agoric/notifier';
import type { ERef } from '@endo/far';
import type { RemotableObject } from '@endo/pass-style';
import type { CopyBag, CopySet, Key, Pattern } from '@endo/patterns';
import type { TypeTag } from '@agoric/internal/src/tagged.js';
import type { AssetKind } from './amountMath.js';

export type { AssetKind } from './amountMath.js';
Expand Down Expand Up @@ -405,16 +406,21 @@ export type PurseMethods<
export type Payment<
K extends AssetKind = AssetKind,
M extends Key = Key,
> = RemotableObject & PaymentMethods<K>;
export type PaymentMethods<K extends AssetKind = AssetKind> = {
/**
* Get the allegedBrand, indicating
* the type of digital asset this payment purports to be, and which issuer to
* use. Because payments are not trusted, any method calls on payments should
* be treated with suspicion and verified elsewhere.
*/
getAllegedBrand: () => Brand<K>;
};
> = RemotableObject &
TypeTag<
{
/**
* Get the allegedBrand, indicating
* the type of digital asset this payment purports to be, and which issuer to
* use. Because payments are not trusted, any method calls on payments should
* be treated with suspicion and verified elsewhere.
*/
getAllegedBrand: () => Brand<K>;
},
'Set-like value type',
M
>;

/**
* All of the difference in how digital asset
* amount are manipulated can be reduced to the behavior of the math on
Expand Down
16 changes: 14 additions & 2 deletions packages/ERTP/test/unitTests/mintObj.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import { makeIssuerKit, AssetKind, AmountMath } from '../../src/index.js';
import { claim, combine } from '../../src/legacy-payment-helpers.js';

/**
* @import {IssuerKit} from '../../src/types.js'
*/

test('mint.getIssuer', t => {
const { mint, issuer } = makeIssuerKit('fungible');
t.is(mint.getIssuer(), issuer);
Expand All @@ -24,13 +28,18 @@
t.assert(AmountMath.isEqual(paymentBalance2, fungible1000));
});

/** @type {import('@agoric/internal').TypedPattern<string>} */
const StringPattern = M.string();

test('mint.mintPayment set w strings AssetKind', async t => {
const { mint, issuer, brand } = makeIssuerKit(
'items',
AssetKind.SET,
undefined,
undefined,
{ elementShape: M.string() },
{
elementShape: StringPattern,
},
);
const items1and2and4 = AmountMath.make(brand, harden(['1', '2', '4']));
const payment1 = mint.mintPayment(items1and2and4);
Expand Down Expand Up @@ -110,11 +119,14 @@
// This test models ballet tickets
test('non-fungible tokens example', async t => {
t.plan(11);

const {
mint: balletTicketMint,
issuer: balletTicketIssuer,
brand,
} = makeIssuerKit('Agoric Ballet Opera tickets', AssetKind.SET);
} = /**
* @type {IssuerKit<'set', { seat: number; show: string; start: string }>}

Check warning on line 128 in packages/ERTP/test/unitTests/mintObj.test.js

View workflow job for this annotation

GitHub Actions / lint-primary

Expected JSDoc block to be aligned
*/ (makeIssuerKit('Agoric Ballet Opera tickets', AssetKind.SET));
Comment on lines +128 to +129
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to also pass a corresponding elementShape to this call to makeIssuerKit?

As a test, maybe not worth bothering, so I ask hypothetically: would it have made sense, if code like this appears in production?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several sites now use elementShape. I think the illustration is best when there's a TypedPattern shape available, like with InvitationElementShape which I was happy to find. This instance I think serves as a useful example of typing when you don't have an elementShape defined.


const startDateString = new Date(2020, 1, 17, 20, 30).toISOString();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,19 @@ import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import { makeIssuerKit, AssetKind, AmountMath } from '@agoric/ertp';

import { InvitationElementShape } from '@agoric/zoe/src/typeGuards.js';
import { makeDepositInvitation } from '../../src/depositInvitation.js';

test('depositInvitation', async t => {
const { mint, issuer, brand } = makeIssuerKit('invitations', AssetKind.SET);
const { mint, issuer, brand } = makeIssuerKit(
'invitations',
AssetKind.SET,
undefined,
undefined,
{
elementShape: InvitationElementShape,
},
);
const purse = issuer.makeEmptyPurse();
const paymentAmount = AmountMath.make(brand, harden([{ instance: {} }]));
const payment = mint.mintPayment(paymentAmount);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
// @ts-nocheck
import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import { makeIssuerKit, AssetKind, AmountMath } from '@agoric/ertp';

import { InvitationElementShape } from '@agoric/zoe/src/typeGuards.js';
import { makeOfferAndFindInvitationAmount } from '../../src/offer.js';

test('findInvitationAmount', async t => {
const { mint, issuer, brand } = makeIssuerKit('invitations', AssetKind.SET);
const { mint, issuer, brand } = makeIssuerKit(
'invitations',
AssetKind.SET,
undefined,
undefined,
{
elementShape: InvitationElementShape,
},
);
const zoeInvitationPurse = issuer.makeEmptyPurse();

const walletAdmin = {};

const zoe = {};

const paymentAmount = AmountMath.make(
Expand All @@ -21,6 +30,7 @@ test('findInvitationAmount', async t => {

const { findInvitationAmount } = makeOfferAndFindInvitationAmount(
walletAdmin,
// @ts-expect-error mock
zoe,
zoeInvitationPurse,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { buildZoeManualTimer } from '@agoric/zoe/tools/manualTimer.js';
import { TimeMath } from '@agoric/time';
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
import { documentStorageSchema } from '@agoric/governance/tools/storageDoc.js';
import { makePriceQuoteIssuer } from '@agoric/zoe/src/contractSupport/priceQuote.js';
import { prepareFluxAggregatorKit } from '../../src/price/fluxAggregatorKit.js';
import { topicPath } from '../supports.js';

Expand Down Expand Up @@ -49,7 +50,7 @@ const makeContext = async () => {
const toTS = val => TimeMath.coerceTimestampRecord(val, timerBrand);

const baggage = makeScalarBigMapStore('test baggage');
const quoteIssuerKit = makeIssuerKit('quote', AssetKind.SET);
const quoteIssuerKit = makePriceQuoteIssuer();

const { makeDurablePublishKit, makeRecorder } = prepareRecorderKitMakers(
baggage,
Expand Down
10 changes: 6 additions & 4 deletions packages/inter-protocol/test/vaultFactory/driver.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { AmountMath, AssetKind, makeIssuerKit } from '@agoric/ertp';
import { allValues, makeTracer, objectMap, NonNullish } from '@agoric/internal';
import { AmountMath, makeIssuerKit } from '@agoric/ertp';
import { allValues, makeTracer, NonNullish, objectMap } from '@agoric/internal';
import { makeNotifierFromSubscriber } from '@agoric/notifier';
import { unsafeMakeBundleCache } from '@agoric/swingset-vat/tools/bundleTool.js';
import {
ceilMultiplyBy,
makePriceQuoteIssuer,
makeRatio,
makeRatioFromAmounts,
} from '@agoric/zoe/src/contractSupport/index.js';
Expand All @@ -13,8 +14,8 @@ import { E } from '@endo/eventual-send';
import { deeplyFulfilled } from '@endo/marshal';

import { eventLoopIteration } from '@agoric/notifier/tools/testSupports.js';
import { providePriceAuthorityRegistry } from '@agoric/vats/src/priceAuthorityRegistry.js';
import { makeScalarBigMapStore } from '@agoric/vat-data/src/index.js';
import { providePriceAuthorityRegistry } from '@agoric/vats/src/priceAuthorityRegistry.js';

import {
setupReserve,
Expand All @@ -30,6 +31,7 @@ import {
} from '../supports.js';

/**
* @import {PriceDescription} from '@agoric/zoe/tools/types.js';
* @import {VaultFactoryContract as VFC} from '../../src/vaultFactory/vaultFactory.js';
* @import {AmountUtils} from '@agoric/zoe/tools/test-utils.js';
*/
Expand Down Expand Up @@ -230,7 +232,7 @@ const setupServices = async (t, initialPrice, priceBase) => {
actualBrandOut: run.brand,
initialPrice: makeRatioFromAmounts(initialPrice, priceBase),
timer,
quoteIssuerKit: makeIssuerKit('quote', AssetKind.SET),
quoteIssuerKit: makePriceQuoteIssuer(),
});
const baggage = makeScalarBigMapStore('baggage');
const { priceAuthority: priceAuthorityReg, adminFacet: priceAuthorityAdmin } =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ export const setupElectorateReserveAndAuction = async (

await startEconomicCommittee(space, electorateTerms);
await setupReserve(space);
// const quoteIssuerKit = makeIssuerKit('quote', AssetKind.SET);

/** @type {import('@agoric/vat-data').Baggage} */
const paBaggage = makeScalarMapStore();
Expand Down
Loading
Loading