Skip to content

Commit

Permalink
fix: tighten invitation proposal patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed Dec 4, 2022
1 parent babdfcb commit 2527e69
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 6 deletions.
15 changes: 13 additions & 2 deletions packages/governance/src/committee.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { makeStore, makeHeapFarInstance, M } from '@agoric/store';
import { natSafeMath } from '@agoric/zoe/src/contractSupport/index.js';
import { E } from '@endo/eventual-send';

import { EmptyProposal } from '@agoric/zoe/src/typeGuards.js';
import { makeHandle } from '@agoric/zoe/src/makeHandle.js';
import {
getOpenQuestions,
Expand Down Expand Up @@ -96,7 +97,12 @@ const start = (zcf, privateArgs) => {
return E(voteCap).submitVote(voterHandle, positions, 1n);
};

return zcf.makeInvitation(continuingVoteHandler, 'vote');
return zcf.makeInvitation(
continuingVoteHandler,
'vote',
undefined,
EmptyProposal,
);
},
},
),
Expand All @@ -107,7 +113,12 @@ const start = (zcf, privateArgs) => {
// This will produce unique descriptions because
// makeCommitteeVoterInvitation() is only called within the following loop,
// which is only called once per Electorate.
return zcf.makeInvitation(offerHandler, `Voter${index}`);
return zcf.makeInvitation(
offerHandler,
`Voter${index}`,
undefined,
EmptyProposal,
);
};

const { committeeName, committeeSize } = zcf.getTerms();
Expand Down
8 changes: 7 additions & 1 deletion packages/governance/src/electorateTools.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EmptyProposal } from '@agoric/zoe/src/typeGuards';
import { E } from '@endo/eventual-send';
import { deeplyFulfilled, Far } from '@endo/marshal';

Expand Down Expand Up @@ -86,7 +87,12 @@ const getQuestion = (questionHandleP, questionStore) =>
*/
const getPoserInvitation = (zcf, addQuestion) => {
const questionPoserHandler = () => Far(`questionPoser`, { addQuestion });
return zcf.makeInvitation(questionPoserHandler, `questionPoser`);
return zcf.makeInvitation(
questionPoserHandler,
`questionPoser`,
undefined,
EmptyProposal,
);
};

harden(startCounter);
Expand Down
9 changes: 7 additions & 2 deletions packages/governance/src/noActionElectorate.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { makePublishKit } from '@agoric/notifier';
import { makePromiseKit } from '@endo/promise-kit';
import { makeHeapFarInstance } from '@agoric/store';
import { EmptyProposal } from '@agoric/zoe/src/typeGuards.js';

import { ElectoratePublicI, ElectorateCreatorI } from './typeGuards.js';

