From 4186af7aa6310d8175ddc5cf3672cf5ed72fe0f5 Mon Sep 17 00:00:00 2001 From: Kate Sills Date: Tue, 10 Aug 2021 16:24:54 -0700 Subject: [PATCH] chore: add makeFeePurse method --- packages/zoe/src/zoeService/feePurse.js | 41 +++++++++++++++++ packages/zoe/src/zoeService/types.js | 10 +++++ packages/zoe/src/zoeService/zoe.js | 4 ++ packages/zoe/test/unitTests/test-zoe.js | 25 +++++++++++ .../zoe/test/unitTests/zcf/setupZcfTest.js | 12 ++++- .../zoe/test/unitTests/zoe/test-feePurse.js | 45 +++++++++++++++++++ 6 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 packages/zoe/src/zoeService/feePurse.js create mode 100644 packages/zoe/test/unitTests/zoe/test-feePurse.js diff --git a/packages/zoe/src/zoeService/feePurse.js b/packages/zoe/src/zoeService/feePurse.js new file mode 100644 index 000000000000..5a3348657ac6 --- /dev/null +++ b/packages/zoe/src/zoeService/feePurse.js @@ -0,0 +1,41 @@ +// @ts-check + +import { Far } from '@agoric/marshal'; + +/** + * + * @param {Issuer} feeIssuer + * @returns {{ + * makeFeePurse: MakeFeePurse + * isFeePurse: (feePurse: Purse) => boolean + * }} + */ +const setupMakeFeePurse = feeIssuer => { + const feePurses = new WeakSet(); + + /** @type {MakeFeePurse} */ + const makeFeePurse = () => { + const purse = feeIssuer.makeEmptyPurse(); + /** @type {FeePurse} */ + const feePurse = Far('feePurse', { + ...purse, + }); + feePurses.add(feePurse); + + // After keeping the purse methods, we throw away the purse + return feePurse; + }; + + /** + * @param {Purse} feePurse + */ + const isFeePurse = feePurse => feePurses.has(feePurse); + + return { + makeFeePurse, + isFeePurse, + }; +}; + +harden(setupMakeFeePurse); +export { setupMakeFeePurse }; diff --git a/packages/zoe/src/zoeService/types.js b/packages/zoe/src/zoeService/types.js index a1a15a94ce71..54c729a88496 100644 --- a/packages/zoe/src/zoeService/types.js +++ b/packages/zoe/src/zoeService/types.js @@ -39,6 +39,7 @@ * object with the instance, installation, description, invitation * handle, and any custom properties specific to the contract. * @property {GetFeeIssuer} getFeeIssuer + * @property {MakeFeePurse} makeFeePurse */ /** @@ -46,6 +47,15 @@ * @returns {Issuer} */ +/** + * @typedef {Purse} FeePurse + */ + +/** + * @callback MakeFeePurse + * @returns {FeePurse} + */ + /** * @callback GetPublicFacet * @param {Instance} instance diff --git a/packages/zoe/src/zoeService/zoe.js b/packages/zoe/src/zoeService/zoe.js index 773db359d4d3..c4fbfb00f770 100644 --- a/packages/zoe/src/zoeService/zoe.js +++ b/packages/zoe/src/zoeService/zoe.js @@ -25,6 +25,7 @@ import { makeOffer } from './offer/offer.js'; import { makeInvitationQueryFns } from './invitationQueries.js'; import { setupCreateZCFVat } from './createZCFVat.js'; import { createFeeMint } from './feeMint.js'; +import { setupMakeFeePurse } from './feePurse.js'; /** * Create an instance of Zoe. @@ -58,6 +59,8 @@ const makeZoeKit = ( shutdownZoeVat, ); + const { makeFeePurse } = setupMakeFeePurse(feeIssuer); + // This method contains the power to create a new ZCF Vat, and must // be closely held. vatAdminSvc is even more powerful - any vat can // be created. We severely restrict access to vatAdminSvc for this reason. @@ -108,6 +111,7 @@ const makeZoeKit = ( install, startInstance, offer, + makeFeePurse, // The functions below are getters only and have no impact on // state within Zoe diff --git a/packages/zoe/test/unitTests/test-zoe.js b/packages/zoe/test/unitTests/test-zoe.js index 8ea42f635653..d52e1180c23c 100644 --- a/packages/zoe/test/unitTests/test-zoe.js +++ b/packages/zoe/test/unitTests/test-zoe.js @@ -4,6 +4,7 @@ import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; import path from 'path'; +import { AmountMath } from '@agoric/ertp'; import { E } from '@agoric/eventual-send'; import { makePromiseKit } from '@agoric/promise-kit'; import { passStyleOf } from '@agoric/marshal'; @@ -371,3 +372,27 @@ test(`zoe.getInvitationDetails - no invitation`, async t => { message: /A Zoe invitation is required, not "\[undefined\]"/, }); }); + +test(`zoe.makeFeePurse`, async t => { + const { zoe, zcf, feeMintAccess } = await setupZCFTest(); + + const feePurse = E(zoe).makeFeePurse(); + const feeIssuer = E(zoe).getFeeIssuer(); + const feeBrand = await E(feeIssuer).getBrand(); + + const zcfMint = await zcf.registerFeeMint('RUN', feeMintAccess); + const { zcfSeat, userSeat } = zcf.makeEmptySeatKit(); + + const fee1000 = AmountMath.make(feeBrand, 1000n); + zcfMint.mintGains({ Fee: fee1000 }, zcfSeat); + + zcfSeat.exit(); + const payment = await E(userSeat).getPayout('Fee'); + await E(feePurse).deposit(payment); + + t.true(AmountMath.isEqual(await E(feePurse).getCurrentAmount(), fee1000)); + + await E(feePurse).withdraw(fee1000); + + t.true(AmountMath.isEmpty(await E(feePurse).getCurrentAmount())); +}); diff --git a/packages/zoe/test/unitTests/zcf/setupZcfTest.js b/packages/zoe/test/unitTests/zcf/setupZcfTest.js index a5466f3b7370..edf759b05d48 100644 --- a/packages/zoe/test/unitTests/zcf/setupZcfTest.js +++ b/packages/zoe/test/unitTests/zcf/setupZcfTest.js @@ -23,7 +23,7 @@ export const setupZCFTest = async (issuerKeywordRecord, terms) => { }; // The contract provides the `zcf` via `setTestJig` upon `start`. const fakeVatAdmin = makeFakeVatAdmin(setZCF); - const { zoeService: zoe } = makeZoeKit(fakeVatAdmin.admin); + const { zoeService: zoe, feeMintAccess } = makeZoeKit(fakeVatAdmin.admin); const bundle = await bundleSource(contractRoot); const installation = await E(zoe).install(bundle); const { creatorFacet, instance } = await E(zoe).startInstance( @@ -34,5 +34,13 @@ export const setupZCFTest = async (issuerKeywordRecord, terms) => { const { vatAdminState } = fakeVatAdmin; // @ts-ignore fix types to understand that zcf is always defined assert(zcf !== undefined); - return { zoe, zcf, instance, installation, creatorFacet, vatAdminState }; + return { + zoe, + zcf, + instance, + installation, + creatorFacet, + vatAdminState, + feeMintAccess, + }; }; diff --git a/packages/zoe/test/unitTests/zoe/test-feePurse.js b/packages/zoe/test/unitTests/zoe/test-feePurse.js new file mode 100644 index 000000000000..2f60da9c6070 --- /dev/null +++ b/packages/zoe/test/unitTests/zoe/test-feePurse.js @@ -0,0 +1,45 @@ +// @ts-check + +// eslint-disable-next-line import/no-extraneous-dependencies +import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; + +import { makeIssuerKit, AssetKind, AmountMath } from '@agoric/ertp'; + +import { setupMakeFeePurse } from '../../../src/zoeService/feePurse.js'; + +const setup = () => { + const runIssuerKit = makeIssuerKit('RUN', AssetKind.NAT, { + decimalPlaces: 6, + }); + const { makeFeePurse, isFeePurse } = setupMakeFeePurse(runIssuerKit.issuer); + return { makeFeePurse, isFeePurse, runIssuerKit }; +}; + +test('feePurse starts empty', async t => { + const { makeFeePurse } = setup(); + const feePurse = makeFeePurse(); + + t.true(AmountMath.isEmpty(feePurse.getCurrentAmount())); +}); + +test('depositing into and withdrawing from feePurse', async t => { + const { makeFeePurse, runIssuerKit } = setup(); + const feePurse = makeFeePurse(); + + const run1000 = AmountMath.make(runIssuerKit.brand, 1000n); + const payment = runIssuerKit.mint.mintPayment(run1000); + feePurse.deposit(payment); + + t.true(AmountMath.isEqual(feePurse.getCurrentAmount(), run1000)); + + feePurse.withdraw(run1000); + + t.true(AmountMath.isEmpty(feePurse.getCurrentAmount())); +}); + +test('isFeePurse', async t => { + const { makeFeePurse, isFeePurse } = setup(); + const feePurse = makeFeePurse(); + + t.true(isFeePurse(feePurse)); +});