Skip to content

Commit

Permalink
feat: add the ability to override param values (#9829)
Browse files Browse the repository at this point in the history
closes: #9596
refs: #9748

## Description

When upgrading VaultFactory, restore the VaultDirector's parameters to the values that exist on chain.

### Security Considerations

No particular security implications.

### Scaling Considerations

Run only on upgrade. No impact.

### Documentation Considerations

No user visible impact.

### Testing Considerations

being tested manually thoroughly. The change to `typeParamManager.js` has a unit test.

### Upgrade Considerations

Make it possible to preserve values that have been updated on chain. It would have been bad to lose the updated value of `ReferencedUI`.
  • Loading branch information
mergify[bot] authored Aug 13, 2024
2 parents d32d6ce + ef2963a commit b9f5667
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 11 deletions.
23 changes: 15 additions & 8 deletions packages/governance/src/contractGovernance/typedParamManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,24 +141,31 @@ harden(makeParamManagerSync);
* @param {ZCF<GovernanceTerms<M>>} zcf
* @param {I} invitations invitation objects, which must come from privateArgs
* @param {M} paramTypesMap
* @param {object} [overrides]
* @returns {TypedParamManager<M & {[K in keyof I]: 'invitation'}>}
*/
export const makeParamManagerFromTerms = (
publisherKit,
zcf,
invitations,
paramTypesMap,
overrides,
) => {
if (overrides) {
console.log('TPM ', { overrides });
}

const { governedParams } = zcf.getTerms();
/** @type {Array<[Keyword, SyncSpecTuple | AsyncSpecTuple]>} */
/** @type {Array<[Keyword, (SyncSpecTuple | AsyncSpecTuple)]>} */
const makerSpecEntries = Object.entries(paramTypesMap).map(
([paramKey, paramType]) => [
paramKey,
/** @type {SyncSpecTuple} */ ([
paramType,
governedParams[paramKey].value,
]),
],
([paramKey, paramType]) => {
const value =
overrides && overrides[paramKey]
? overrides[paramKey]
: governedParams[paramKey].value;

return [paramKey, /** @type {SyncSpecTuple} */ ([paramType, value])];
},
);
// Every governed contract has an Electorate param that starts as `initialPoserInvitation` private arg
for (const [name, invitation] of Object.entries(invitations)) {
Expand Down
24 changes: 24 additions & 0 deletions packages/governance/test/unitTests/typedParamManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,27 @@ test('Unknown', async t => {
await paramManager.updateParams({ Surprise: ['gift', 'party'] });
t.deepEqual(paramManager.getSurprise(), ['gift', 'party']);
});

test('makeParamManagerFromTerms overrides', async t => {
const terms = harden({
governedParams: {
Mmr: { type: 'nat', value: makeRatio(150n, drachmaKit.brand) },
},
});
const issuerKeywordRecord = harden({
Ignore: drachmaKit.issuer,
});
const { zcf } = await setupZCFTest(issuerKeywordRecord, terms);

const paramManager = await makeParamManagerFromTerms(
makeStoredPublisherKit(),
// @ts-expect-error missing governance terms
zcf,
{ Electorate: zcf.makeInvitation(() => null, 'mock poser invitation') },
{
Mmr: 'ratio',
},
{ Mmr: makeRatio(100n, drachmaKit.brand) },
);
t.deepEqual(paramManager.getMmr().numerator.value, 100n);
});
39 changes: 37 additions & 2 deletions packages/inter-protocol/src/proposals/upgrade-vaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@ export const upgradeVaults = async (powers, { options }) => {
}
}

const readCurrentDirectorParams = async () => {
const { publicFacet: directorPF } = kit;

await null;

const subscription = E(directorPF).getElectorateSubscription();
const notifier = makeNotifierFromAsyncIterable(subscription);
let { value, updateCount } = await notifier.getUpdateSince(0n);
// @ts-expect-error It's an amount.
while (AmountMath.isEmpty(value.current.MinInitialDebt.value)) {
({ value, updateCount } = await notifier.getUpdateSince(updateCount));
trace(
`minInitialDebt was empty, retried`,
value.current.MinInitialDebt.value,
);
}

return harden({
MinInitialDebt: value.current.MinInitialDebt.value,
ReferencedUI: value.current.ReferencedUI.value,
RecordingPeriod: value.current.RecordingPeriod.value,
ChargingPeriod: value.current.ChargingPeriod.value,
});
};
const directorParamOverrides = await readCurrentDirectorParams();

const readManagerParams = async () => {
const { publicFacet: directorPF } = kit;

Expand All @@ -99,9 +125,17 @@ export const upgradeVaults = async (powers, { options }) => {
const notifier = makeNotifierFromAsyncIterable(subscription);
let { value, updateCount } = await notifier.getUpdateSince(0n);
// @ts-expect-error It's an amount.
while (AmountMath.isEmpty(value.current.DebtLimit.value)) {
if (AmountMath.isEmpty(value.current.DebtLimit.value)) {
// The parameters might have been empty at start, and the notifier might
// give the first state before the current state.
trace(`debtLimit was empty, retrying`, value.current.DebtLimit.value);
({ value, updateCount } = await notifier.getUpdateSince(updateCount));
trace(`debtLimit was empty, retried`, value.current.DebtLimit.value);

// @ts-expect-error It's an amount.
if (AmountMath.isEmpty(value.current.DebtLimit.value)) {
trace('debtLimit was empty after retrying');
throw Error('🚨Governed parameters empty after retry, Giving up');
}
}
trace(kwd, 'params at', updateCount, 'are', value.current);
params[kwd] = harden({
Expand Down Expand Up @@ -138,6 +172,7 @@ export const upgradeVaults = async (powers, { options }) => {
initialPoserInvitation: poserInvitation,
initialShortfallInvitation: shortfallInvitation,
managerParams: managerParamValues,
directorParamOverrides,
});

const upgradeResult = await E(kit.adminFacet).upgradeContract(
Expand Down
4 changes: 3 additions & 1 deletion packages/inter-protocol/src/vaultFactory/vaultFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ harden(meta);
* string,
* import('./params.js').VaultManagerParamOverrides
* >;
* directorParamOverrides: [object];
* }} privateArgs
* @param {import('@agoric/swingset-liveslots').Baggage} baggage
*/
Expand All @@ -86,6 +87,7 @@ export const start = async (zcf, privateArgs, baggage) => {
storageNode,
auctioneerInstance,
managerParams,
directorParamOverrides,
} = privateArgs;

trace('awaiting debtMint');
Expand Down Expand Up @@ -117,7 +119,6 @@ export const start = async (zcf, privateArgs, baggage) => {
marshaller,
);
/** a powerful object; can modify the invitation */
trace('awaiting makeParamManagerFromTerms');
const vaultDirectorParamManager = await makeParamManagerFromTerms(
{
publisher: governanceSubscriptionKit.publication,
Expand All @@ -129,6 +130,7 @@ export const start = async (zcf, privateArgs, baggage) => {
[SHORTFALL_INVITATION_KEY]: initialShortfallInvitation,
},
vaultDirectorParamTypes,
directorParamOverrides,
);

const director = provideDirector(
Expand Down

0 comments on commit b9f5667

Please sign in to comment.