-
Notifications
You must be signed in to change notification settings - Fork 226
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
Manage treasury 3189 #3310
Changes from 7 commits
57aad11
e01c968
937a5a1
fc1e381
ebe1f36
6ce7f17
2052b0a
cbc1df0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 }; |
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; | ||
|
@@ -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. | ||
|
@@ -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 = { | ||
|
@@ -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], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
vs-code is complaining that it can't find the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This list is specifically for the UI config that is returned to 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
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()], | ||
]; | ||
|
@@ -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], | ||
|
@@ -159,5 +197,9 @@ export async function installOnChain({ | |
), | ||
); | ||
|
||
return creatorFacet; | ||
const voteCreator = Far('treasury vote creator', { | ||
voteOnParamChange: E(governorCreatorFacet).voteOnParamChange, | ||
}); | ||
|
||
return { treasuryCreator, voteCreator }; | ||
} |
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, | ||
}, | ||
]); | ||
}; |
There was a problem hiding this comment.
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 thecosmic-swingset
integration test does.There was a problem hiding this comment.
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.