Skip to content

Commit

Permalink
feat(types): Payment for set-like carries key type
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Jan 9, 2025
1 parent 543c6f9 commit c7d1762
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 27 deletions.
2 changes: 1 addition & 1 deletion packages/ERTP/src/issuerKit.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ harden(prepareIssuerKit);
* anything else is corrupted by that corrupted state. See
* https://github.com/Agoric/agoric-sdk/issues/3434
* @param {IssuerOptionsRecord} [options]
* @returns {IssuerKit<K, any>}
* @returns {IssuerKit<K>}
*/
export const makeIssuerKit = (
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} M
* @param {ERef<Purse<K, M>>} recoveryPurse
* @param {ERef<Payment<K, M>>} srcPaymentP
* @param {Pattern} [optAmountShape]
* @returns {Promise<P>}
* @returns {Promise<Payment<K, M>>}
*/
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} M
* @param {ERef<Purse<K, M>>} recoveryPurse
* @param {ERef<Payment<K, M>>[]} srcPaymentsPs
* @param {Pattern} [optTotalAmount]
* @returns {Promise<Payment<K>>}
* @returns {Promise<Payment<K, M>>}
*/
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, M>} */ (
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 { Tagged } 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 &
Tagged<
{
/**
* 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
9 changes: 8 additions & 1 deletion packages/ERTP/test/unitTests/mintObj.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { defineDurableKind, makeKindHandle } from '@agoric/vat-data';
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 Down Expand Up @@ -110,11 +114,14 @@ test('mint.mintPayment set AssetKind with invites', async t => {
// 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 123 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));

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

Expand Down
2 changes: 1 addition & 1 deletion packages/smart-wallet/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ declare const CapDataShape: unique symbol;

// Match the type in Zoe, which can't be imported because it's ambient.
// This omits the parameters that aren't used in this module.
type Invitation = Payment<'set'>;
type Invitation = Payment<'set', InvitationDetails>;

/**
* A petname can either be a plain string or a path for which the first element
Expand Down
1 change: 1 addition & 0 deletions packages/smart-wallet/test/marshal-contexts.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ test('makeExportContext.serialize handles unregistered identities', t => {

t.deepEqual(context.fromCapData(actual), invitationAmount);

// @ts-expect-error missing [tag] for set-like key
const myPayment = /** @type {Payment} */ (
Far('payment', { getAllegedBrand: () => assert.fail('no impl') })
);
Expand Down
5 changes: 3 additions & 2 deletions packages/zoe/src/zoeService/offer/burnInvitation.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import { E } from '@endo/eventual-send';
* Burn the invitation, assert that only one invitation was burned,
* and extract and return the instanceHandle and invitationHandle
*
* @param {Issuer} invitationIssuer
* @param {ERef<Payment>} invitation
* @template {AssetKind} K
* @param {Issuer<K>} invitationIssuer
* @param {ERef<Payment<K>>} invitation
* @returns {Promise<{
* instanceHandle: Instance,
* invitationHandle: InvitationHandle,
Expand Down
2 changes: 1 addition & 1 deletion packages/zoe/src/zoeService/types-ambient.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@
* @property {() => Promise<PaymentPKeywordRecord>} getPayouts
* returns a promise for a KeywordPaymentRecord containing all the payouts from
* this seat. The promise will resolve after the seat has exited.
* @property {(keyword: Keyword) => Promise<Payment<any>>} getPayout
* @property {(keyword: Keyword) => Promise<Payment<any, any>>} getPayout
* returns a promise for the Payment corresponding to the indicated keyword.
* The promise will resolve after the seat has exited. If there is no payment
* corresponding to the keyword, an error will be thrown. (It used to return
Expand Down
2 changes: 1 addition & 1 deletion packages/zoe/test/unitTests/contracts/coveredCall.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -994,7 +994,7 @@ test('zoe - coveredCall non-fungible', async t => {
// already escrowed.

const invitationIssuer = await E(zoe).getInvitationIssuer();
/** @type {Payment<any>} */
/** @type {Payment<'set', InvitationDetails>} */
const bobExclOption = await claim(
E(invitationIssuer).makeEmptyPurse(),
optionP,
Expand Down

0 comments on commit c7d1762

Please sign in to comment.