agoric 0.21.2-other-dev-8f8782b.0 → 0.21.2-other-dev-fbe72e7.0.fbe72e7

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.
Files changed (44) hide show
  1. package/README.md +0 -73
  2. package/package.json +60 -43
  3. package/src/anylogger-agoric.js +31 -16
  4. package/src/bin-agops.js +8 -12
  5. package/src/chain-config.js +42 -16
  6. package/src/commands/auction.js +22 -19
  7. package/src/commands/gov.js +475 -0
  8. package/src/commands/inter.js +44 -62
  9. package/src/commands/oracle.js +144 -87
  10. package/src/commands/perf.js +20 -16
  11. package/src/commands/psm.js +29 -26
  12. package/src/commands/reserve.js +13 -6
  13. package/src/commands/test-upgrade.js +15 -8
  14. package/src/commands/vaults.js +31 -18
  15. package/src/commands/wallet.js +121 -48
  16. package/src/cosmos.js +3 -3
  17. package/src/deploy.js +10 -5
  18. package/src/entrypoint.js +2 -5
  19. package/src/follow.js +14 -10
  20. package/src/helpers.js +10 -5
  21. package/src/init.js +2 -9
  22. package/src/install.js +18 -25
  23. package/src/lib/bundles.js +102 -0
  24. package/src/lib/chain.js +71 -35
  25. package/src/lib/format.js +28 -34
  26. package/src/lib/index.js +7 -0
  27. package/src/lib/packageManager.js +24 -0
  28. package/src/lib/wallet.js +44 -144
  29. package/src/main-publish.js +2 -3
  30. package/src/main.js +95 -125
  31. package/src/open.js +8 -10
  32. package/src/publish.js +4 -9
  33. package/src/scripts.js +14 -32
  34. package/src/sdk-package-names.js +22 -9
  35. package/src/set-defaults.js +2 -1
  36. package/src/start.js +59 -68
  37. package/tools/getting-started.js +272 -0
  38. package/tools/resm-plugin/deploy.js +18 -0
  39. package/tools/resm-plugin/package.json +12 -0
  40. package/tools/resm-plugin/src/output.js +1 -0
  41. package/tools/resm-plugin/src/plugin.js +17 -0
  42. package/CHANGELOG.md +0 -1069
  43. package/src/commands/ec.js +0 -314
  44. package/src/lib/rpc.js +0 -272
package/src/entrypoint.js CHANGED
@@ -1,11 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ /* eslint-env node */
2
3
  // @jessie-check
3
4
 
4
- /* global process */
5
-
6
- import '@endo/init/pre.js';
7
- import 'esm';
8
- import '@agoric/casting/node-fetch-shim.js';
9
5
  import '@endo/init/legacy.js';
10
6
 
11
7
  import path from 'path';
@@ -25,6 +21,7 @@ const log = anylogger('agoric');
25
21
  const progname = path.basename(process.argv[1]);
26
22
 
27
23
  const stdout = str => process.stdout.write(str);
24
+ /** @type {(...args: ConstructorParameters<typeof WebSocket>) => WebSocket} */
28
25
  const makeWebSocket = (...args) => new WebSocket(...args);
29
26
 
30
27
  const rawArgs = process.argv.slice(2);
package/src/follow.js CHANGED
@@ -126,25 +126,29 @@ export default async function followerMain(progname, rawArgs, powers, opts) {
126
126
  });
127
127
  }
128
128
 
129
- const leaderOptions = makeLeaderOptions({
130
- sleep,
131
- jitter,
132
- log: verbose ? console.warn : () => undefined,
133
- });
134
-
135
129
  const [_cmd, ...specs] = rawArgs;
136
130
 
137
131
  verbose && console.warn('Creating leader for', bootstrap);
138
- const leader = makeLeader(bootstrap, leaderOptions);
132
+ const leader = makeLeader(
133
+ bootstrap,
134
+ makeLeaderOptions({
135
+ sleep,
136
+ jitter,
137
+ log: verbose ? console.warn : () => undefined,
138
+ }),
139
+ );
139
140
  const iterate = opts.lossy ? iterateLatest : iterateEach;
