Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage treasury 3189 #3310

Merged
merged 8 commits into from
Oct 8, 2021
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';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggled to find a test where I could walk through this code... it looks like nothing in the tests in this package uses install-on-chain.js. But I see the cosmic-swingset integration test does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was how I discovered that I needed to update this was that the integration test failed. It looks like it may be used in bringing up a chain, so I tried to make the settings as realistic as possible.

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],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm struggling to judge whether these board / naming updates are correct. I suspect there are some external constraints... from dapp-treasury?

installOnChain has no doc comment.

vs-code is complaining that it can't find the NameHub type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was mostly guessing here. I should ask @michaelfig to review this PR and opine on the changes to `install-on-chain.js

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This list is specifically for the UI config that is returned to dapp-treasury/ui. So, unless the Treasury UI is going to use those board IDs, please don't bother with them.

Rather than a 5-member committee that has no active members, I would prefer a "don't do anything except unilaterally replace my electorate" facet that could be handed to the bootstrap vat, so that the bootstrap could install the actual initial electorate.

It's fine if that's done in a future PR, but please explicitly mark all the inescapable committee governance in this file as a temporary hack just to allow the treasury not to crash during installation.

Copy link
Contributor Author

@Chris-Hibbert Chris-Hibbert Oct 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way governance works is that the electorate has to exist before the contract is instantiated, in order to make the governance visible to all. I'm fine with saying there's no governance during testNet, but the way I'd do that is by building a do-nothing electorate. I think that's straightforward, and may be useful in other, similar circumstances.

It would be visible that there's a place for governance, but no one can initiate governance actions in the current installation.

This list is specifically for the UI config that is returned to dapp-treasury/ui. So, unless the Treasury UI is going to use those board IDs, please don't bother with them.

I'm not going to add code to the Treasury UI at this point, but it ought to be able to help the user review invitations. It should be able to identify the installations. All these installations are relevant to the user.

['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