agoric 0.18.4 → 0.18.5-dev-800c2bb.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,13 +4,10 @@
4
4
  /* eslint-disable func-names */
5
5
  /* global fetch, process */
6
6
  import { Command } from 'commander';
7
+ import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
7
8
  import { asPercent } from '../lib/format.js';
8
- import { makePSMSpendAction } from '../lib/psm.js';
9
- import {
10
- makeRpcUtils,
11
- boardSlottingMarshaller,
12
- storageHelper,
13
- } from '../lib/rpc.js';
9
+ import { makeRpcUtils, storageHelper } from '../lib/rpc.js';
10
+ import { outputExecuteOfferAction } from '../lib/wallet.js';
14
11
 
15
12
  // Adapted from https://gist.github.com/dckc/8b5b2f16395cb4d7f2ff340e0bc6b610#file-psm-tool
16
13
 
@@ -33,35 +30,15 @@ function collectValues(val, memo) {
33
30
  return memo;
34
31
  }
35
32
 
36
- const { vstorage, fromBoard, agoricNames } = await makeRpcUtils({ fetch });
37
-
38
33
  /**
39
- *
40
- * @param {[Minted: string, Anchor: string]} pair
41
- */
42
- const getGovernanceState = async ([Minted, Anchor]) => {
43
- const govContent = await vstorage.readLatest(
44
- `published.psm.${Minted}.${Anchor}.governance`,
45
- );
46
- assert(govContent, 'no gov content');
47
- const { current: governance } = last(
48
- storageHelper.unserializeTxt(govContent, fromBoard),
49
- );
50
- const { [`psm.${Minted}.${Anchor}`]: instance } = agoricNames.instance;
51
-
52
- return { instance, governance };
53
- };
54
-
55
- /**
56
- *
57
34
  * @param {import('anylogger').Logger} logger
58
35
  */