140
141
  await Promise.all(
141
142
  specs.map(async spec => {
142
143
  verbose && console.warn('Following', spec);
143
144
  const castingSpec = makeCastingSpec(spec);
144
145
  const follower = makeFollower(castingSpec, leader, followerOptions);
145
- for await (const { value, blockHeight, currentBlockHeight } of iterate(
146
- follower,
147
- )) {
146
+ for await (const obj of iterate(follower)) {
147
+ if ('error' in obj) {
148
+ console.error('Error following:', obj.error);
149
+ continue;
150
+ }
151
+ const { value, blockHeight, currentBlockHeight } = obj;
148
152
  const blockHeightPrefix = opts.blockHeight ? `${blockHeight}:` : '';
149
153
  const currentBlockHeightPrefix = opts.currentBlockHeight
150
154
  ? `${currentBlockHeight}:`
package/src/helpers.js CHANGED
@@ -1,7 +1,10 @@
1
- /* global process */
1
+ /* eslint-env node */
2
2
  // @ts-check
3
3
 
4
- /** @typedef {import('child_process').ChildProcess} ChildProcess */
4
+ /** @import { ChildProcess } from 'child_process' */
5
+
6
+ // Backwards compatibility
7
+ export { fetchEnvNetworkConfig as getNetworkConfig } from '@agoric/client-utils';
5
8
 
6
9
  export const getSDKBinaries = ({
7
10
  jsPfx = '../..',
@@ -40,10 +43,12 @@ export const makePspawn = ({
40
43
  *
41
44
  * @param {string} cmd command name to run
42
45
  * @param {Array<string>} cargs arguments to the command
43
- * @param {object} param2
44
- * @param {string | [string, string, string]} [param2.stdio] standard IO
46
+ * @param {object} [opts]
47
+ * @param {string} [opts.cwd]
48
+ * @param {string | [string, string, string]} [opts.stdio] standard IO
45
49
  * specification
46
- * @param {Record<string, string | undefined>} [param2.env] environment
50
+ * @param {Record<string, string | undefined>} [opts.env] environment
51
+ * @param {boolean} [opts.detached] whether the child process should be detached
47
52
  * @returns {Promise<number> & { childProcess: ChildProcess }}} promise for
48
53
  * exit status. The return result has a `childProcess` property to obtain
49
54
  * control over the running process
package/src/init.js CHANGED
@@ -1,12 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import { makePspawn } from './helpers.js';
3
3
 
4
- // Ambient types. Needed only for dev but this does a runtime import.
5
- // https://github.com/Agoric/agoric-sdk/issues/6512
6
- import '@endo/captp/src/types.js';
7
- import '@agoric/swingset-vat/exported.js';
8
- import '@agoric/swingset-vat/src/vats/network/types.js';
9
-
10
4
  // Use either an absolute template URL, or find it relative to DAPP_URL_BASE.
11
5
  const gitURL = (relativeOrAbsoluteURL, base) => {
12
6
  const url = new URL(relativeOrAbsoluteURL, base);
@@ -40,9 +34,10 @@ export default async function initMain(_progname, rawArgs, priv, opts) {
40
34
  dappBranch = ['-b', opts.dappBranch];
41
35
  }
42
36
 
37
+ const shallow = ['--depth', '1', '--shallow-submodules'];
43
38
  const exitStatus = await pspawn(
44
39
  'git',
45
- ['clone', '--origin=upstream', dappURL, DIR, ...dappBranch],
40
+ ['clone', '--origin=upstream', ...shallow, dappURL, DIR, ...dappBranch],
46
41
  {
47
42
  stdio: 'inherit',
48
43
  },
@@ -60,7 +55,6 @@ export default async function initMain(_progname, rawArgs, priv, opts) {
60
55
  const path = `${DIR}/${dir}package.json`;
61
56
  log('rewriting ', path);
62
57
 
63
- // eslint-disable-next-line no-await-in-loop
64
58
  const contents = await fs.readFile(path, 'utf-8');
65
59
  const pkg = JSON.parse(contents.replace(/@DIR@/g, DIR));
66
60
  if (dir === '') {
@@ -74,7 +68,6 @@ export default async function initMain(_progname, rawArgs, priv, opts) {
74
68
  pkg.name = `${DIR}${pkg.name.substr(topLevelName.length)}`;
75
69
  const json = JSON.stringify(pkg, undefined, 2);
76
70
 
77
- // eslint-disable-next-line no-await-in-loop
78
71
  await fs.writeFile(path, json);
79
72
  }
80
73
 
package/src/install.js CHANGED
@@ -1,16 +1,17 @@
1
- /* global process AggregateError Buffer */
1
+ /* eslint-env node */
2
2
  import path from 'path';
3
3
  import chalk from 'chalk';
4
+ import { execFileSync } from 'child_process';
4
5
  import { makePspawn } from './helpers.js';
5
6
  import DEFAULT_SDK_PACKAGE_NAMES from './sdk-package-names.js';
7
+ import { listWorkspaces } from './lib/packageManager.js';
6
8
 
7
9
  const REQUIRED_AGORIC_START_PACKAGES = [
8
10
  '@agoric/solo',
9
11
  '@agoric/cosmic-swingset',
10
12
  ];
11
13
 
12
- const filename = new URL(import.meta.url).pathname;
13
- const dirname = path.dirname(filename);
14
+ const dirname = path.dirname(new URL(import.meta.url).pathname);
14
15
 
15
16
  export default async function installMain(progname, rawArgs, powers, opts) {
16
17
  const { anylogger, fs, spawn } = powers;
@@ -31,26 +32,16 @@ export default async function installMain(progname, rawArgs, powers, opts) {
31
32
  const rimraf = file => pspawn('rm', ['-rf', file]);
32
33
 
33
34
  async function getWorktreePackagePaths(cwd = '.', map = new Map()) {
34
- // run `yarn workspaces info` to get the list of directories to
35
- // use, instead of a hard-coded list
36
- const p = pspawn('yarn', ['workspaces', '--silent', 'info'], {
37
- cwd,
38
- stdio: ['inherit', 'pipe', 'inherit'],
39
- });
40
- const stdout = [];
41
- p.childProcess.stdout.on('data', out => stdout.push(out));
42
- await p;
43
- const d = JSON.parse(Buffer.concat(stdout).toString('utf-8'));
44
- Object.entries(d).forEach(([name, { location }]) =>
45
- map.set(name, path.resolve(cwd, location)),
46
- );
35
+ for (const { name, location } of listWorkspaces({ execFileSync }, cwd)) {
36
+ map.set(name, path.resolve(cwd, location));
37
+ }
47
38
  return map;
48
39
  }
49
40
 
50
41
  let subdirs;
51
42
  const workTrees = ['.'];
52
43
  let sdkWorktree;
53
- /** @type {Map<string, string>} */
44
+ /** @type {Map<string, string | null>} */
54
45
  const sdkPackageToPath = new Map();
55
46
  const linkFolder = path.resolve(`_agstate/yarn-links`);
56
47
  const linkFlags = [];
@@ -61,7 +52,6 @@ export default async function installMain(progname, rawArgs, powers, opts) {
61
52
  const yarnInstallEachWorktree = async (phase, ...flags) => {
62
53
  for await (const workTree of workTrees) {
63
54
  log.info(`yarn install ${phase} in ${workTree}`);
64
- // eslint-disable-next-line no-await-in-loop
65
55
  const yarnInstall = await pspawn(
66
56
  'yarn',
67
57
  [...linkFlags, 'install', ...flags],
@@ -133,7 +123,6 @@ export default async function installMain(progname, rawArgs, powers, opts) {
133
123
  // Ensure we update the package.json before exiting.
134
124
  const updatePackageJson = async () => {
135
125
  // Don't update on exit anymore.
136
- // eslint-disable-next-line no-use-before-define
137
126
  process.off('beforeExit', updatePackageJsonOnExit);
138
127
  log.info(`updating ${pjson}`);
139
128
  await fs.writeFile(
@@ -172,12 +161,9 @@ export default async function installMain(progname, rawArgs, powers, opts) {
172
161
  .then(results => {
173
162
  // After all have settled, throw any errors.
174
163
  const failures = results.filter(
175
- ({ status }) => status !== 'fulfilled',
164
+ result => result.status !== 'fulfilled',
176
165
  );
177
166
  if (failures.length) {
178
- if (typeof AggregateError !== 'function') {
179
- throw failures[0].reason;
180
- }
181
167
  throw AggregateError(
182
168
  failures.map(({ reason }) => reason),
183
169
  'Failed to prune',
@@ -269,7 +255,9 @@ export default async function installMain(progname, rawArgs, powers, opts) {
269
255
  };
270
256
  await Promise.all(subdirs.map(removeNodeModulesSymlinks));
271
257
  } else {
272
- DEFAULT_SDK_PACKAGE_NAMES.forEach(name => sdkPackageToPath.set(name, null));
258
+ for (const name of DEFAULT_SDK_PACKAGE_NAMES) {
259
+ sdkPackageToPath.set(name, null);
260
+ }
273
261
  }
274
262
 
275
263
  if (forceSdkVersion !== undefined) {
@@ -287,7 +275,12 @@ export default async function installMain(progname, rawArgs, powers, opts) {
287
275
  // Create symlinks to the SDK packages.
288
276
  await Promise.all(
289
277
  [...sdkPackageToPath.entries()].map(async ([pjName, dir]) => {
278
+ if (typeof dir !== 'string') {
279
+ throw Error(`unexpected incomplete package mapping: ${pjName}`);
280
+ }
281
+
290
282
  const SUBOPTIMAL = false;
283
+ await null;
291
284
  if (SUBOPTIMAL) {
292
285
  // This use of yarn is noisy and slow.
293
286
  await pspawn('yarn', [...linkFlags, 'unlink', pjName]);
@@ -303,7 +296,7 @@ export default async function installMain(progname, rawArgs, powers, opts) {
303
296
  log('linking', linkName);
304
297
  return fs
305
298
  .mkdir(linkDir, { recursive: true })
306
- .then(_ => fs.symlink(path.relative(linkDir, dir), linkName));
299
+ .then(() => fs.symlink(path.relative(linkDir, dir), linkName));
307
300
  }),
308
301
  );
309
302
 
@@ -0,0 +1,102 @@
1
+ // @ts-check
2
+ /* eslint-env node */
3
+
4
+ import assert from 'node:assert/strict';
5
+ import fs from 'node:fs';
6
+ import { join } from 'node:path';
7
+
8
+ import { ZipReader } from '@endo/zip';
9
+
10
+ /** @import {Bundle} from '@agoric/swingset-vat'; */
11
+ /** @import {CoreEvalPlan} from '@agoric/deploy-script-support/src/writeCoreEvalParts.js' */
12
+
13
+ // exported for testing
14
+ export const PACKAGE_NAME_RE = /^(?:@[^/]+\/)?[^/]+/;
15
+
16
+ /**
17
+ * @typedef {{ name: string, label: string, location: string, modules: Record<string, {compartment: string, module: string}>}} Compartment
18
+ */
19
+
20
+ /**
21
+ * @typedef CompartmentMap
22
+ * @property {string[]} tags
23
+ * @property {{compartment: string, module: string}} entry
24
+ * @property {Record<string, Compartment>} compartments
25
+ */
26
+
27
+ /** @param {Bundle} bundleObj*/
28
+ export const extractBundleInfo = async bundleObj => {
29
+ if (bundleObj.moduleFormat !== 'endoZipBase64') {
30
+ throw Error('only endoZipBase64 is supported');
31
+ }
32
+
33
+ const contents = Buffer.from(bundleObj.endoZipBase64, 'base64');
34
+
35
+ const zipReader = new ZipReader(contents);
36
+ const { files } = zipReader;
37
+
38
+ const cmapEntry = files.get('compartment-map.json');
39
+ /** @type {CompartmentMap} */
40
+ const compartmentMap = JSON.parse(Buffer.from(cmapEntry.content).toString());
41
+
42
+ // XXX mapIter better but requires SES
43
+ const fileSizes = Object.fromEntries(
44
+ Array.from(files.values()).map(f => [
45
+ f.name,
46
+ // bundle contents are not compressed
47
+ f.content.length,
48
+ ]),
49
+ );
50
+
51
+ return { compartmentMap, fileSizes };
52
+ };
53
+
54
+ // UNTIL https://github.com/endojs/endo/issues/1656
55
+ /** @param {string} bundleFilename */
56
+ export const statBundle = async bundleFilename => {
57
+ const bundle = fs.readFileSync(bundleFilename, 'utf8');
58
+ /** @type {Bundle} */
59
+ const bundleObj = JSON.parse(bundle);
60
+ console.log('\nBUNDLE', bundleObj.moduleFormat, bundleFilename);
61
+
62
+ const info = await extractBundleInfo(bundleObj);
63
+ assert(info, 'no bundle info');
64
+
65
+ /** @type {Record<string, number>} */
66
+ const byPackage = {};
67
+ let totalSize = 0;
68
+ for (const [filename, size] of Object.entries(info.fileSizes)) {
69
+ totalSize += size;
70
+ if (filename === 'compartment-map.json') {
71
+ continue;
72
+ }
73
+ const packageName = filename.match(PACKAGE_NAME_RE)?.[0];
74
+ assert(packageName, `invalid filename ${filename}`);
75
+ byPackage[packageName] ||= 0;
76
+ byPackage[packageName] += size;
77
+ }
78
+
79
+ console.log('Sum of file sizes in each package:');
80
+ console.table(byPackage);
81
+
82
+ console.log('total size:', totalSize);
83
+ console.log('\nTo explore the contents:\n');
84
+ console.log(
85
+ ` DIR=$(mktemp -d); cat ${bundleFilename} | jq -r .endoZipBase64 | base64 -d | tar xC $DIR; open $DIR`,
86
+ );
87
+ };
88
+
89
+ /** @param {string} path */
90
+ export const statPlans = async path => {
91
+ const files = await fs.promises.readdir(path);
92
+ const planfiles = files.filter(f => f.endsWith('plan.json'));
93
+
94
+ for (const planfile of planfiles) {
95
+ /** @type {CoreEvalPlan} */
96
+ const plan = JSON.parse(fs.readFileSync(join(path, planfile), 'utf8'));
97
+ console.log('\n**\nPLAN', plan.name);
98
+ for (const bundle of plan.bundles) {
99
+ await statBundle(bundle.fileName);
100
+ }
101
+ }
102
+ };
package/src/lib/chain.js CHANGED
@@ -1,7 +1,13 @@
1
1
  // @ts-check
2
- /* global process */
2
+ /* eslint-env node */
3
3
  import { normalizeBech32 } from '@cosmjs/encoding';
4
4
  import { execFileSync as execFileSyncAmbient } from 'child_process';
5
+ import { makeAgoricQueryClient } from '@agoric/client-utils';
6
+
7
+ /**
8
+ * @import {MinimalNetworkConfig} from '@agoric/client-utils';
9
+ * @import {Params, ParamsSDKType} from '@agoric/cosmic-proto/agoric/swingset/swingset.js';
10
+ */
5
11
 
6
12
  const agdBinary = 'agd';
7
13
 
@@ -34,10 +40,42 @@ export const normalizeAddressWithOptions = (
34
40
  };
35
41
  harden(normalizeAddressWithOptions);
36
42
 
43
+ /** @typedef {number | 'auto' | ['auto', adjustment?: number | undefined]} GasLimit */
44
+
45
+ /**
46
+ * @param {GasLimit} limit
47
+ * @returns {string[]}
48
+ */
49
+ const makeGasOpts = limit => {
50
+ if (Number.isFinite(limit) || limit === 'auto') {
51
+ return [`--gas=${limit}`];
52
+ }
53
+ if (Array.isArray(limit) && limit.length >= 1 && limit[0] === 'auto') {
54
+ const gasOpts = ['--gas=auto'];
55
+ if (limit.length > 1) {
56
+ const [adjustment, ...rest] = limit.slice(1);
57
+ const adjustmentIsValid =
58
+ adjustment === undefined ||
59
+ (Number.isFinite(adjustment) && Number(adjustment) > 0);
60
+ if (rest.length !== 0 || !adjustmentIsValid) {
61
+ throw Error('invalid gas input');
62
+ }
63
+ if (adjustment !== undefined) {
64
+ gasOpts.push(`--gas-adjustment=${adjustment}`);
65
+ }
66
+ }
67
+ return gasOpts;
68
+ }
69
+
70
+ throw Error('invalid gas input');
71
+ };
72
+
37
73
  /**
38
74
  * @param {ReadonlyArray<string>} swingsetArgs
39
- * @param {import('./rpc').MinimalNetworkConfig & {
75
+ * @param {MinimalNetworkConfig & {
40
76
  * from: string,
77
+ * fees?: string,
78
+ * gas?: GasLimit,
41
79
  * dryRun?: boolean,
42
80
  * verbose?: boolean,
43
81
  * keyring?: {home?: string, backend: string}
@@ -48,6 +86,8 @@ harden(normalizeAddressWithOptions);
48
86
  export const execSwingsetTransaction = (swingsetArgs, opts) => {
49
87
  const {
50
88
  from,
89
+ fees,
90
+ gas = ['auto', 1.2],
51
91
  dryRun = false,
52
92
  verbose = true,
53
93
  keyring = undefined,
@@ -60,9 +100,12 @@ export const execSwingsetTransaction = (swingsetArgs, opts) => {
60
100
  const backendOpt = keyring?.backend
61
101
  ? [`--keyring-backend=${keyring.backend}`]
62
102
  : [];
103
+ const feeOpt = fees ? ['--fees', fees] : [];
63
104
  const cmd = [`--node=${rpcAddrs[0]}`, `--chain-id=${chainName}`].concat(
64
105
  homeOpt,
65
106
  backendOpt,
107
+ feeOpt,
108
+ makeGasOpts(gas),
66
109
  [`--from=${from}`, 'tx', 'swingset'],
67
110
  swingsetArgs,
68
111
  );
@@ -74,31 +117,34 @@ export const execSwingsetTransaction = (swingsetArgs, opts) => {
74
117
  stdout.write('\n');
75
118
  } else {
76
119
  const yesCmd = cmd.concat(['--yes']);
77
- if (verbose) console.log('Executing ', yesCmd);
78
- return execFileSync(agdBinary, yesCmd, { encoding: 'utf-8' });
120
+ if (verbose) console.log('Executing ', agdBinary, yesCmd);
121
+ const out = execFileSync(agdBinary, yesCmd, { encoding: 'utf-8' });
122
+
123
+ // agd puts this diagnostic on stdout rather than stderr :-/
124
+ // "Default sign-mode 'direct' not supported by Ledger, using sign-mode 'amino-json'.
125
+ if (out.startsWith('Default sign-mode')) {
126
+ const stripDiagnostic = out.replace(/^Default[^\n]+\n/, '');
127
+ return stripDiagnostic;
128
+ }
129
+ return out;
79
130
  }
80
131
  };
81
132
  harden(execSwingsetTransaction);
82
133
 
83
- // xxx rpc should be able to query this by HTTP without shelling out
84
- export const fetchSwingsetParams = net => {
85
- const { chainName, rpcAddrs, execFileSync = execFileSyncAmbient } = net;
86
- const cmd = [
87
- `--node=${rpcAddrs[0]}`,
88
- `--chain-id=${chainName}`,
89
- 'query',
90
- 'swingset',
91
- 'params',
92
- '--output',
93
- '--json',
94
- ];
95
- const buffer = execFileSync(agdBinary, cmd);
96
- return JSON.parse(buffer.toString());
134
+ /**
135
+ *
136
+ * @param {MinimalNetworkConfig} net
137
+ * @returns {Promise<Params>}
138
+ */
139
+ export const fetchSwingsetParams = async net => {
140
+ const client = await makeAgoricQueryClient(net);
141
+ const { params } = await client.agoric.swingset.params();
142
+ return params;
97
143
  };
98
144
  harden(fetchSwingsetParams);
99
145
 
100
146
  /**
101
- * @param {import('./rpc').MinimalNetworkConfig & {
147
+ * @param {MinimalNetworkConfig & {
102
148
  * execFileSync: typeof import('child_process').execFileSync,
103
149
  * delay: (ms: number) => Promise<void>,
104
150
  * period?: number,
@@ -117,12 +163,10 @@ export const pollBlocks = opts => async lookup => {
117
163
  for (;;) {
118
164
  const sTxt = execFileSync(agdBinary, ['status', ...nodeArgs]);
119
165
  const status = JSON.parse(sTxt.toString());
120
- const {
121
- SyncInfo: { latest_block_time: time, latest_block_height: height },
122
- } = status;
166
+ const { latest_block_time: time, latest_block_height: height } =
167
+ status.sync_info || status.SyncInfo;
123
168
  try {
124
169
  // see await null above
125
- // eslint-disable-next-line @jessie.js/no-nested-await, no-await-in-loop
126
170
  const result = await lookup({ time, height });
127
171
  return result;
128
172
  } catch (_err) {
@@ -132,7 +176,6 @@ export const pollBlocks = opts => async lookup => {
132
176
  height,
133
177
  'retrying...',
134
178
  );
135
- // eslint-disable-next-line @jessie.js/no-nested-await, no-await-in-loop
136
179
  await delay(period);
137
180
  }
138
181
  }
@@ -140,14 +183,14 @@ export const pollBlocks = opts => async lookup => {
140
183
 
141
184
  /**
142
185
  * @param {string} txhash
143
- * @param {import('./rpc').MinimalNetworkConfig & {
186
+ * @param {MinimalNetworkConfig & {
144
187
  * execFileSync: typeof import('child_process').execFileSync,
145
188
  * delay: (ms: number) => Promise<void>,
146
189
  * period?: number,
147
190
  * }} opts
148
191
  */
149
192
  export const pollTx = async (txhash, opts) => {
150
- const { execFileSync, rpcAddrs, chainName } = opts;
193
+ const { execFileSync, rpcAddrs } = opts;
151
194
 
152
195
  const nodeArgs = [`--node=${rpcAddrs[0]}`];
153
196
  const outJson = ['--output', 'json'];
@@ -155,15 +198,8 @@ export const pollTx = async (txhash, opts) => {
155
198
  const lookup = async () => {
156
199
  const out = execFileSync(
157
200
  agdBinary,
158
- [
159
- 'query',
160
- 'tx',
161
- txhash,
162
- `--chain-id=${chainName}`,
163
- ...nodeArgs,
164
- ...outJson,
165
- ],
166
- { stdio: ['ignore', 'pipe', 'ignore'] },
201
+ ['query', 'tx', txhash, ...nodeArgs, ...outJson],
202
+ { stdio: ['ignore', 'pipe', 'pipe'] },
167
203
  );
168
204
  // XXX this type is defined in a .proto file somewhere
169
205
  /** @type {{ height: string, txhash: string, code: number, timestamp: string }} */
package/src/lib/format.js CHANGED
@@ -1,25 +1,21 @@
1
- // @ts-check
1
+ import { Fail, q } from '@endo/errors';
2
2
  import { makeBoardRemote } from '@agoric/vats/tools/board-utils.js';
3
- // eslint-disable-next-line no-unused-vars -- typeof below
4
- import { makeAgoricNames } from './rpc.js';
5
3
 
6
- // ambient types
7
- import '@agoric/ertp/src/types-ambient.js';
8
-
9
- /** @typedef {import('@agoric/vats/tools/board-utils.js').BoardRemote} BoardRemote */
4
+ /**
5
+ * @import {Amount, Brand} from '@agoric/ertp'
6
+ * @import {AgoricNamesRemotes, BoardRemote, VBankAssetDetail} from '@agoric/vats/tools/board-utils.js';
7
+ */
10
8
 
9
+ // TODO Move to packages/internal.
11
10
  /**
12
- * Like @endo/nat but coerces
11
+ * Parses the input and returns either a finite number or NaN.
13
12
  *
14
- * @param {string} str
15
- * @returns {bigint}
13
+ * @param {string} input
14
+ * @returns {number}
16
15
  */
17
- export const Natural = str => {
18
- const b = BigInt(str);
19
- if (b < 0) {
20
- throw RangeError(`${b} is negative`);
21
- }
22
- return b;
16
+ export const parseFiniteNumber = input => {
17
+ const result = /[0-9]/.test(input || '') ? Number(input) : NaN;
18
+ return Number.isFinite(result) ? result : NaN;
23
19
  };
24
20
 
25
21
  /**
@@ -30,21 +26,18 @@ export const Natural = str => {
30
26
  */
31
27
  export const bigintReplacer = (k, v) => (typeof v === 'bigint' ? `${v}` : v);
32
28
 
33
- /** @type {import('@agoric/vats/tools/board-utils.js').VBankAssetDetail} */
29
+ /** @type {Partial<VBankAssetDetail>} */
34
30
  // eslint-disable-next-line no-unused-vars
35
31
  const exampleAsset = {
36
- // @ts-expect-error cast
37
32
  brand: makeBoardRemote({ boardId: 'board0425', iface: 'Alleged: BLD brand' }),
38
33
  displayInfo: { assetKind: 'nat', decimalPlaces: 6 },
39
- // @ts-expect-error cast
40
34
  issuer: makeBoardRemote({ boardId: null, iface: undefined }),
41
- petname: 'Agoric staking token',
35
+ proposedName: 'Agoric staking token',
42
36
  };
43
- /** @typedef {import('@agoric/vats/tools/board-utils.js').VBankAssetDetail } AssetDescriptor */
44
37
 
45
38
  /**
46
- * @param {AssetDescriptor[]} assets
47
- * @returns {(a: Amount & { brand: BoardRemote }) => [string, number | any[]]}
39
+ * @param {VBankAssetDetail[]} assets
40
+ * @returns {(a: Amount & { brand: BoardRemote }) => [string | null, number | any[]]}
48
41
  */
49
42
  export const makeAmountFormatter = assets => amt => {
50
43
  const { brand, value } = amt;
@@ -60,7 +53,9 @@ export const makeAmountFormatter = assets => amt => {
60
53
  return [issuerName, Number(value) / 10 ** decimalPlaces];
61
54
  case 'set':
62
55
  assert(Array.isArray(value));
56
+ // @ts-expect-error narrowed
63
57
  if (value[0]?.handle?.iface?.includes('InvitationHandle')) {
58
+ // @ts-expect-error narrowed
64
59
  return [issuerName, value.map(v => v.description)];
65
60
  }
66
61
  return [issuerName, value];
@@ -75,8 +70,6 @@ export const asPercent = ratio => {
75
70
  return (100 * Number(numerator.value)) / Number(denominator.value);
76
71
  };
77
72
 
78
- const { Fail, quote: q } = assert;
79
-
80
73
  const isObject = x => typeof x === 'object' && x !== null;
81
74
 
82
75
  /**
@@ -94,8 +87,8 @@ export const asBoardRemote = x => {
94
87
  /**
95
88
  * Summarize the balances array as user-facing informative tuples
96
89
  *
97
- * @param {import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord['purses']} purses
98
- * @param {AssetDescriptor[]} assets
90
+ * @param {import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord['purses']} purses
91
+ * @param {VBankAssetDetail[]} assets
99
92
  */
100
93
  export const purseBalanceTuples = (purses, assets) => {
101
94
  const fmt = makeAmountFormatter(assets);
@@ -107,14 +100,15 @@ export const purseBalanceTuples = (purses, assets) => {
107
100
  */
108
101
  export const fmtRecordOfLines = record => {
109
102
  const { stringify } = JSON;
103
+ /** @type {Array<[string, string[]]>} */
110
104
  const groups = Object.entries(record).map(([key, items]) => [
111
105
  key,
112
106
  items.map(item => ` ${stringify(item)}`),
113
107
  ]);
114
- const lineEntries = groups.map(
115
- // @ts-expect-error ???
116
- ([key, lines]) => ` ${stringify(key)}: [\n${lines.join(',\n')}\n ]`,
117
- );
108
+ const lineEntries = groups.map(([key, lines]) => {
109
+ const linesStr = lines.length === 0 ? `[]` : `[\n${lines.join(',\n')}\n ]`;
110
+ return ` ${stringify(key)}: ${linesStr}`;
111
+ });
118
112
  return `{\n${lineEntries.join(',\n')}\n}`;
119
113
  };
120
114
 
@@ -122,7 +116,7 @@ export const fmtRecordOfLines = record => {
122
116
  * Summarize the offerStatuses of the state as user-facing informative tuples
123
117
  *
124
118
  * @param {import('@agoric/smart-wallet/src/utils.js').CoalescedWalletState} state
125
- * @param {Awaited<ReturnType<typeof makeAgoricNames>>} agoricNames
119
+ * @param {AgoricNamesRemotes} agoricNames
126
120
  */
127
121
  export const offerStatusTuples = (state, agoricNames) => {
128
122
  const { offerStatuses } = state;
@@ -177,9 +171,9 @@ export const offerStatusTuples = (state, agoricNames) => {
177
171
  };
178
172
 
179
173
  /**
180
- * @param {import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord} current
174
+ * @param {import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord} current
181
175
  * @param {ReturnType<import('@agoric/smart-wallet/src/utils.js').makeWalletStateCoalescer>['state']} coalesced
182
- * @param {Awaited<ReturnType<typeof makeAgoricNames>>} agoricNames
176
+ * @param {AgoricNamesRemotes} agoricNames
183
177
  */
184
178
  export const summarize = (current, coalesced, agoricNames) => {
185
179
  return {
@@ -0,0 +1,7 @@
1
+ /** @file Utility library for use in other packages */
2
+
3
+ export * from './bundles.js';
4
+ export * from './casting.js';
5
+ export * from './chain.js';
6
+ export * from './format.js';
7
+ export * from './wallet.js';