Skip to content

Commit

Permalink
Merge pull request #6197 from Agoric/6108-psm-governance-cli
Browse files Browse the repository at this point in the history
6108 psm governance cli
  • Loading branch information
mergify[bot] authored Sep 15, 2022
2 parents a390655 + 19cf0fb commit 99274dd
Show file tree
Hide file tree
Showing 8 changed files with 314 additions and 34 deletions.
182 changes: 174 additions & 8 deletions packages/agoric-cli/src/commands/psm.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ import {

const last = xs => xs[xs.length - 1];

function collectValues(val, memo) {
memo.push(val);
return memo;
}

const { vstorage, fromBoard, agoricNames } = await makeRpcUtils({ fetch });

/**
Expand Down Expand Up @@ -81,7 +86,7 @@ export const makePsmCommand = async logger => {

const marshaller = boardSlottingMarshaller();

const lookupInstance = ([minted, anchor]) => {
const lookupPsmInstance = ([minted, anchor]) => {
const name = `psm-${minted}-${anchor}`;
const instance = agoricNames.instance[name];
if (!instance) {
Expand Down Expand Up @@ -113,6 +118,7 @@ export const makePsmCommand = async logger => {
psm
.command('info')
.description('show governance info about the PSM (BROKEN)')
// TODO DRY with https://github.com/Agoric/agoric-sdk/issues/6181
.requiredOption(
'--pair [Minted.Anchor]',
'token pair (Minted.Anchor)',
Expand Down Expand Up @@ -155,24 +161,59 @@ export const makePsmCommand = async logger => {
.action(async function () {
const opts = this.opts();
console.warn('running with options', opts);
const instance = await lookupInstance(opts.pair);
const instance = await lookupPsmInstance(opts.pair);
// @ts-expect-error RpcRemote types not real instances
const spendAction = makePSMSpendAction(instance, agoricNames.brand, opts);
outputAction(spendAction);
});

psm
.command('vote')
.description('prepare an offer to vote')
.option('--offerId [number]', 'Offer id', String(Date.now()))
.action(function () {
.command('committee')
.description('join the economic committee')
.option('--offerId [number]', 'Offer id', Number, Date.now())
.action(async function () {
const opts = this.opts();

const { economicCommittee } = agoricNames.instance;
assert(economicCommittee, 'missing economicCommittee');

/** @type {import('../lib/psm.js').OfferSpec} */
const offer = {
id: Number(opts.offerId),
invitationSpec: {
source: 'purse',
// @ts-expect-error rpc
instance: economicCommittee,
description: 'Voter0', // XXX it may not always be
},
proposal: {},
};

outputAction({
method: 'executeOffer',
offer,
});

console.warn('Now execute the prepared offer');
});

psm
.command('charter')
.description('prepare an offer to accept the charter invitation')
.option('--offerId [number]', 'Offer id', Number, Date.now())
.action(async function () {
const opts = this.opts();

const { psmCharter } = agoricNames.instance;
assert(psmCharter, 'missing psmCharter');

/** @type {import('../lib/psm.js').OfferSpec} */
const offer = {
id: opts.offerId,
id: Number(opts.offerId),
invitationSpec: {
source: 'purse',
instance: opts.instance,
// @ts-expect-error rpc
instance: psmCharter,
description: 'PSM charter member invitation',
},
proposal: {},
Expand All @@ -182,6 +223,131 @@ export const makePsmCommand = async logger => {
method: 'executeOffer',
offer,
});

console.warn('Now execute the prepared offer');
});

psm
.command('proposePauseOffers')
.description('propose a vote')
.option('--offerId [number]', 'Offer id', Number, Date.now())
.requiredOption(
'--pair [Minted.Anchor]',
'token pair (Minted.Anchor)',
s => s.split('.'),
['IST', 'AUSD'],
)
.requiredOption(
'--previousOfferId [number]',
'offer that had continuing invitation result',
Number,
)
.requiredOption(
'--substring [string]',
'an offer string to pause (can be repeated)',
collectValues,
[],
)
.option(
'--deadline [minutes]',
'minutes from now to close the vote',
Number,
1,
)
.action(async function () {
const opts = this.opts();

const psmInstance = lookupPsmInstance(opts.pair);

/** @type {import('../lib/psm.js').OfferSpec} */
const offer = {
id: Number(opts.offerId),
invitationSpec: {
source: 'continuing',
previousOffer: opts.previousOfferId,
invitationMakerName: 'VoteOnPauseOffers',
// ( instance, strings list, timer deadline seconds )
invitationArgs: harden([
psmInstance,
opts.substring,
BigInt(opts.deadline * 60 + Math.round(Date.now() / 1000)),
]),
},
proposal: {},
};

outputAction({
method: 'executeOffer',
offer,
});

console.warn('Now execute the prepared offer');
});