59
- export const makePsmCommand = async logger => {
36
+ export const makePsmCommand = logger => {
60
37
  const psm = new Command('psm').description('PSM commands').usage(
61
38
  `
62
39
  WALLET=my-wallet
63
40
  export AGORIC_NET=ollinet
64
-
41
+
65
42
  # provision wallet if necessary
66
43
  agd keys add $WALLET
67
44
  # provision with faucet, e.g.
@@ -74,7 +51,7 @@ export const makePsmCommand = async logger => {
74
51
  # prepare an offer to send
75
52
  # (offerId is optional but best to specify as each must be higher than the last and the default value is a huge number)
76
53
  agops psm swap --wantMinted 6 --feePct 0.01 --offerId 12 > offer-12.json
77
-
54
+
78
55
  # watch for results in one terminal
79
56
  agoric wallet watch --from $WALLET
80
57
 
@@ -84,28 +61,45 @@ export const makePsmCommand = async logger => {
84
61
  `,
85
62
  );
86
63
 
87
- const marshaller = boardSlottingMarshaller();
64
+ const rpcTools = async () => {
65
+ const utils = await makeRpcUtils({ fetch });
88
66
 
89
- const lookupPsmInstance = ([minted, anchor]) => {
90
- const name = `psm-${minted}-${anchor}`;
91
- const instance = agoricNames.instance[name];
92
- if (!instance) {
93
- logger.debug('known instances:', agoricNames.instance);
94
- throw new Error(`Unknown instance ${name}`);
95
- }
96
- return instance;
97
- };
67
+ const lookupPsmInstance = ([minted, anchor]) => {
68
+ const name = `psm-${minted}-${anchor}`;
69
+ const instance = utils.agoricNames.instance[name];
70
+ if (!instance) {
71
+ logger.debug('known instances:', utils.agoricNames.instance);
72
+ throw Error(`Unknown instance ${name}`);
73
+ }
74
+ return instance;
75
+ };
76
+
77
+ /**
78
+ *
79
+ * @param {[Minted: string, Anchor: string]} pair
80
+ */
81
+ const getGovernanceState = async ([Minted, Anchor]) => {
82
+ const govContent = await utils.vstorage.readLatest(
83
+ `published.psm.${Minted}.${Anchor}.governance`,
84
+ );
85
+ assert(govContent, 'no gov content');
86
+ const { current: governance } = last(
87
+ storageHelper.unserializeTxt(govContent, utils.fromBoard),
88
+ );
89
+ const { [`psm.${Minted}.${Anchor}`]: instance } =
90
+ utils.agoricNames.instance;
98
91
 
99
- /** @param {import('../lib/psm.js').BridgeAction} bridgeAction */
100
- const outputAction = bridgeAction => {
101
- const capData = marshaller.serialize(bridgeAction);
102
- process.stdout.write(JSON.stringify(capData));
92
+ return { instance, governance };
93
+ };
94
+
95
+ return { ...utils, lookupPsmInstance, getGovernanceState };
103
96
  };
104
97
 
105
98
  psm
106
99
  .command('list')
107
100
  .description('list all PSMs in network')
108
101
  .action(async function () {
102
+ const { vstorage } = await rpcTools();
109
103
  const mints = await vstorage.keys('published.psm');
110
104
  for (const minted of mints) {
111
105
  const anchors = await vstorage.keys(`published.psm.${minted}`);
@@ -125,8 +119,9 @@ export const makePsmCommand = async logger => {
125
119
  s => s.split('.'),
126
120
  ['IST', 'AUSD'],
127
121
  )
128
- .action(async function () {
129
- const { pair } = this.opts();
122
+ .action(async function (opts) {
123
+ const { pair } = opts;
124
+ const { getGovernanceState } = await rpcTools();
130
125
  const { governance } = await getGovernanceState(pair);
131
126
  console.log('psm governance params', Object.keys(governance));
132
127
  console.log('MintLimit', governance.MintLimit.value);
@@ -154,83 +149,29 @@ export const makePsmCommand = async logger => {
154
149
  )
155
150
  // wantMinted and giveMinted options conflict.
156
151
  // TODO use .conflicts() after https://github.com/Agoric/agoric-sdk/issues/6181
157
- .option('--wantMinted [DOLLARS]', 'amount of anchor tokens to give', Number)
158
- .option('--giveMinted [DOLLARS]', 'amount of minted tokens to give', Number)
152
+ .option('--wantMinted <DOLLARS>', 'amount of anchor tokens to give', Number)
153
+ .option('--giveMinted <DOLLARS>', 'amount of minted tokens to give', Number)
159
154
  .option('--feePct [%]', 'Gas fee percentage', Number)
160
- .option('--offerId [number]', 'Offer id', Number, Date.now())
161
- .action(async function () {
162
- const opts = this.opts();
155
+ .option('--offerId <string>', 'Offer id', String, `swap-${Date.now()}`)
156
+ .action(async function (
157
+ /** @type {Parameters<typeof Offers.psm.swap>[2]} */ opts,
158
+ ) {
163
159
  console.warn('running with options', opts);
160
+ const { agoricNames, lookupPsmInstance } = await rpcTools();
164
161
  const instance = await lookupPsmInstance(opts.pair);
165
- // @ts-expect-error xxx RpcRemote
166
- const spendAction = makePSMSpendAction(instance, agoricNames.brand, opts);
167
- outputAction(spendAction);
168
- });
169
-
170
- psm
171
- .command('committee')
172
- .description('join the economic committee')
173
- .option('--offerId [number]', 'Offer id', Number, Date.now())
174
- .action(async function () {
175
- const opts = this.opts();
176
-
177
- const { economicCommittee } = agoricNames.instance;
178
- assert(economicCommittee, 'missing economicCommittee');
179
-
180
- /** @type {import('../lib/psm.js').OfferSpec} */
181
- const offer = {
182
- id: Number(opts.offerId),
183
- invitationSpec: {
184
- source: 'purse',
185
- // @ts-expect-error xxx RpcRemote
186
- instance: economicCommittee,
187
- description: 'Voter0', // XXX it may not always be
188
- },
189
- proposal: {},
190
- };
191
-
192
- outputAction({
193
- method: 'executeOffer',
194
- offer,
195
- });
196
-
197
- console.warn('Now execute the prepared offer');
198
- });
199
-
200
- psm
201
- .command('charter')
202
- .description('prepare an offer to accept the charter invitation')
203
- .option('--offerId [number]', 'Offer id', Number, Date.now())
204
- .action(async function () {
205
- const opts = this.opts();
206
-
207
- const { psmCharter } = agoricNames.instance;
208
- assert(psmCharter, 'missing psmCharter');
209
-
210
- /** @type {import('../lib/psm.js').OfferSpec} */
211
- const offer = {
212
- id: Number(opts.offerId),
213
- invitationSpec: {
214
- source: 'purse',
215
- // @ts-expect-error xxx RpcRemote
216
- instance: psmCharter,
217
- description: 'PSM charter member invitation',
218
- },
219
- proposal: {},
220
- };
221
-
222
- outputAction({
223
- method: 'executeOffer',
224
- offer,
225
- });
226
-
227
- console.warn('Now execute the prepared offer');
162
+ const offer = Offers.psm.swap(instance, agoricNames.brand, opts);
163
+ outputExecuteOfferAction(offer);
228
164
  });
229
165
 
230
166
  psm
231
167
  .command('proposePauseOffers')
232
168
  .description('propose a vote')
233
- .option('--offerId [number]', 'Offer id', Number, Date.now())
169
+ .option(
170
+ '--offerId <string>',
171
+ 'Offer id',
172
+ String,
173
+ `proposePauseOffers-${Date.now()}`,
174
+ )
234
175
  .requiredOption(
235
176
  '--pair [Minted.Anchor]',
236
177
  'token pair (Minted.Anchor)',
@@ -238,33 +179,31 @@ export const makePsmCommand = async logger => {
238
179
  ['IST', 'AUSD'],
239
180
  )
240
181
  .requiredOption(
241
- '--psmCharterAcceptOfferId [number]',
182
+ '--charterAcceptOfferId <string>',
242
183
  'offer that had continuing invitation result',
243
- Number,
244
184
  )
245
185
  .requiredOption(
246
- '--substring [string]',
186
+ '--substring <string>',
247
187
  'an offer string to pause (can be repeated)',
248
188
  collectValues,
249
189
  [],
250
190
  )
251
191
  .option(
252
- '--deadline [minutes]',
192
+ '--deadline <minutes>',
253
193
  'minutes from now to close the vote',
254
194
  Number,
255
195
  1,
256
196
  )
257
- .action(async function () {
258
- const opts = this.opts();
259
-
197
+ .action(async function (opts) {
198
+ const { lookupPsmInstance } = await rpcTools();
260
199
  const psmInstance = lookupPsmInstance(opts.pair);
261
200
 
262
- /** @type {import('../lib/psm.js').OfferSpec} */
201
+ /** @type {import('@agoric/smart-wallet/src/offers.js').OfferSpec} */
263
202
  const offer = {
264
- id: Number(opts.offerId),
203
+ id: opts.offerId,
265
204
  invitationSpec: {
266
205
  source: 'continuing',
267
- previousOffer: opts.psmCharterAcceptOfferId,
206
+ previousOffer: opts.charterAcceptOfferId,
268
207
  invitationMakerName: 'VoteOnPauseOffers',
269
208
  // ( instance, strings list, timer deadline seconds )
270
209
  invitationArgs: harden([
@@ -276,10 +215,7 @@ export const makePsmCommand = async logger => {
276
215
  proposal: {},
277
216
  };
278
217
 
279
- outputAction({
280
- method: 'executeOffer',
281
- offer,
282
- });
218
+ outputExecuteOfferAction(offer);
283
219
 
284
220
  console.warn('Now execute the prepared offer');
285
221
  });
@@ -288,10 +224,10 @@ export const makePsmCommand = async logger => {
288
224
  .command('proposeChangeMintLimit')
289
225
  .description('propose to change the MintLimit parameter')
290
226
  .option(
291
- '--offerId [number]',
227
+ '--offerId <string>',
292
228
  'id of this offer (optional)',
293
- Number,
294
- Date.now(),
229
+ String,
230
+ `proposeChangeMintLimit-${Date.now()}`,
295
231
  )
296
232
  .requiredOption(
297
233
  '--pair [Minted.Anchor]',
@@ -300,20 +236,18 @@ export const makePsmCommand = async logger => {
300
236
  ['IST', 'AUSD'],
301
237
  )
302
238
  .requiredOption(
303
- '--previousOfferId [number]',
239
+ '--previousOfferId <string>',
304
240
  'offer using psm charter invitation',
305
- Number,
306
241
  )
307
- .requiredOption('--limit [number]', 'new mint limit (in IST)', Number)
242
+ .requiredOption('--limit <number>', 'new mint limit (in IST)', Number)
308
243
  .option(
309
- '--deadline [minutes]',
244
+ '--deadline <minutes>',
310
245
  'minutes from now to close the vote',
311
246
  Number,
312
247
  1,
313
248
  )
314
- .action(async function () {
315
- const opts = this.opts();
316
-
249
+ .action(async function (opts) {
250
+ const { agoricNames, lookupPsmInstance } = await rpcTools();
317
251
  const psmInstance = lookupPsmInstance(opts.pair);
318
252
 
319
253
  const istBrand = agoricNames.brand.IST;
@@ -321,9 +255,9 @@ export const makePsmCommand = async logger => {
321
255
  brand: istBrand,
322
256
  value: BigInt(opts.limit * 1_000_000),
323
257
  });
324
- /** @type {import('../lib/psm.js').OfferSpec} */
258
+ /** @type {import('@agoric/smart-wallet/src/offers.js').OfferSpec} */
325
259
  const offer = {
326
- id: Number(opts.offerId),
260
+ id: opts.offerId,
327
261
  invitationSpec: {
328
262
  source: 'continuing',
329
263
  previousOffer: opts.previousOfferId,
@@ -337,76 +271,7 @@ export const makePsmCommand = async logger => {
337
271
  },
338
272
  };
339
273
 
340
- outputAction({
341
- method: 'executeOffer',
342
- offer,
343
- });
344
-
345
- console.warn('Now execute the prepared offer');
346
- });
347
-
348
- psm
349
- .command('vote')
350
- .description('vote on a question (hard-coded for now))')
351
- .option('--offerId [number]', 'Offer id', Number, Date.now())
352
- .requiredOption(
353
- '--econCommAcceptOfferId [number]',
354
- 'offer that had continuing invitation result',
355
- Number,
356
- )
357
- .requiredOption(
358
- '--pair [Minted.Anchor]',
359
- 'token pair (Minted.Anchor)',
360
- s => s.split('.'),
361
- ['IST', 'AUSD'],
362
- )
363
- .requiredOption(
364
- '--forPosition [number]',
365
- 'index of one position to vote for (within the question description.positions); ',
366
- Number,
367
- )
368
- .action(async function () {
369
- const opts = this.opts();
370
-
371
- const questionHandleCapDataStr = await vstorage.readLatest(
372
- 'published.committees.Economic_Committee.latestQuestion',
373
- );
374
- const questionDescriptions = storageHelper.unserializeTxt(
375
- questionHandleCapDataStr,
376
- fromBoard,
377
- );
378
-
379
- assert(questionDescriptions, 'missing questionDescriptions');
380
- assert(
381
- questionDescriptions.length === 1,
382
- 'multiple questions not supported',
383
- );
384
-
385
- const questionDesc = questionDescriptions[0];
386
- // TODO support multiple position arguments
387
- const chosenPositions = [questionDesc.positions[opts.forPosition]];
388
- assert(chosenPositions, `undefined position index ${opts.forPosition}`);
389
-
390
- /** @type {import('../lib/psm.js').OfferSpec} */
391
- const offer = {
392
- id: Number(opts.offerId),
393
- invitationSpec: {
394
- source: 'continuing',
395
- previousOffer: opts.previousOfferId,
396
- invitationMakerName: 'makeVoteInvitation',
397
- // (positionList, questionHandle)
398
- invitationArgs: harden([
399
- chosenPositions,
400
- questionDesc.questionHandle,
401
- ]),
402
- },
403
- proposal: {},
404
- };
405
-
406
- outputAction({
407
- method: 'executeOffer',
408
- offer,
409
- });
274
+ outputExecuteOfferAction(offer);
410
275
 
411
276
  console.warn('Now execute the prepared offer');
412
277
  });
@@ -0,0 +1,98 @@
1
+ /* eslint-disable @jessie.js/no-nested-await */
2
+ // @ts-check
3
+ /* eslint-disable func-names */
4
+ /* global fetch, process */
5
+ import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
6
+ import { Command } from 'commander';
7
+ import { makeRpcUtils } from '../lib/rpc.js';
8
+ import { outputActionAndHint } from '../lib/wallet.js';
9
+
10
+ /**
11
+ * @param {import('anylogger').Logger} _logger
12
+ * @param {*} io
13
+ */
14
+ export const makeReserveCommand = (_logger, io = {}) => {
15
+ const { stdout = process.stdout, stderr = process.stderr, now } = io;
16
+ const reserve = new Command('reserve').description('Asset Reserve commands');
17
+
18
+ reserve
19
+ .command('add')
20
+ .description('add collateral to the reserve')
21
+ .requiredOption('--give <number>', 'Collateral to give', Number)
22
+ .option('--collateral-brand <string>', 'Collateral brand key', 'ATOM')
23
+ .option('--offer-id <string>', 'Offer id', String, `addCollateral-${now()}`)
24
+ .action(
25
+ /**
26
+ * @param {{
27
+ * give: number,
28
+ * collateralBrand: string,
29
+ * offerId: string,
30
+ * }} opts
31
+ */
32
+ async ({ collateralBrand, ...opts }) => {
33
+ const { agoricNames } = await makeRpcUtils({ fetch });
34
+
35
+ const offer = Offers.reserve.AddCollateral(agoricNames.brand, {
36
+ collateralBrandKey: collateralBrand,
37
+ ...opts,
38
+ });
39
+ outputActionAndHint(
40
+ { method: 'executeOffer', offer },
41
+ { stdout, stderr },
42
+ );
43
+ },
44
+ );
45
+
46
+ reserve
47
+ .command('proposeBurn')
48
+ .description('propose a call to burnFeesToReduceShortfall')
49
+ .option(
50
+ '--offerId <string>',
51
+ 'Offer id',
52
+ String,
53
+ `proposePauseOffers-${Date.now()}`,
54
+ )
55
+ .requiredOption(
56
+ '--charterAcceptOfferId <string>',
57
+ 'offer that had continuing invitation result',
58
+ )
59
+ .requiredOption('--value [integer]', 'value of µIST to burn', BigInt)
60
+ .option(
61
+ '--deadline <minutes>',
62
+ 'minutes from now to close the vote',
63
+ Number,
64
+ 1,
65
+ )
66
+ .action(async function (opts) {
67
+ const { agoricNames } = await makeRpcUtils({ fetch });
68
+
69
+ const reserveInstance = agoricNames.instance.reserve;
70
+ assert(reserveInstance, 'missing reserve in names');
71
+
72
+ const feesToBurn = { brand: agoricNames.brand.IST, value: opts.value };
73
+
74
+ /** @type {import('@agoric/smart-wallet/src/offers.js').OfferSpec} */
75
+ const offer = {
76
+ id: opts.offerId,
77
+ invitationSpec: {
78
+ source: 'continuing',
79
+ previousOffer: opts.charterAcceptOfferId,
80
+ invitationMakerName: 'VoteOnApiCall',
81
+ invitationArgs: harden([
82
+ reserveInstance,
83
+ 'burnFeesToReduceShortfall',
84
+ [feesToBurn],
85
+ BigInt(opts.deadline * 60 + Math.round(Date.now() / 1000)),
86
+ ]),
87
+ },
88
+ proposal: {},
89
+ };
90
+
91
+ outputActionAndHint(
92
+ { method: 'executeOffer', offer },
93
+ { stdout, stderr },
94
+ );
95
+ });
96
+
97
+ return reserve;
98
+ };
@@ -0,0 +1,109 @@
1
+ // @ts-check
2
+ /* global process */
3
+ import { Fail } from '@agoric/assert';
4
+ import { CommanderError } from 'commander';
5
+ import { normalizeAddressWithOptions } from '../lib/chain.js';
6
+ import { bigintReplacer } from '../lib/format.js';
7
+ import { getNetworkConfig } from '../lib/rpc.js';
8
+ import { makeWalletUtils, sendAction } from '../lib/wallet.js';
9
+
10
+ /**
11
+ * Make commands for testing.
12
+ *
13
+ * @param {{
14
+ * env: Partial<Record<string, string>>,
15
+ * stdout: Pick<import('stream').Writable,'write'>,
16
+ * stderr: Pick<import('stream').Writable,'write'>,
17
+ * now: () => number,
18
+ * createCommand: // Note: includes access to process.stdout, .stderr, .exit
19
+ * typeof import('commander').createCommand,
20
+ * execFileSync: typeof import('child_process').execFileSync,
21
+ * setTimeout: typeof setTimeout,
22
+ * }} process
23
+ * @param {{ fetch: typeof window.fetch }} net
24
+ */
25
+ export const makeTestCommand = (
26
+ { env, stdout, now, setTimeout, execFileSync, createCommand },
27
+ { fetch },
28
+ ) => {
29
+ /** @param {number} ms */
30
+ const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
31
+ const show = (info, indent = false) =>
32
+ stdout.write(
33
+ `${JSON.stringify(info, bigintReplacer, indent ? 2 : undefined)}\n`,
34
+ );
35
+
36
+ const tryMakeUtils = async () => {
37
+ await null;
38
+ try {
39
+ // XXX pass fetch to getNetworkConfig() explicitly
40
+ // await null above makes this await safe
41
+ // eslint-disable-next-line @jessie.js/no-nested-await
42
+ const networkConfig = await getNetworkConfig(env);
43
+ return makeWalletUtils({ fetch, execFileSync, delay }, networkConfig);
44
+ } catch (err) {
45
+ throw new CommanderError(1, 'RPC_FAIL', err.message);
46
+ }
47
+ };
48
+
49
+ const testCmd = createCommand('test').description('test operations');
50
+
51
+ testCmd
52
+ .command('upgrade-contract')
53
+ .description('make simple offer to upgrade-contract')
54
+ .option('--home <dir>', 'agd CosmosSDK application home directory')
55
+ .option(
56
+ '--keyring-backend <os|file|test>',
57
+ `keyring's backend (os|file|test) (default "${
58
+ env.AGORIC_KEYRING_BACKEND || 'os'
59
+ }")`,
60
+ env.AGORIC_KEYRING_BACKEND,
61
+ )
62
+ .option('--offer-id <string>', 'Offer id', String, `my-${now()}`)
63
+ .requiredOption(
64
+ '--from <address>',
65
+ 'wallet address literal or name',
66
+ literalOrName =>
67
+ normalizeAddressWithOptions(literalOrName, testCmd.opts(), {
68
+ execFileSync,
69
+ }),
70
+ )
71
+ .action(async opts => {
72
+ const { agoricNames, networkConfig, pollOffer } = await tryMakeUtils();
73
+ const { from } = opts;
74
+ const { home, keyringBackend: backend } = testCmd.opts();
75
+
76
+ const io = { ...networkConfig, execFileSync, delay, stdout };
77
+ /** @type {import('@agoric/smart-wallet/src/offers.js').OfferSpec} */
78
+ const offer = {
79
+ id: opts.offerId,
80
+ invitationSpec: {
81
+ source: 'contract',
82
+ instance: agoricNames.instance.myInstance || Fail`no myInstance`,
83
+ publicInvitationMaker: 'makeInvitation',
84
+ },
85
+ proposal: {
86
+ want: { Tokens: { brand: agoricNames.brand.GoodStuff, value: 32n } },
87
+ },
88
+ };
89
+ const result = await sendAction(
90
+ {
91
+ method: 'executeOffer',
92
+ offer,
93
+ },
94
+ { keyring: { home, backend }, from, verbose: false, ...io },
95
+ );
96
+ if (result?.code !== 0) {
97
+ throw result;
98
+ }
99
+ show({ height: result?.height, txhash: result?.txhash });
100
+ const found = await pollOffer(from, offer.id, result.height, true);
101
+ show(found, true);
102
+ const numWantsSatisfied = found.numWantsSatisfied || 0;
103
+ if (numWantsSatisfied < 1) {
104
+ process.exit(2);
105
+ }
106
+ });
107
+
108
+ return testCmd;
109
+ };