diff --git a/main/.vitepress/config.mjs b/main/.vitepress/config.mjs
index ad304cf56..9230f9981 100644
--- a/main/.vitepress/config.mjs
+++ b/main/.vitepress/config.mjs
@@ -1,4 +1,4 @@
-import { defineConfig } from 'vitepress'
+import { defineConfig } from 'vitepress';
import { nav } from './themeConfig/nav.js';
import { rewrites } from './themeConfig/rewrites.js';
@@ -142,6 +142,7 @@ export default defineConfig({
},
{ text: 'Zoe Overview', link: '/guides/zoe/' },
{ text: 'Contract Upgrade', link: '/guides/zoe/contract-upgrade' },
+ { text: 'Contract Governance', link: '/guides/governance/' },
],
},
{
diff --git a/main/.vitepress/themeConfig/nav.js b/main/.vitepress/themeConfig/nav.js
index 8e4d95fbd..c92cb9ec7 100644
--- a/main/.vitepress/themeConfig/nav.js
+++ b/main/.vitepress/themeConfig/nav.js
@@ -112,12 +112,6 @@ export const nav = [
text: 'New Features',
ariaLabel: 'New Features Menu',
items: [
- {
- text: 'Governance',
- ariaLabel: 'Governance Internal Documentation',
- link:
- 'https://github.com/Agoric/agoric-sdk/blob/master/packages/governance/docs/',
- },
{
text: 'Patterns',
ariaLabel: 'Patterns Source Code Link',
diff --git a/main/glossary/index.md b/main/glossary/index.md
index 713c5f077..3094cadbe 100644
--- a/main/glossary/index.md
+++ b/main/glossary/index.md
@@ -1,6 +1,7 @@
---
sidebar: auto
---
+
# Glossary
This page lists words, expressions, or concepts used by the Agoric technology stack.
@@ -42,7 +43,7 @@ abstract right to participate in a particular exchange.
An amount is comprised of a [brand](#brand) with a [value](#amountvalue). For example, "4 Quatloos"
is an amount with a value of "4" and a brand of the imaginary currency "Quatloos".
-**Important**: Amounts are *descriptions* of digital assets, not the actual assets. They have no
+**Important**: Amounts are _descriptions_ of digital assets, not the actual assets. They have no
economic scarcity or intrinsic value.
For example, to make you an offer to buy a magic sword in a game,
a party sends you an amount describing the asset of 5 Quatloos they're willing to trade for your
@@ -61,6 +62,7 @@ updates its balance to 10 Quatloos. But a deposit of a non-fungible theater tick
five tickets is performed by set union rather than by arithmetic.
`AmountMath` has a single set of polymorphic methods that deal with different asset kinds:
+
- `AssetKind.NAT`: Used with [fungible](#fungible) assets.
Each amount value is a natural number (non-negative integer).
This is the default `AssetKind`.
@@ -82,11 +84,12 @@ For more information, see the [ERTP documentation's AmountMath section](/guides/
and the [ERTP API's AmountMath section](/reference/ertp-api/amount-math).
+
## AmountValue
An AmountValue is the part of an [Amount](#amount) that describes the value of something
that can be owned or shared: how much, how many, or a description of a unique asset, such as
-$3, Pixel(3,2), or “Seat J12 for the show September 27th at 9:00pm”.
+\$3, Pixel(3,2), or “Seat J12 for the show September 27th at 9:00pm”.
For a [fungible](#fungible) Amount, the AmountValue is usually a non-negative **BigInt** such as `10n` or `137n`.
For a [non-fungible](#non-fungible) Amount, the AmountValue might be a [CopySet](/guides/js-programming/far#pass-styles-and-harden) containing strings naming particular rights or objects representing the rights directly.
@@ -128,19 +131,23 @@ and the [ERTP API's Brand section](/reference/ertp-api/brand).
## Bundle
Before a contract can be installed on Zoe, its source code must be bundled. This is done by:
+
```js
import bundleSource from '@endo/bundle-source';
const atomicSwapBundle = await bundleSource(
require.resolve('@agoric/zoe/src/contracts/atomicSwap'),
);
```
+
The installation operation returns an `installation`, which is an object with a single
`getBundle()` method for accessing an installed contract's source code.
In most cases, the bundle contains a base64-encoded zip file that you can
extract for review.
+
```js
const { endoZipBase64 } = await E(installation).getBundle();
```
+
```sh
echo "$endoZipBase64" | base64 -d > bundle.zip
unzip bundle.zip
@@ -156,11 +163,17 @@ unavailable for later use. See
Comparable is a deprecated synonym of [Key](#key).
+## Continuing Invitation Pattern
+
+A continuing invitation presumes a previous offer
+whose result includes capabilities to make further
+invitations. See [`source: 'continuing'`](/guides/getting-started/contract-rpc#source-continuing) in [Specifying Offers](/guides/getting-started/contract-rpc#specifying-offers) for details.
+
## Contract Installation and Contract Instance
-In Agoric documentation, *contract* usually refers to a contract's source code that
-defines how the contract works. A contract's source code is *installed* on Zoe. A
-contract is *instantiated* to create *contract instances*, which are the active
+In Agoric documentation, _contract_ usually refers to a contract's source code that
+defines how the contract works. A contract's source code is _installed_ on Zoe. A
+contract is _instantiated_ to create _contract instances_, which are the active
execution of a contract's code running on Zoe.
For example, a realtor has a standard house selling agreement. The contract is the
@@ -201,7 +214,7 @@ For more details, see the [agoric-sdk `network` package](https://github.com/Agor
## E()
-(Also referred to as *eventual send*) `E()` is a local "bridge" function that
+(Also referred to as _eventual send_) `E()` is a local "bridge" function that
asynchronously invokes methods on local or remote objects, for example those
in another vat, machine, or blockchain. It takes as its argument either a local object
or a [presence](#presence) for a remote object or a promise for a local or remote object, and
@@ -230,7 +243,7 @@ Guide](https://github.com/endojs/endo/blob/HEAD/packages/ses/docs/guide.md)
## ERTP
-*Electronic Rights Transfer Protocol* is a uniform way of transferring tokens and other digital assets,
+_Electronic Rights Transfer Protocol_ is a uniform way of transferring tokens and other digital assets,
both [fungible](#fungible) and [non-fungible](#non-fungible), in JavaScript. All kinds of digital assets
can easily be created and they can be all be transferred in exactly the same ways, with exactly the same security properties.
@@ -247,7 +260,7 @@ see the [ERTP documentation](/guides/ertp/) and [ERTP API documentation](/refere
To give assets for a possible transaction to an impartial third party, who keeps them until specified conditions are satisfied.
For example, Alice wants to sell Bob a ticket for $100. Alice escrows the ticket, and Bob escrows the $100, with Zoe. Zoe
-does not give Alice the $100 or Bob the ticket until it has both items. Since neither Alice nor Bob ever holds both items at
+does not give Alice the \$100 or Bob the ticket until it has both items. Since neither Alice nor Bob ever holds both items at
once, they don't have to trust each other to do the transaction. Zoe automatically escrows payments for transaction offers.
## Eventual Send
@@ -261,18 +274,21 @@ For details, see [`E(zoe).offer(...)`](/reference/zoe-api/zoe#proposals).
## Facet
-A *facet* is an object that exposes an API or particular view of some larger entity, which may be an object itself.
+A _facet_ is an object that exposes an API or particular view of some larger entity, which may be an object itself.
You can make any number of facets of an entity. In JavaScript, you often make a facet that forwards method calls:
+
```js
import { Far } from '@endo/far';
const facet = Far('FacetName', {
myMethod: (...args) => oldObject.method(...args),
});
```
+
Two Agoric uses are:
-- *Deposit Facet*: A facet of a [purse](#purse). Anyone with a reference to its deposit facet object can add
+
+- _Deposit Facet_: A facet of a [purse](#purse). Anyone with a reference to its deposit facet object can add
appropriately branded assets to the purse, but cannot withdraw assets from the purse or find out its balance.
-- *Public Facet*: A set of methods and properties for an object that a developer chooses to be publicly visible and usable.
+- _Public Facet_: A set of methods and properties for an object that a developer chooses to be publicly visible and usable.
## Fungible
@@ -315,6 +331,7 @@ to immediately participate. Otherwise, the contract instance must create any add
Every [offer](#offer) to participate in a contract instance must include an invitation to that instance in the first argument to [`E(zoe).offer(...)`](/reference/zoe-api/zoe#e-zoe-offer-invitation-proposal-paymentpkeywordrecord-offerargs), and any wallet receiving one will validate it via the [InvitationIssuer](#invitationissuer).
An invitation's [amount](#amount) includes the following properties:
+
- The contract's installation in Zoe, including access to its source code.
- The contract instance this invitation is for.
- A handle used to refer to this invitation.
@@ -351,7 +368,7 @@ and the [ERTP API's Issuer section](/reference/ertp-api/issuer).
## Key
-A *Key* is a [passable](#passable) containing no promises or errors, and can
+A _Key_ is a [passable](#passable) containing no promises or errors, and can
thus be synchronously compared for structural equivalence with another piece of data.
If either side of the comparison contains promises and/or errors, equality is indeterminable.
If both are fulfilled down to [presences](#presence) and local state, then either they're the
@@ -361,14 +378,14 @@ Keys can be used as elements of CopySets and CopyBags and as keys of CopyMaps (s
## Keyword
-A *Keyword* is a string that is an ASCII-only [identifier](https://developer.mozilla.org/en-US/docs/Glossary/Identifier),
+A _Keyword_ is a string that is an ASCII-only [identifier](https://developer.mozilla.org/en-US/docs/Glossary/Identifier),
starts with an upper case letter, and is not equal to "NaN" or "Infinity".
See **[Zoe Data Types](/reference/zoe-api/zoe-data-types#keyword)**.
## Mint
-[ERTP](#ertp) has a *mint* object, which creates digital assets. [ZCF](#zcf) provides a different interface to an ERTP mint, called a
-*ZCFMint*. Assets and AssetHolders created using ZCFMints can be used in all the same ways as assets created by other ERTP Mints.
+[ERTP](#ertp) has a _mint_ object, which creates digital assets. [ZCF](#zcf) provides a different interface to an ERTP mint, called a
+_ZCFMint_. Assets and AssetHolders created using ZCFMints can be used in all the same ways as assets created by other ERTP Mints.
They interact with Purses, Payments, Brands, and Issuers in the same ways.
- ERTP mints create digital assets and are the only ERTP objects with the authority to do so.
@@ -413,13 +430,14 @@ Objects have state, behavior, and references. Let's say Object A has references
and C, while B and C do not have references to each other. Thus, A can communicate with B and C,
and B and C cannot communicate with each other. There is an effective zero-cost firewall between B and C.
-An *object capability system* constrains how references are obtained. You can't get one just by
+An _object capability system_ constrains how references are obtained. You can't get one just by
knowing the name of a global variable or a public class. You can only get a reference via:
+
- Creation: Functions that create objects get a reference to them.
- Construction: Constructors can endow their constructed objects with references, including inherited references.
- Introduction:
- A has references to B and C.
- - B and C do *not* have references to each other
+ - B and C do _not_ have references to each other
- A sends B a reference to C.
- B now has a reference to C and can communicate with C.
@@ -447,7 +465,7 @@ can immediately cause the [seat](#seat) to exit, getting back the amount it offe
## Passable
-A *passable* is something that can be sent to and from remote objects.
+A _passable_ is something that can be sent to and from remote objects.
Passables include pass-by-copy primitive values such as numbers and strings and
pass-by-reference values such as Remotables and Promises.
Passables also include [CopyArrays](#copyarray) and [CopyRecords](#copyrecord), which are
@@ -494,6 +512,7 @@ expressing [offer](#offer) conditions regarding what assets will be given,
what is desired in exchange (protected by [offer safety](#offer-safety)), and
an [exit rule](#exit-rule) defining how/when the offer can be canceled.
For example:
+
```js
const myProposal = harden({
give: { Asset: AmountMath.make(quatloosBrand, 4n) },
@@ -501,6 +520,7 @@ const myProposal = harden({
exit: { onDemand: null },
});
```
+
`give` and `want` each associate [Keywords](#keyword) defined by the contract with corresponding [Amounts](#amount) describing respectively what will be given and what is being requested in exchange.
See [Offers](/guides/zoe/proposal).
@@ -581,7 +601,7 @@ See the [Wallet Guide and API](/guides/wallet/).
## ZCF
-*ZCF (Zoe Contract Facet)* is the [facet](#facet) of Zoe exposed to contract code. The Zoe
+_ZCF (Zoe Contract Facet)_ is the [facet](#facet) of Zoe exposed to contract code. The Zoe
Contract Facet methods can be called synchronously by contract code.
See the [ZCF API](/reference/zoe-api/zoe-contract-facet).
diff --git a/main/guides/getting-started/contract-rpc.md b/main/guides/getting-started/contract-rpc.md
index c70018e08..ea03a1728 100644
--- a/main/guides/getting-started/contract-rpc.md
+++ b/main/guides/getting-started/contract-rpc.md
@@ -130,8 +130,41 @@ public facet.
-::: tip InvitationSpec Patterns
-For more `InvitationSpec` examples, see [How to make an offer from a dapp via the smart wallet? \(InvitationSpec Patterns\) · #8082](https://github.com/Agoric/agoric-sdk/discussions/8082) July 2023
+::: tip InvitationSpec Usage
+
+Supposing `spec` is an `InvitationSpec`, its `.source` is one of:
+
+- `purse` - to make an offer with an invitation that is already in the Invitation purse of the smart wallet and agrees with `spec` on `.instance` and `.description` properties. For example, in [dapp-econ-gov](https://github.com/Agoric/dapp-econ-gov), committee members use invitations sent to them when the committee was created.
+
+- `contract` - the smart wallet makes an invitation by calling a method on the public facet of a specified instance: `E(E(zoe).getPublicFacet(spec.instance)[spec.publicInvitationMaker](...spec.invitationArgs)`
+
+- `agoricContract` - for example, from [dapp-inter](https://github.com/Agoric/dapp-inter):
+
+```js
+{
+ source: 'agoricContract',
+ instancePath: ['VaultFactory'],
+ callPipe: [
+ ['getCollateralManager', [toLock.brand]],
+ ['makeVaultInvitation'],
+ ],
+ }
+```
+
+The smart wallet finds the instance using `E(agoricNames).lookup('instance', ...spec.instancePath)` and makes a chain of calls specified by `spec.callPipe`. Each entry in the callPipe is a `[methodName, args?]` pair used to execute a call on the preceding result. The end of the pipe is expected to return an Invitation.
+
+- `continuing` - For example, `dapp-inter` uses the following `InvitationSpec` to adjust a vault:
+
+```js
+{
+ source: 'continuing',
+ previousOffer: vaultOfferId,
+ invitationMakerName: 'AdjustBalances',
+}
+```
+
+In this continuing offer, the smart wallet uses the `spec.previousOffer` id to look up the `.invitationMakers` property of the result of the previous offer. It uses `E(invitationMakers)[spec.invitationMakerName](...spec.invitationArgs)` to make an invitation.
+
:::
The client fills in the proposal, which instructs the `SmartWallet`
diff --git a/main/guides/governance/assets/gov-param-1.mmd b/main/guides/governance/assets/gov-param-1.mmd
new file mode 100644
index 000000000..24963fb2a
--- /dev/null
+++ b/main/guides/governance/assets/gov-param-1.mmd
@@ -0,0 +1,17 @@
+sequenceDiagram
+ autonumber
+
+ actor V1 as Voter1
+
+ participant Core as swaparoo deployer
+
+ participant Zoe
+ participant Ctee as swaparoo committee
+
+ Core-)Zoe: startInstance(committee, ...)
+ Zoe--)Ctee: start({committeeSize: 1})
+ Ctee--)Zoe: { creatorFacet, ... }
+ Zoe--)Core: { creatorFacet, ... }
+ Core--)Ctee: E(creatorFacet).getVoterInvitations()
+ Ctee--)Core: [invitationV1]
+ Core--)V1: invitationV1
diff --git a/main/guides/governance/assets/gov-param-1.svg b/main/guides/governance/assets/gov-param-1.svg
new file mode 100644
index 000000000..f595c87ac
--- /dev/null
+++ b/main/guides/governance/assets/gov-param-1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/main/guides/governance/assets/gov-param-2.mmd b/main/guides/governance/assets/gov-param-2.mmd
new file mode 100644
index 000000000..07cc23774
--- /dev/null
+++ b/main/guides/governance/assets/gov-param-2.mmd
@@ -0,0 +1,15 @@
+sequenceDiagram
+ autonumber
+
+ participant Core as swaparoo deployer
+ participant Zoe
+
+ participant SG as swaparoo governor
+ participant S as swaparoo
+
+ Core-)SG: startInstance(contractGovernor, { governedContractInstallation: swaparoo })
+ SG--)S: startInstance(swaparoo)
+ S--)SG: swaparooFacets
+ SG--)Core: govFacets
+ Core--)SG: E(govFacets.creatorFacet).getCreatorFacet()
+ SG--)Core: swaparoo limitedCreatorFacet
diff --git a/main/guides/governance/assets/gov-param-2.svg b/main/guides/governance/assets/gov-param-2.svg
new file mode 100644
index 000000000..8d491d071
--- /dev/null
+++ b/main/guides/governance/assets/gov-param-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/main/guides/governance/assets/gov-param-3.mmd b/main/guides/governance/assets/gov-param-3.mmd
new file mode 100644
index 000000000..22550db4a
--- /dev/null
+++ b/main/guides/governance/assets/gov-param-3.mmd
@@ -0,0 +1,15 @@
+sequenceDiagram
+ autonumber
+
+ actor V1 as Voter1
+
+ participant Core as swaparoo deployer
+
+ participant Zoe
+ participant Charter as swaparoo charter
+
+ Core-)Charter: startInstance(econCommitteeCharter, ...)
+ Charter--)Core: { creatorFacet, ... }
+ Core--)Charter: E(creatorFacet).getVoterInvitations()
+ Charter--)Core: [invitationQ1]
+ Core--)V1: invitationQ1
diff --git a/main/guides/governance/assets/gov-param-3.svg b/main/guides/governance/assets/gov-param-3.svg
new file mode 100644
index 000000000..02fa35cff
--- /dev/null
+++ b/main/guides/governance/assets/gov-param-3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/main/guides/governance/assets/gov-param-4-accept.mmd b/main/guides/governance/assets/gov-param-4-accept.mmd
new file mode 100644
index 000000000..63de82bf0
--- /dev/null
+++ b/main/guides/governance/assets/gov-param-4-accept.mmd
@@ -0,0 +1,14 @@
+sequenceDiagram
+ autonumber
+
+ actor V1 as Voter1
+
+ participant WF as walletFactory
+ participant Zoe
+ participant Charter as swaparoo charter
+
+ V1-)WF: executeOffer({ id: 'v0-accept-charter', { invitationSpec: source: 'purse', description: 'charter member invitation', instance: swaparooCharter } })
+ WF--)Zoe: offer(invitationQ1)
+ Zoe--)Charter: charterMemberHandler()
+ Charter--)WF: { invitationMakers: { VoteOnParamChange, ... } }
+ WF--)V1: { payouts: {} }
diff --git a/main/guides/governance/assets/gov-param-4-accept.svg b/main/guides/governance/assets/gov-param-4-accept.svg
new file mode 100644
index 000000000..088d8ae76
--- /dev/null
+++ b/main/guides/governance/assets/gov-param-4-accept.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/main/guides/governance/assets/gov-param-5-putq.mmd b/main/guides/governance/assets/gov-param-5-putq.mmd
new file mode 100644
index 000000000..e69de29bb
diff --git a/main/guides/governance/assets/inter-ec-invoke-method.png b/main/guides/governance/assets/inter-ec-invoke-method.png
new file mode 100644
index 000000000..86a19427a
Binary files /dev/null and b/main/guides/governance/assets/inter-ec-invoke-method.png differ
diff --git a/main/guides/governance/index.md b/main/guides/governance/index.md
new file mode 100644
index 000000000..7ea23e49c
--- /dev/null
+++ b/main/guides/governance/index.md
@@ -0,0 +1,271 @@
+# Contract Governance
+
+To help build systems with a good balance of decentralization and executive control, the Agoric platform includes, in addition to chain-wide governance included in the Cosmos SDK platform, an [@agoric/governance](https://github.com/Agoric/agoric-sdk/blob/master/packages/governance/#readme) package with a flexible architecture supporting 3 main forms of control over a contract:
+
+- **Parameter Governance**: control of parameter values
+- **API governance**: capability to invoke particular methods called out by the contract's authors
+- **Offer filters**: capability to disable the use of some offers/invitations to deal with emergencies
+
+::: tip Governance Package Aims
+
+The emphasis, throughout the design of the governance package, is on ensuring that clients of a contract have visibility into who can make changes, what changes they can make, and what the current state of the controlled values is.
+
+An [Attacker's Guide](https://github.com/Agoric/agoric-sdk/blob/master/packages/governance/docs/AttackGuide.md) explains the assurances that the governance package aims to support.
+
+:::
+
+## Parameter Governance
+
+In [Starting a Contract Instance](../zoe/#starting-a-contract-instance), we saw that contracts are parameterized by _terms_. Parameter governance supports
+having an authorized party, the _Electorate_, change such parameters while the contract is running.
+
+In [dapp-agoric-basics](https://github.com/Agoric/dapp-agoric-basics), the
+swaparoo contract has a governed `Fee` amount parameter:
+
+```js
+const paramTypes = harden(
+ /** @type {const} */ ({
+ Fee: ParamTypes.AMOUNT,
+ }),
+);
+```
+
+## Reusing Contracts for Electorate, Election Manager
+
+This dapp uses the `committee.js` contract from `@agoric/governance` for its
+electorate. The core eval deployment script starts the swaparoo committee,
+gets invitations, and sends them to the the smart wallets of the voters.
+In `test-vote-by-committee.js`, the committee consists of just 1 voter.
+
+
+
+## Adding Parameter Governance to a Contract
+
+Adding parameter governance to a contract consists mainly of using `handleParamGovernance(...)`.
+We pass it `zcf` so that it can `getTerms()` for initial parameter values, and we
+pass `paramTypes` to specify governed parameters and their types. `initialPoserInvitation`
+is necessary to set up replacing the electorate. `storageNode` and `marshaller` are used
+to publish values of the parameters to vstorage.
+
+```js
+import { handleParamGovernance } from '@agoric/governance/src/contractHelper.js';
+
+export const start = async (zcf, privateArgs, baggage) => {
+...
+ const { publicMixin, makeDurableGovernorFacet, params } =
+ await handleParamGovernance(
+ zcf,
+ privateArgs.initialPoserInvitation,
+ paramTypes,
+ privateArgs.storageNode,
+ privateArgs.marshaller,
+ );
+...
+}
+```
+
+We get back
+
+- `params`: read-only parameter access. `params.getFee().value` gives us the current value of the `Fee` parameter at any time, for example.
+- `publicMixin`: to make the parameter values available via the contract `publicFacet`
+- `makeDurableGovernorFacet`: to combine the contract's existing creator facet methods (known as the `limitedCreatorFacet`) with methods for _changing parameter values_.
+
+```js
+export const start = async (zcf, privateArgs, baggage) => {
+...
+ const publicFacet = Far('Public', {
+ makeFirstInvitation,
+ ...publicMixin,
+ });
+ const limitedCreatorFacet = Far('Creator', {
+ makeCollectFeesInvitation() {
+ return makeCollectFeesInvitation(zcf, feeSeat, feeBrand, 'Fee');
+ },
+ });
+ const { governorFacet } = makeDurableGovernorFacet(
+ baggage,
+ limitedCreatorFacet,
+ );
+ return harden({ publicFacet, creatorFacet: governorFacet });
+}
+```
+
+## Starting a Governed Contract via its Governor
+
+Changing parameter values is something only the electorate is authorized to do.
+To enforce this, a governed contract is started by a _contract governor_.
+The contract governor holds the fully-functional creator facet that includes
+the capability to change the parameters. The caller that starts the governor
+gets only the `limitedCreatorFacet`.
+
+
+
+_For clarity, some Zoe API details are ommitted from this figure._
+
+## Putting a question via an Election Manager
+
+An _ElectionManager_ is responsible for letting an appropriate party call `addQuestion()`. The `econCommitteeCharter.js` contract from `@agoric/inter-protocol` is sufficiently general to handle most forms of parameter
+governance, so we reuse it here. Swaparoo core eval deployment likewise
+starts this contract, asks it for invitations, and sends them to the voters:
+
+
+
+_For clarity, some Zoe API details are ommitted from this figure._
+
+The committee participant then instructs their smart wallet to
+redeem the charter invitation to get a capability to put a question using `VoteOnParamChange`,
+using `v0-accept-charter` to identify this offer:
+
+```js
+test.serial('Voter0 accepts charter, committee invitations', async t => {
+...
+ await victor.acceptCharterInvitation('v0-accept-charter');
+...
+});
+```
+
+
+
+To exercises their `VoteOnParamChange` capability,
+the committee participant makes a [continuing invitation](/glossary/#continuing-invitation-pattern),
+referring back to `v0-accept-charter` as `charterAcceptOfferId`:
+
+```js
+const makeVoter = (t, wallet, wellKnown) => {
+...
+ const putQuestion = async (offerId, params, deadline) => {
+ const instance = await wellKnown.instance[contractName]; // swaparoo instance handle
+ const path = { paramPath: { key: 'governedParams' } };
+
+ /** @type {import('@agoric/inter-protocol/src/econCommitteeCharter.js').ParamChangesOfferArgs} */
+ const offerArgs = harden({ deadline, params, instance, path });
+
+ /** @type {import('@agoric/smart-wallet/src/offers.js').OfferSpec} */
+ const offer = {
+ id: offerId,
+ invitationSpec: {
+ source: 'continuing',
+ previousOffer: NonNullish(charterAcceptOfferId),
+ invitationMakerName: 'VoteOnParamChange',
+ },
+ offerArgs,
+ proposal: {},
+ };
+ return doOffer(offer);
+ };
+};
+```
+
+The `offerArgs` include a deadline and details of the params to change:
+
+```js
+test.serial('vote to change swap fee', async t => {
+...
+ const targetFee = IST(50n, 100n); // 50 / 100 = 0.5 IST
+ const changes = { Fee: targetFee };
+...
+ const deadline = BigInt(new Date(2024, 6, 1, 9, 10).valueOf() / 1000);
+ const result = await victor.putQuestion('proposeToSetFee', changes, deadline);
+ t.log('question is posed', result);
+...
+});
+```
+
+## Voting on a question
+
+The voter likewise executes an offer to accept their invitation to participate in the committee,
+using `v0-join-committee` to identify this offer:
+
+```js
+test.serial('Voter0 accepts charter, committee invitations', async t => {
+...
+ await victor.acceptCommitteeInvitation('v0-join-committee', 0);
+...
+});
+```
+
+To exercise their capability to vote, they make a continuing invitation
+referring back to `v0-join-committee` as `committeeOfferId`:
+
+```js
+const makeVoter = (t, wallet, wellKnown) => {
+...
+ const vote = async (offerId, details, position) => {
+ const chosenPositions = [details.positions[position]];
+
+ /** @type {import('./wallet-tools.js').OfferSpec} */
+ const offer = {
+ id: offerId,
+ invitationSpec: {
+ source: 'continuing',
+ previousOffer: NonNullish(committeeOfferId),
+ invitationMakerName: 'makeVoteInvitation',
+ invitationArgs: harden([chosenPositions, details.questionHandle]),
+ },
+ proposal: {},
+ };
+ return doOffer(offer);
+ };
+...
+};
+```
+
+Each question has a unique `questionHandle` object, part of the `details`
+published to vstorage.
+
+```js
+test.serial('vote to change swap fee', async t => {
+...
+ const details = await vstorage.get(`published.committee.swaparoo.latestQuestion`);
+ t.is(details.electionType, 'param_change');
+ const voteResult = await victor.vote('voteToSetFee', details, 0);
+ t.log('victor voted:', voteResult);
+...
+});
+```
+
+Once the deadline is reached, the contract governor is notified that the question
+carried. It instructs the swaparoo contract to change the fee.
+
+```js
+test.serial('vote to change swap fee', async t => {
+...
+ const swapPub = E(zoe).getPublicFacet(
+ swapPowers.instance.consume[contractName],
+ );
+...
+ const after = await E(swapPub).getAmount('Fee');
+ t.deepEqual(after, targetFee);
+});
+```
+
+The swaparoo contract also publishes its updated parameters to vstorage.
+
+## API Governance
+
+Just as parameter governance lets an electorate change parameters of a
+governed contract, API governance lets the electorate exercise contract APIs;
+in particular: creator facet methods on a governed contract.
+
+For example, in Inter Protocol, the Economic Committee can add and remove
+oracle operators:
+
+
+
+For details, see:
+
+- [dapp-econ-gov](https://github.com/Agoric/dapp-econ-gov)
+- `vaultFactory` contract in `@agoric/inter-protocol`
+
+## Offer Filters
+
+An electorate can instruct a contract to have Zoe filter the offers that
+will be passed to the ocntract. See
+
+- [zcf.setOfferFilter(strings)](/reference/zoe-api/zoe-contract-facet#zcf-setofferfilter-strings).
+- [How to add a contract level pause feature? · agoric-sdk · Discussion #8172](https://github.com/Agoric/agoric-sdk/discussions/8172)