Skip to content

Commit

Permalink
Manage treasury 3189 (#3310)
Browse files Browse the repository at this point in the history
* feat!: use contractGovernor to govern Treasury using ParamManager

extract params to a separate file
integrate contract governance into treasury
swingset test for treasury governance

closes #3189
closes #3473

* chore: minor cleanups: drop extra logs, standardize asserts, tsc fix

* chore: improve typescript declarations

* feat: add noAction electorate for assurance of no governance changes

* chore: validate() in test checks the installations

* fix: import types.js into params so bundle is usable in tests

* fix: remove spurious distinction in naming of Liquidity keyword

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
Chris-Hibbert and mergify[bot] authored Oct 8, 2021
1 parent 1372ee8 commit c19f141
Show file tree
Hide file tree
Showing 30 changed files with 1,758 additions and 643 deletions.
53 changes: 53 additions & 0 deletions packages/governance/src/noActionElectorate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// @ts-check

import { Far } from '@agoric/marshal';
import { makeSubscriptionKit } from '@agoric/notifier';
import { makePromiseKit } from '@agoric/promise-kit';

/**
* This Electorate visibly has no members, takes no votes, and approves no
* changes.
*
* @type {ContractStartFn}
*/
const start = zcf => {
const { subscription } = makeSubscriptionKit();

/** @type {ElectoratePublic} */
const publicFacet = Far('publicFacet', {
getQuestionSubscription: () => subscription,
getOpenQuestions: () => {
/** @type {Handle<'Question'>[]} */
const noQuestions = [];
const questionsPromise = makePromiseKit();
questionsPromise.resolve(noQuestions);
return questionsPromise.promise;
},
getName: () => 'no Action electorate',
getInstance: zcf.getInstance,
getQuestion: () => {
throw Error(`noActionElectorate doesn't have questions.`);
},
});

/** @type {ElectorateCreatorFacet} */
const creatorFacet = Far('creatorFacet', {
getPoserInvitation: () => {
return zcf.makeInvitation(() => {},
`noActionElectorate doesn't allow posing questions`);
},
addQuestion() {
throw Error(`noActionElectorate doesn't add questions.`);
},
getVoterInvitations: () => {
throw Error(`No Action Electorate doesn't have invitations.`);
},
getQuestionSubscription: () => subscription,
getPublicFacet: () => publicFacet,
});

return { publicFacet, creatorFacet };
};

harden(start);
export { start };
21 changes: 17 additions & 4 deletions packages/governance/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,10 +352,23 @@
* @property {Handle<'Question'>} questionHandle
*/

/**
* @callback GetParams - getParams() retrieves a Record containing
* keyword pairs with descriptions of parameters under governance.
* @returns {Record<Keyword,ParamDescription>}
*/

/**
* @callback GetParam - getParam() retrieves a description of a parameter under
* governance.
* @param {string} name
* @returns {ParamDescription}
*/

/**
* @typedef {Object} ParamManagerBase
* @property {() => Record<Keyword,ParamDescription>} getParams
* @property {(name: string) => ParamDescription} getParam
* @property {GetParams} getParams
* @property {GetParam} getParam
* @property {() => Subscription<ParamDescription>} getSubscription
*/

Expand Down Expand Up @@ -444,7 +457,7 @@
*
* A powerful facet that carries access to both the creatorFacet to be passed
* to the caller and the paramManager, which will be used exclusively by the
* ContractGovenor.
* ContractGovernor.
* @property {() => Promise<LimitedCreatorFacet>} getLimitedCreatorFacet
* @property {() => ParamManagerRetriever} getParamMgrRetriever
*/
Expand Down Expand Up @@ -494,7 +507,7 @@

/**
* @callback SetupGovernance
* @param {ERef<ParamManagerRetriever>} retriever
* @param {ERef<ParamManagerRetriever>} paramManagerRetriever
* @param {ERef<PoserFacet>} poserFacet
* @param {Instance} contractInstance
* @param {Timer} timer
Expand Down
2 changes: 1 addition & 1 deletion packages/store/src/legacyMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { assert, details as X, q } from '@agoric/assert';

/**
* This module and its fraternal sibling legacyWeakMap exist only to
* each a transition to the modern `store` system, are deprecated,
* ease a transition to the modern `store` system, are deprecated,
* and will eventually disappear. They are needed for now to support
* some of the uses of the old behavior that are not compatible with
* the new. The constraint imposed by the new is that only passables can
Expand Down
66 changes: 54 additions & 12 deletions packages/treasury/bundles/install-on-chain.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
// @ts-check
import { E } from '@agoric/eventual-send';

import '@agoric/governance/exported.js';

import liquidateBundle from './bundle-liquidateMinimum.js';
import autoswapBundle from './bundle-multipoolAutoswap.js';
import stablecoinBundle from './bundle-stablecoinMachine.js';
import contractGovernorBundle from './bundle-contractGovernor.js';
import noActionElectorateBundle from './bundle-noActionElectorate.js';
import binaryVoteCounterBundle from './bundle-binaryVoteCounter.js';
import { governedParameterTerms } from '../src/params';
import { Far } from '@agoric/marshal';

const SECONDS_PER_HOUR = 60n * 60n;
const SECONDS_PER_DAY = 24n * SECONDS_PER_HOUR;
Expand Down Expand Up @@ -58,11 +65,18 @@ export async function installOnChain({
['liquidate', liquidateBundle],
['autoswap', autoswapBundle],
['stablecoin', stablecoinBundle],
['contractGovernor', contractGovernorBundle],
['noActionElectorate', noActionElectorateBundle],
['binaryCounter', binaryVoteCounterBundle],

];
const [
liquidationInstall,
autoswapInstall,
stablecoinMachineInstall,
contractGovernorInstall,
noActionElectorateInstall,
binaryCounterInstall,
] = await Promise.all(
nameBundles.map(async ([name, bundle]) => {
// Install the bundle in Zoe.
Expand All @@ -74,42 +88,63 @@ export async function installOnChain({
}),
);

// The Electorate is a no-action electorate, so the testNet runs without
// anyone having the ability to change the treasury's parameters.
const {
creatorFacet: electorateCreatorFacet,
instance: electorateInstance,
} = await E(zoeWPurse).startInstance(noActionElectorateInstall);

const loanParams = {
chargingPeriod: SECONDS_PER_HOUR,
recordingPeriod: SECONDS_PER_DAY,
poolFee,
protocolFee,
};

const terms = harden({
const treasuryTerms = harden({
autoswapInstall,
liquidationInstall,
priceAuthority,
loanParams,
timerService: chainTimerService,
governedParams: governedParameterTerms,
bootstrapPaymentValue,
});
const governorTerms = harden({
timer: chainTimerService,
electorateInstance,
governedContractInstallation: stablecoinMachineInstall,
governed: {
terms: treasuryTerms,
issuerKeywordRecord: {},
privateArgs: harden({ feeMintAccess }),
},
});

const privateArgs = harden({ feeMintAccess });

const { instance, creatorFacet } = await E(zoeWPurse).startInstance(
stablecoinMachineInstall,
const {
creatorFacet: governorCreatorFacet,
} = await E(zoeWPurse).startInstance(
contractGovernorInstall,
undefined,
terms,
privateArgs,
governorTerms,
harden({ electorateCreatorFacet }),
);

const treasuryInstance = await E(governorCreatorFacet).getInstance();
const [
ammInstance,
invitationIssuer,
{
issuers: { Governance: govIssuer, RUN: centralIssuer },
brands: { Governance: govBrand, RUN: centralBrand },
},
treasuryCreator,
] = await Promise.all([
E(creatorFacet).getAMM(),
E(E(governorCreatorFacet).getCreatorFacet()).getAMM(),
E(zoeWPurse).getInvitationIssuer(),
E(zoeWPurse).getTerms(instance),
E(zoeWPurse).getTerms(treasuryInstance),
E(governorCreatorFacet).getCreatorFacet()
]);

const treasuryUiDefaults = {
Expand All @@ -123,12 +158,15 @@ export async function installOnChain({

// Look up all the board IDs.
const boardIdValue = [
['INSTANCE_BOARD_ID', instance],
['INSTANCE_BOARD_ID', treasuryInstance],
['INSTALLATION_BOARD_ID', stablecoinMachineInstall],
['RUN_ISSUER_BOARD_ID', centralIssuer],
['RUN_BRAND_BOARD_ID', centralBrand],
['AMM_INSTALLATION_BOARD_ID', autoswapInstall],
['LIQ_INSTALLATION_BOARD_ID', liquidationInstall],
['BINARY_COUNTER_INSTALLATION_BOARD_ID', binaryCounterInstall],
['NO_ACTION_INSTALLATION_BOARD_ID', noActionElectorateInstall],
['CONTRACT_GOVERNOR_INSTALLATION_BOARD_ID', contractGovernorInstall],
['AMM_INSTANCE_BOARD_ID', ammInstance],
['INVITE_BRAND_BOARD_ID', E(invitationIssuer).getBrand()],
];
Expand All @@ -146,7 +184,7 @@ export async function installOnChain({
// Install the names in agoricNames.
const nameAdminUpdates = [
[uiConfigAdmin, treasuryUiDefaults.CONTRACT_NAME, treasuryUiDefaults],
[instanceAdmin, treasuryUiDefaults.CONTRACT_NAME, instance],
[instanceAdmin, treasuryUiDefaults.CONTRACT_NAME, treasuryInstance],
[instanceAdmin, treasuryUiDefaults.AMM_NAME, ammInstance],
[brandAdmin, 'TreasuryGovernance', govBrand],
[issuerAdmin, 'TreasuryGovernance', govIssuer],
Expand All @@ -159,5 +197,9 @@ export async function installOnChain({
),
);

return creatorFacet;
const voteCreator = Far('treasury vote creator', {
voteOnParamChange: E(governorCreatorFacet).voteOnParamChange,
});

return { treasuryCreator, voteCreator };
}
4 changes: 3 additions & 1 deletion packages/treasury/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
"@agoric/promise-kit": "^0.2.27",
"@agoric/store": "^0.6.5",
"@agoric/swingset-vat": "^0.22.1",
"@agoric/zoe": "^0.19.1"
"@agoric/zoe": "^0.19.1",
"@agoric/governance": "^0.2.0",
"@agoric/same-structure": "^0.1.20"
},
"devDependencies": {
"@agoric/babel-standalone": "^7.14.3",
Expand Down
16 changes: 16 additions & 0 deletions packages/treasury/scripts/build-bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ async function main() {
`@agoric/zoe/src/contracts/multipoolAutoswap/multipoolAutoswap.js`,
`${dirname}/../bundles/bundle-multipoolAutoswap.js`,
],
[
'@agoric/governance/src/contractGovernor.js',
`${dirname}/../bundles/bundle-contractGovernor.js`,
],
[
'@agoric/governance/src/committee.js',
`${dirname}/../bundles/bundle-committee.js`,
],
[
'@agoric/governance/src/noActionElectorate.js',
`${dirname}/../bundles/bundle-noActionElectorate.js`,
],
[
'@agoric/governance/src/binaryVoteCounter.js',
`${dirname}/../bundles/bundle-binaryVoteCounter.js`,
],
];
for (const [contractFilename, outputPath] of contractOutputs) {
// eslint-disable-next-line no-await-in-loop
Expand Down
88 changes: 88 additions & 0 deletions packages/treasury/src/params.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// @ts-check

import './types.js';

import { buildParamManager, ParamType } from '@agoric/governance';

export const POOL_FEE_KEY = 'PoolFee';
export const PROTOCOL_FEE_KEY = 'ProtocolFee';

export const CHARGING_PERIOD_KEY = 'ChargingPeriod';
export const RECORDING_PERIOD_KEY = 'RecordingPeriod';

export const INITIAL_MARGIN_KEY = 'InitialMargin';
export const LIQUIDATION_MARGIN_KEY = 'LiquidationMargin';
export const INTEREST_RATE_KEY = 'InterestRate';
export const LOAN_FEE_KEY = 'LoanFee';

export const governedParameterTerms = {
loanParams: [POOL_FEE_KEY, PROTOCOL_FEE_KEY],
poolParams: [
CHARGING_PERIOD_KEY,
RECORDING_PERIOD_KEY,
INITIAL_MARGIN_KEY,
LIQUIDATION_MARGIN_KEY,
INTEREST_RATE_KEY,
LOAN_FEE_KEY,
],
};

/** @type {{ FEE: 'fee', POOL: 'pool' }} */
export const ParamKey = {
FEE: 'fee',
POOL: 'pool',
};

/** @type {MakeFeeParamManager} */
export const makeFeeParamManager = ammFees => {
// @ts-ignore buildParamManager doesn't describe all the update methods
return buildParamManager([
{
name: POOL_FEE_KEY,
value: ammFees.poolFee,
type: ParamType.NAT,
},
{
name: PROTOCOL_FEE_KEY,
value: ammFees.protocolFee,
type: ParamType.NAT,
},
]);
};

/** @type {MakePoolParamManager} */
export const makePoolParamManager = (loanParams, rates) => {
// @ts-ignore buildParamManager doesn't describe all the update methods
return buildParamManager([
{
name: CHARGING_PERIOD_KEY,
value: loanParams.chargingPeriod,
type: ParamType.NAT,
},
{
name: RECORDING_PERIOD_KEY,
value: loanParams.recordingPeriod,
type: ParamType.NAT,
},
{
name: INITIAL_MARGIN_KEY,
value: rates.initialMargin,
type: ParamType.RATIO,
},
{
name: LIQUIDATION_MARGIN_KEY,
value: rates.liquidationMargin,
type: ParamType.RATIO,
},
{
name: INTEREST_RATE_KEY,
value: rates.interestRate,
type: ParamType.RATIO,
},
{
name: LOAN_FEE_KEY,
value: rates.loanFee,
type: ParamType.RATIO,
},
]);
};
Loading

0 comments on commit c19f141

Please sign in to comment.