diff --git a/packages/ERTP/package.json b/packages/ERTP/package.json index 7bc8f25d6aa1..e752aa557f73 100644 --- a/packages/ERTP/package.json +++ b/packages/ERTP/package.json @@ -52,6 +52,7 @@ }, "devDependencies": { "@endo/bundle-source": "^2.5.2", + "@endo/exo": "^0.2.3", "@fast-check/ava": "^1.1.5", "ava": "^5.3.0", "tsd": "^0.28.1" diff --git a/packages/ERTP/src/payment.js b/packages/ERTP/src/payment.js index 9c3a0e644288..a6feb5562d17 100644 --- a/packages/ERTP/src/payment.js +++ b/packages/ERTP/src/payment.js @@ -11,9 +11,13 @@ import { prepareExoClass } from '@agoric/vat-data'; * @param {string} name * @param {Brand} brand * @param {InterfaceGuard} PaymentI - * @returns {() => Payment} + * @returns {{ + * makePayment: () => Payment, + * revokePayment: (payment: Payment) => boolean + * }} */ export const preparePaymentKind = (issuerBaggage, name, brand, PaymentI) => { + let revokePayment; const makePayment = prepareExoClass( issuerBaggage, `${name} payment`, @@ -24,7 +28,16 @@ export const preparePaymentKind = (issuerBaggage, name, brand, PaymentI) => { return brand; }, }, + { + getRevoker(revoke) { + revokePayment = revoke; + }, + }, ); - return makePayment; + assert(revokePayment !== undefined); + return harden({ + makePayment, + revokePayment, + }); }; harden(preparePaymentKind); diff --git a/packages/ERTP/src/paymentLedger.js b/packages/ERTP/src/paymentLedger.js index b4575854d14a..f8ef8db49292 100644 --- a/packages/ERTP/src/paymentLedger.js +++ b/packages/ERTP/src/paymentLedger.js @@ -121,7 +121,12 @@ export const preparePaymentLedger = ( amountShape, ); - const makePayment = preparePaymentKind(issuerBaggage, name, brand, PaymentI); + const { makePayment, revokePayment } = preparePaymentKind( + issuerBaggage, + name, + brand, + PaymentI, + ); /** @type {ShutdownWithFailure} */ const shutdownLedgerWithFailure = reason => { @@ -198,6 +203,8 @@ export const preparePaymentLedger = ( paymentRecoverySets.delete(payment); recoverySet.delete(payment); } + // @ts-expect-error The usual type param confusion + revokePayment(payment); }; /** @type {(left: Amount, right: Amount) => Amount} */ diff --git a/packages/ERTP/test/unitTests/test-recovery.js b/packages/ERTP/test/unitTests/test-recovery.js index 3ed6ef326a76..aeb13ab746d6 100644 --- a/packages/ERTP/test/unitTests/test-recovery.js +++ b/packages/ERTP/test/unitTests/test-recovery.js @@ -1,4 +1,5 @@ import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; +import { GET_INTERFACE_GUARD } from '@endo/exo'; import { getCopySetKeys, keyEQ, makeCopySet } from '@agoric/store'; import { makeIssuerKit, AmountMath } from '../../src/index.js'; @@ -67,4 +68,12 @@ test('payment recovery from mint recovery set', async t => { t.throws(() => bobPurse.deposit(payment2), { message: /was not a live payment for brand/, }); + t.throws(() => payment2.getAllegedBrand(), { + message: + '"In \\"getAllegedBrand\\" method of (precious payment)" may only be applied to a valid instance: "[Alleged: precious payment]"', + }); + t.throws(() => payment2[GET_INTERFACE_GUARD](), { + message: + '"In \\"[Symbol(getInterfaceGuard)]\\" method of (precious payment)" may only be applied to a valid instance: "[Alleged: precious payment]"', + }); });