Expand Down Expand Up @@ -37,8 +38,12 @@ const start = zcf => {

const creatorFacet = makeHeapFarInstance('creatorFacet', ElectorateCreatorI, {
getPoserInvitation() {
return zcf.makeInvitation(() => {},
`noActionElectorate doesn't allow posing questions`);
return zcf.makeInvitation(
() => {},
`noActionElectorate doesn't allow posing questions`,
undefined,
EmptyProposal,
);
},
addQuestion(_instance, _question) {
throw Error(`noActionElectorate doesn't add questions.`);
Expand Down
4 changes: 4 additions & 0 deletions packages/inter-protocol/src/psm/psm.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ export const start = async (zcf, privateArgs, baggage) => {
M.splitRecord({
give: { In: anchorAmountShape },
want: M.or({ Out: stableAmountShape }, {}),
multiples: 1n,
exit: M.any(),
}),
);
},
Expand All @@ -290,6 +292,8 @@ export const start = async (zcf, privateArgs, baggage) => {
M.splitRecord({
give: { In: stableAmountShape },
want: M.or({ Out: anchorAmountShape }, {}),
multiples: 1n,
exit: M.any(),
}),
);
},
Expand Down
152 changes: 152 additions & 0 deletions packages/inter-protocol/src/psm/psmCharter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import '@agoric/governance/src/exported.js';
import { makeScalarMapStore, M, makeHeapFarInstance, fit } from '@agoric/store';
import '@agoric/zoe/exported.js';
import '@agoric/zoe/src/contracts/exported.js';
import {
EmptyProposal,
InstanceHandleShape,
} from '@agoric/zoe/src/typeGuards.js';
import { BrandShape } from '@agoric/ertp';
import { TimestampShape } from '@agoric/swingset-vat/src/vats/timer/typeGuards.js';
import { E } from '@endo/far';

/**
* @file
*
* This contract makes it possible for those who govern the PSM to call for
* votes on changes. A more complete implementation would validate parameters,
* constrain deadlines and possibly split the ability to call for votes into
* separate capabilities for finer grain encapsulation.
*/

/**
* @typedef {object} ParamChangesOfferArgs
* @property {bigint} deadline
* @property {Instance} instance
* @property {Record<string, unknown>} params
* @property {{paramPath: { key: string }}} [path]
*/
const ParamChangesOfferArgsShape = harden(
M.split(
{
deadline: TimestampShape,
instance: InstanceHandleShape,
params: M.recordOf(M.string(), M.any()),
},
M.partial({
path: { paramPath: { key: M.string() } },
}),
),
);

/**
* @param {ZCF<{binaryVoteCounterInstallation:Installation}>} zcf
*/
export const start = async zcf => {
const { binaryVoteCounterInstallation: counter } = zcf.getTerms();
/** @type {MapStore<Instance,GovernedContractFacetAccess<{},{}>>} */
const instanceToGovernor = makeScalarMapStore();

const makeParamInvitation = () => {
/**
* @param {ZCFSeat} seat
* @param {ParamChangesOfferArgs} args
*/
const voteOnParamChanges = (seat, args) => {
fit(args, ParamChangesOfferArgsShape);
seat.exit();

const {
params,
instance,
deadline,
path = { paramPath: { key: 'governedApi' } },
} = args;
const psmGovernor = instanceToGovernor.get(instance);
return E(psmGovernor).voteOnParamChanges(counter, deadline, {
...path,
changes: params,
});
};

return zcf.makeInvitation(
voteOnParamChanges,
'vote on param changes',
undefined,
EmptyProposal,
);
};

const makeOfferFilterInvitation = (instance, strings, deadline) => {
const voteOnOfferFilterHandler = seat => {
seat.exit();

const psmGovernor = instanceToGovernor.get(instance);
return E(psmGovernor).voteOnOfferFilter(counter, deadline, strings);
};

return zcf.makeInvitation(
voteOnOfferFilterHandler,
'vote on offer filter',
undefined,
EmptyProposal,
);
};

const MakerShape = M.interface('PSM Charter InvitationMakers', {
VoteOnParamChange: M.call().returns(M.promise()),
VoteOnPauseOffers: M.call(
InstanceHandleShape,
M.arrayOf(M.string()),
TimestampShape,
).returns(M.promise()),
});
const invitationMakers = makeHeapFarInstance(
'PSM Invitation Makers',
MakerShape,
{
VoteOnParamChange: makeParamInvitation,
VoteOnPauseOffers: makeOfferFilterInvitation,
},
);

const charterMemberHandler = seat => {
seat.exit();
return harden({ invitationMakers });
};

const psmCharterCreatorI = M.interface('PSM Charter creatorFacet', {
addInstance: M.call(InstanceHandleShape, M.any())
.optional(BrandShape, BrandShape)
.returns(),
makeCharterMemberInvitation: M.call().returns(M.promise()),
});

const creatorFacet = makeHeapFarInstance(
'PSM Charter creatorFacet',
psmCharterCreatorI,
{
/**
* @param {Instance} psmInstance
* @param {GovernedContractFacetAccess<{},{}>} psmGovernorFacet
* @param {Brand} [anchor] for diagnostic use only
* @param {Brand} [minted] for diagnostic use only
*/
addInstance: (psmInstance, psmGovernorFacet, anchor, minted) => {
console.log('psmCharter: adding instance', { minted, anchor });
instanceToGovernor.init(psmInstance, psmGovernorFacet);
},
makeCharterMemberInvitation: () =>
zcf.makeInvitation(
charterMemberHandler,
'PSM charter member invitation',
undefined,
EmptyProposal,
),
},
);

return harden({ creatorFacet });
};

export const INVITATION_MAKERS_DESC = 'PSM charter member invitation';
11 changes: 10 additions & 1 deletion packages/zoe/src/contractSupport/zoeHelpers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { fit, keyEQ } from '@agoric/store';
import '../../exported.js';

import { fit, keyEQ, M } from '@agoric/store';
import { E } from '@endo/eventual-send';
import { makePromiseKit } from '@endo/promise-kit';
import { AssetKind } from '@agoric/ertp';
Expand Down Expand Up @@ -216,6 +218,13 @@ export const depositToSeat = async (zcf, recipientSeat, amounts, payments) => {
const invitation = zcf.makeInvitation(
reallocateAfterDeposit,
'temporary seat for deposit',
undefined,
harden({
give: M.any(),
want: {},
multiples: 1n,
exit: { onDemand: null },
}),
);
const proposal = harden({ give: amounts });
harden(payments);
Expand Down
6 changes: 6 additions & 0 deletions packages/zoe/src/typeGuards.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ export const FullProposalShape = harden({
});
/** @see {Proposal} type */
export const ProposalShape = M.splitRecord({}, FullProposalShape, {});
export const EmptyProposal = harden({
give: {},
want: {},
multiples: 1n,
exit: { onDemand: null },
});

export const isOnDemandExitRule = exit => {
const [exitKey] = Object.getOwnPropertyNames(exit);
Expand Down

0 comments on commit 2527e69

Please sign in to comment.