psm
.command('vote')
.description('vote on a question (hard-coded for now))')
.option('--offerId [number]', 'Offer id', Number, Date.now())
.requiredOption(
'--previousOfferId [number]',
'offer that had continuing invitation result',
Number,
)
.requiredOption(
'--pair [Minted.Anchor]',
'token pair (Minted.Anchor)',
s => s.split('.'),
['IST', 'AUSD'],
)
.requiredOption(
'--forPosition [number]',
'index of one position to vote for (within the question description.positions); ',
Number,
)
.action(async function () {
const opts = this.opts();

const questionHandleCapDataStr = await vstorage.read(
'published.committees.Initial_Economic_Committee.latestQuestion',
);
const questionDescriptions = storageHelper.unserialize(
questionHandleCapDataStr,
fromBoard,
);

assert(questionDescriptions, 'missing questionDescriptions');
assert(
questionDescriptions.length === 1,
'multiple questions not supported',
);

const questionDesc = questionDescriptions[0];
// TODO support multiple position arguments
const chosenPositions = [questionDesc.positions[opts.forPosition]];
assert(chosenPositions, `undefined position index ${opts.forPosition}`);

/** @type {import('../lib/psm.js').OfferSpec} */
const offer = {
id: Number(opts.offerId),
invitationSpec: {
source: 'continuing',
previousOffer: opts.previousOfferId,
invitationMakerName: 'makeVoteInvitation',
// (positionList, questionHandle)
invitationArgs: harden([
chosenPositions,
questionDesc.questionHandle,
]),
},
proposal: {},
};

outputAction({
method: 'executeOffer',
offer,
});

console.warn('Now execute the prepared offer');
});

return psm;
Expand Down
16 changes: 12 additions & 4 deletions packages/agoric-cli/src/commands/wallet.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-check
/* eslint-disable func-names */
/* global fetch, process */
import { execSync } from 'child_process';
import {
iterateLatest,
makeCastingSpec,
Expand Down Expand Up @@ -30,15 +31,22 @@ export const makeWalletCommand = async () => {
normalizeAddress,
)
.requiredOption('--offer [filename]', 'path to file with prepared offer')
.option('--dry-run', 'spit out the command instead of running it')
.action(function () {
const { from, offer } = this.opts();
const { dryRun, from, offer } = this.opts();
const { chainName, rpcAddrs } = networkConfig;

const cmd = `agd --node=${rpcAddrs[0]} --chain-id=${chainName} --from=${from} tx swingset wallet-action --allow-spend "$(cat ${offer})"`;

process.stdout.write('Run this interactive command in shell:\n\n');
process.stdout.write(cmd);
process.stdout.write('\n');
if (dryRun) {
process.stdout.write('Run this interactive command in shell:\n\n');
process.stdout.write(cmd);
process.stdout.write('\n');
} else {
const yesCmd = `${cmd} --yes`;
console.log('Executing ', yesCmd);
execSync(yesCmd);
}
});

wallet
Expand Down
17 changes: 11 additions & 6 deletions packages/agoric-cli/src/lib/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const exampleAsset = {
issuer: { boardId: null, iface: undefined },
petname: 'Agoric staking token',
};
/** @typedef {import('@agoric/smart-wallet/src/smartWallet').BrandDescriptor & {brand: {boardId: string, iface: string}}} AssetDescriptor */
/** @typedef {import('@agoric/smart-wallet/src/smartWallet').BrandDescriptor & {brand: import('./rpc').RpcRemote}} AssetDescriptor */

/** @param {AssetDescriptor[]} assets */
export const makeAmountFormatter = assets => amt => {
Expand All @@ -40,11 +40,16 @@ export const makeAmountFormatter = assets => amt => {
petname,
displayInfo: { assetKind, decimalPlaces = 0 },
} = asset;
const petnameStr = Array.isArray(petname) ? petname.join('.') : petname;
if (assetKind !== 'nat') return [['?'], petnameStr];
/** @type {[qty: number, petname: string]} */
const scaled = [Number(value) / 10 ** decimalPlaces, petnameStr];
return scaled;
const name = Array.isArray(petname) ? petname.join('.') : petname;
switch (assetKind) {
case 'nat':
/** @type {[petname: string, qty: number]} */
return [name, Number(value) / 10 ** decimalPlaces];
case 'set':
return [name, value];
default:
return [name, ['?']];
}
};

export const asPercent = ratio => {
Expand Down
1 change: 1 addition & 0 deletions packages/agoric-cli/src/lib/psm.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const simpleOffers = (state, agoricNames) => {
payouts,
} = o;
const entry = Object.entries(agoricNames.instance).find(
// @ts-expect-error xxx RpcRemote
([_name, candidate]) => candidate === instance,
);
const instanceName = entry ? entry[0] : '???';
Expand Down
19 changes: 6 additions & 13 deletions packages/agoric-cli/src/lib/rpc.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
/* eslint-disable @jessie.js/no-nested-await */
// @ts-check
/* eslint-disable @jessie.js/no-nested-await */
/* global Buffer, fetch, process */

import { NonNullish } from '@agoric/assert';

/**
* @template K, V
* @typedef {[key: K, val: V]} Entry<K,V>
* @typedef {{boardId: string, iface: string}} RpcRemote
*/

export const networkConfigUrl = agoricNetSubdomain =>
Expand Down Expand Up @@ -225,21 +224,15 @@ harden(storageHelper);
/**
* @param {IdMap} ctx
* @param {VStorage} vstorage
* @param {string[]} [kinds]
* @returns {Promise<{brand: Record<string, RpcRemote>, instance: Record<string, RpcRemote>}>}
*/
export const makeAgoricNames = async (
ctx,
vstorage,
kinds = ['brand', 'instance'],
) => {
export const makeAgoricNames = async (ctx, vstorage) => {
const entries = await Promise.all(
kinds.map(async kind => {
['brand', 'instance'].map(async kind => {
const content = await vstorage.read(`published.agoricNames.${kind}`);
const parts = storageHelper.unserialize(content, ctx).at(-1);

/** @type {Entry<string, Record<string, any>>} */
const entry = [kind, Object.fromEntries(parts)];
return entry;
return [kind, Object.fromEntries(parts)];
}),
);
return Object.fromEntries(entries);
Expand Down
Loading

0 comments on commit 99274dd

Please sign in to comment.