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.
- package/README.md +73 -0
- package/package.json +37 -30
- package/src/bin-agops.js +68 -6
- package/src/chain-config.js +19 -2
- package/src/commands/auction.js +170 -0
- package/src/commands/ec.js +314 -0
- package/src/commands/inter.js +626 -0
- package/src/commands/oracle.js +212 -0
- package/src/commands/perf.js +32 -22
- package/src/commands/psm.js +75 -210
- package/src/commands/reserve.js +98 -0
- package/src/commands/test-upgrade.js +109 -0
- package/src/commands/vaults.js +161 -0
- package/src/commands/wallet.js +85 -60
- package/src/cosmos.js +5 -9
- package/src/deploy.js +208 -334
- package/src/entrypoint.js +9 -2
- package/src/follow.js +40 -4
- package/src/init.js +11 -10
- package/src/install.js +25 -30
- package/src/json-http-client-node.js +2 -2
- package/src/json.js +1 -1
- package/src/lib/casting.js +4 -2
- package/src/lib/chain.js +147 -33
- package/src/lib/format.js +58 -43
- package/src/lib/rpc.js +106 -101
- package/src/lib/wallet.js +305 -10
- package/src/main-publish.js +3 -5
- package/src/main.js +74 -42
- package/src/open.js +3 -1
- package/src/publish.js +25 -23
- package/src/run.js +20 -0
- package/src/scripts.js +190 -0
- package/src/sdk-package-names.js +6 -2
- package/src/set-defaults.js +2 -2
- package/src/start.js +69 -35
- package/src/lib/psm.js +0 -82
package/src/follow.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import process from 'process';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
Far,
|
|
5
|
+
getInterfaceOf,
|
|
6
|
+
decodeToJustin,
|
|
7
|
+
makeMarshal,
|
|
8
|
+
} from '@endo/marshal';
|
|
5
9
|
|
|
6
10
|
import {
|
|
7
11
|
iterateLatest,
|
|
@@ -12,11 +16,39 @@ import {
|
|
|
12
16
|
} from '@agoric/casting';
|
|
13
17
|
import { makeLeaderOptions } from './lib/casting.js';
|
|
14
18
|
|
|
19
|
+
const makeCapDataToQclass = () => {
|
|
20
|
+
const valToSlot = new WeakMap();
|
|
21
|
+
const slotToVal = new Map();
|
|
22
|
+
const convertValToSlot = val => {
|
|
23
|
+
return valToSlot.get(val);
|
|
24
|
+
};
|
|
25
|
+
const convertSlotToVal = (slot, iface) => {
|
|
26
|
+
if (slotToVal.has(slot)) {
|
|
27
|
+
return slotToVal.get(slot);
|
|
28
|
+
}
|
|
29
|
+
const debugName = iface.startsWith('Alleged: ')
|
|
30
|
+
? iface.slice('Alleged: '.length)
|
|
31
|
+
: iface;
|
|
32
|
+
const remotable = Far(debugName, {});
|
|
33
|
+
|
|
34
|
+
valToSlot.set(remotable, slot);
|
|
35
|
+
slotToVal.set(slot, remotable);
|
|
36
|
+
return remotable;
|
|
37
|
+
};
|
|
38
|
+
const { toCapData, fromCapData } = makeMarshal(
|
|
39
|
+
convertValToSlot,
|
|
40
|
+
convertSlotToVal,
|
|
41
|
+
);
|
|
42
|
+
const capDataToQclass = capdata => toCapData(fromCapData(capdata));
|
|
43
|
+
return capDataToQclass;
|
|
44
|
+
};
|
|
45
|
+
|
|
15
46
|
export default async function followerMain(progname, rawArgs, powers, opts) {
|
|
16
47
|
const { anylogger } = powers;
|
|
17
48
|
const console = anylogger('agoric:follower');
|
|
18
49
|
|
|
19
50
|
const {
|
|
51
|
+
firstValueOnly,
|
|
20
52
|
proof,
|
|
21
53
|
output,
|
|
22
54
|
bootstrap = 'http://localhost:26657',
|
|
@@ -35,11 +67,12 @@ export default async function followerMain(progname, rawArgs, powers, opts) {
|
|
|
35
67
|
switch (output) {
|
|
36
68
|
case 'justinlines':
|
|
37
69
|
case 'justin': {
|
|
70
|
+
const capDataToQclass = makeCapDataToQclass();
|
|
38
71
|
followerOptions.unserializer = null;
|
|
39
72
|
const pretty = !output.endsWith('lines');
|
|
40
|
-
formatOutput =
|
|
73
|
+
formatOutput = capdata => {
|
|
74
|
+
const { body, slots } = capDataToQclass(capdata);
|
|
41
75
|
const encoded = JSON.parse(body);
|
|
42
|
-
// @ts-expect-error Expected 1-2 arguments, but got 3.
|
|
43
76
|
return decodeToJustin(encoded, pretty, slots);
|
|
44
77
|
};
|
|
45
78
|
break;
|
|
@@ -121,6 +154,9 @@ export default async function followerMain(progname, rawArgs, powers, opts) {
|
|
|
121
154
|
value,
|
|
122
155
|
)}\n`,
|
|
123
156
|
);
|
|
157
|
+
if (firstValueOnly) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
124
160
|
}
|
|
125
161
|
}),
|
|
126
162
|
);
|
package/src/init.js
CHANGED
|
@@ -2,8 +2,10 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { makePspawn } from './helpers.js';
|
|
3
3
|
|
|
4
4
|
// Ambient types. Needed only for dev but this does a runtime import.
|
|
5
|
+
// https://github.com/Agoric/agoric-sdk/issues/6512
|
|
5
6
|
import '@endo/captp/src/types.js';
|
|
6
7
|
import '@agoric/swingset-vat/exported.js';
|
|
8
|
+
import '@agoric/swingset-vat/src/vats/network/types.js';
|
|
7
9
|
|
|
8
10
|
// Use either an absolute template URL, or find it relative to DAPP_URL_BASE.
|
|
9
11
|
const gitURL = (relativeOrAbsoluteURL, base) => {
|
|
@@ -38,15 +40,14 @@ export default async function initMain(_progname, rawArgs, priv, opts) {
|
|
|
38
40
|
dappBranch = ['-b', opts.dappBranch];
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
) {
|
|
43
|
+
const exitStatus = await pspawn(
|
|
44
|
+
'git',
|
|
45
|
+
['clone', '--origin=upstream', dappURL, DIR, ...dappBranch],
|
|
46
|
+
{
|
|
47
|
+
stdio: 'inherit',
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
if (exitStatus) {
|
|
50
51
|
throw Error('cannot clone');
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -55,7 +56,7 @@ export default async function initMain(_progname, rawArgs, priv, opts) {
|
|
|
55
56
|
|
|
56
57
|
let topLevelName;
|
|
57
58
|
const subdirs = ['', 'api/', 'contract/', 'ui/', '_agstate/agoric-servers/'];
|
|
58
|
-
for (const dir of subdirs) {
|
|
59
|
+
for await (const dir of subdirs) {
|
|
59
60
|
const path = `${DIR}/${dir}package.json`;
|
|
60
61
|
log('rewriting ', path);
|
|
61
62
|
|
package/src/install.js
CHANGED
|
@@ -59,7 +59,7 @@ export default async function installMain(progname, rawArgs, powers, opts) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
const yarnInstallEachWorktree = async (phase, ...flags) => {
|
|
62
|
-
for (const workTree of workTrees) {
|
|
62
|
+
for await (const workTree of workTrees) {
|
|
63
63
|
log.info(`yarn install ${phase} in ${workTree}`);
|
|
64
64
|
// eslint-disable-next-line no-await-in-loop
|
|
65
65
|
const yarnInstall = await pspawn(
|
|
@@ -226,15 +226,14 @@ export default async function installMain(progname, rawArgs, powers, opts) {
|
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
} else {
|
|
229
|
+
const subdirPackageJsonExists = async subd => {
|
|
230
|
+
const exists = await fs.stat(`${subd}/package.json`).catch(_ => false);
|
|
231
|
+
return exists && subd;
|
|
232
|
+
};
|
|
229
233
|
const existingSubdirs = await Promise.all(
|
|
230
234
|
['.', '_agstate/agoric-servers', 'contract', 'api', 'ui']
|
|
231
235
|
.sort()
|
|
232
|
-
.map(
|
|
233
|
-
const exists = await fs
|
|
234
|
-
.stat(`${subd}/package.json`)
|
|
235
|
-
.catch(_ => false);
|
|
236
|
-
return exists && subd;
|
|
237
|
-
}),
|
|
236
|
+
.map(subdirPackageJsonExists),
|
|
238
237
|
);
|
|
239
238
|
subdirs = existingSubdirs.filter(subd => subd);
|
|
240
239
|
}
|
|
@@ -254,22 +253,21 @@ export default async function installMain(progname, rawArgs, powers, opts) {
|
|
|
254
253
|
await fs.symlink(sdkRoot, sdkWorktree);
|
|
255
254
|
}
|
|
256
255
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
await fs.unlink(nm).catch(_ => {});
|
|
256
|
+
const removeNodeModulesSymlinks = async subdir => {
|
|
257
|
+
const nm = `${subdir}/node_modules`;
|
|
258
|
+
log(chalk.bold.green(`removing ${nm} link`));
|
|
259
|
+
await fs.unlink(nm).catch(_ => {});
|
|
262
260
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
);
|
|
261
|
+
// Remove all the package links.
|
|
262
|
+
// This is needed to prevent yarn errors when installing new versions of
|
|
263
|
+
// linked modules (like `@agoric/zoe`).
|
|
264
|
+
await Promise.all(
|
|
265
|
+
[...sdkPackageToPath.keys()].map(async pjName =>
|
|
266
|
+
fs.unlink(`${nm}/${pjName}`).catch(_ => {}),
|
|
267
|
+
),
|
|
268
|
+
);
|
|
269
|
+
};
|
|
270
|
+
await Promise.all(subdirs.map(removeNodeModulesSymlinks));
|
|
273
271
|
} else {
|
|
274
272
|
DEFAULT_SDK_PACKAGE_NAMES.forEach(name => sdkPackageToPath.set(name, null));
|
|
275
273
|
}
|
|
@@ -310,17 +308,14 @@ export default async function installMain(progname, rawArgs, powers, opts) {
|
|
|
310
308
|
);
|
|
311
309
|
|
|
312
310
|
const sdkPackages = [...sdkPackageToPath.keys()].sort();
|
|
313
|
-
for (const subdir of subdirs) {
|
|
314
|
-
// eslint-disable-next-line no-await-in-loop
|
|
311
|
+
for await (const subdir of subdirs) {
|
|
315
312
|
const exists = await fs.stat(`${subdir}/package.json`).catch(_ => false);
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
// eslint-disable-next-line no-await-in-loop
|
|
319
|
-
(await pspawn('yarn', [...linkFlags, 'link', ...sdkPackages], {
|
|
313
|
+
const exitStatus = await (exists &&
|
|
314
|
+
pspawn('yarn', [...linkFlags, 'link', ...sdkPackages], {
|
|
320
315
|
stdio: 'inherit',
|
|
321
316
|
cwd: subdir,
|
|
322
|
-
}))
|
|
323
|
-
) {
|
|
317
|
+
}));
|
|
318
|
+
if (exitStatus) {
|
|
324
319
|
log.error('Cannot yarn link', ...sdkPackages);
|
|
325
320
|
return 1;
|
|
326
321
|
}
|
|
@@ -48,14 +48,14 @@ export const makeJsonHttpClient = ({ http }) => {
|
|
|
48
48
|
const responseBytes = Buffer.concat(chunks);
|
|
49
49
|
const responseText = textDecoder.decode(responseBytes);
|
|
50
50
|
if (response.statusCode !== 200) {
|
|
51
|
-
throw
|
|
51
|
+
throw Error(`${responseText}`);
|
|
52
52
|
}
|
|
53
53
|
// May throw: becomes rejection below.
|
|
54
54
|
const responseBody = JSON.parse(responseText);
|
|
55
55
|
resolve(responseBody);
|
|
56
56
|
})().catch(cause => {
|
|
57
57
|
reject(
|
|
58
|
-
|
|
58
|
+
Error(`JSON HTTP RPC failed with error: ${cause.message}`, {
|
|
59
59
|
cause,
|
|
60
60
|
}),
|
|
61
61
|
);
|
package/src/json.js
CHANGED
|
@@ -12,7 +12,7 @@ export const parseLocatedJson = (source, location) => {
|
|
|
12
12
|
return JSON.parse(source);
|
|
13
13
|
} catch (error) {
|
|
14
14
|
if (error instanceof SyntaxError) {
|
|
15
|
-
throw
|
|
15
|
+
throw SyntaxError(`Cannot parse JSON from ${location}, ${error}`);
|
|
16
16
|
}
|
|
17
17
|
throw error;
|
|
18
18
|
}
|
package/src/lib/casting.js
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
import { delay, exponentialBackoff, randomBackoff } from '@agoric/casting';
|
|
4
4
|
|
|
5
5
|
// TODO: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
|
|
6
|
-
/**
|
|
7
|
-
* @
|
|
6
|
+
/**
|
|
7
|
+
* @param {{log: (...args: any) => void, sleep: number, jitter: number }} config
|
|
8
|
+
* @returns {import('@agoric/casting').LeaderOptions}
|
|
9
|
+
*/
|
|
8
10
|
export const makeLeaderOptions = ({ log, sleep, jitter }) => {
|
|
9
11
|
return {
|
|
10
12
|
retryCallback: (where, e, attempt) => {
|
package/src/lib/chain.js
CHANGED
|
@@ -1,60 +1,174 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
/* global process */
|
|
3
3
|
import { normalizeBech32 } from '@cosmjs/encoding';
|
|
4
|
-
import {
|
|
4
|
+
import { execFileSync as execFileSyncAmbient } from 'child_process';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const agdBinary = 'agd';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} literalOrName
|
|
10
|
+
* @param {{ keyringBackend?: string }} opts
|
|
11
|
+
* @param {{ execFileSync: typeof execFileSyncAmbient }} [io]
|
|
12
|
+
*/
|
|
13
|
+
export const normalizeAddressWithOptions = (
|
|
14
|
+
literalOrName,
|
|
15
|
+
{ keyringBackend = undefined } = {},
|
|
16
|
+
io = { execFileSync: execFileSyncAmbient },
|
|
17
|
+
) => {
|
|
7
18
|
try {
|
|
8
19
|
return normalizeBech32(literalOrName);
|
|
9
20
|
} catch (_) {
|
|
10
21
|
// not an address so try as a key
|
|
11
|
-
const
|
|
22
|
+
const backendOpt = keyringBackend
|
|
23
|
+
? [`--keyring-backend=${keyringBackend}`]
|
|
24
|
+
: [];
|
|
25
|
+
const buff = io.execFileSync(agdBinary, [
|
|
26
|
+
`keys`,
|
|
27
|
+
...backendOpt,
|
|
28
|
+
`show`,
|
|
29
|
+
`--address`,
|
|
30
|
+
literalOrName,
|
|
31
|
+
]);
|
|
12
32
|
return normalizeBech32(buff.toString().trim());
|
|
13
33
|
}
|
|
14
34
|
};
|
|
15
|
-
harden(
|
|
35
|
+
harden(normalizeAddressWithOptions);
|
|
16
36
|
|
|
17
37
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
38
|
+
* @param {ReadonlyArray<string>} swingsetArgs
|
|
39
|
+
* @param {import('./rpc').MinimalNetworkConfig & {
|
|
40
|
+
* from: string,
|
|
41
|
+
* dryRun?: boolean,
|
|
42
|
+
* verbose?: boolean,
|
|
43
|
+
* keyring?: {home?: string, backend: string}
|
|
44
|
+
* stdout?: Pick<import('stream').Writable, 'write'>
|
|
45
|
+
* execFileSync?: typeof import('child_process').execFileSync
|
|
46
|
+
* }} opts
|
|
25
47
|
*/
|
|
26
|
-
export const execSwingsetTransaction = (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
export const execSwingsetTransaction = (swingsetArgs, opts) => {
|
|
49
|
+
const {
|
|
50
|
+
from,
|
|
51
|
+
dryRun = false,
|
|
52
|
+
verbose = true,
|
|
53
|
+
keyring = undefined,
|
|
54
|
+
chainName,
|
|
55
|
+
rpcAddrs,
|
|
56
|
+
stdout = process.stdout,
|
|
57
|
+
execFileSync = execFileSyncAmbient,
|
|
58
|
+
} = opts;
|
|
59
|
+
const homeOpt = keyring?.home ? [`--home=${keyring.home}`] : [];
|
|
36
60
|
const backendOpt = keyring?.backend
|
|
37
|
-
? `--keyring-backend=${keyring.backend}`
|
|
38
|
-
:
|
|
39
|
-
const cmd =
|
|
61
|
+
? [`--keyring-backend=${keyring.backend}`]
|
|
62
|
+
: [];
|
|
63
|
+
const cmd = [`--node=${rpcAddrs[0]}`, `--chain-id=${chainName}`].concat(
|
|
64
|
+
homeOpt,
|
|
65
|
+
backendOpt,
|
|
66
|
+
[`--from=${from}`, 'tx', 'swingset'],
|
|
67
|
+
swingsetArgs,
|
|
68
|
+
);
|
|
40
69
|
|
|
41
70
|
if (dryRun) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
71
|
+
stdout.write(`Run this interactive command in shell:\n\n`);
|
|
72
|
+
stdout.write(`${agdBinary} `);
|
|
73
|
+
stdout.write(cmd.join(' '));
|
|
74
|
+
stdout.write('\n');
|
|
45
75
|
} else {
|
|
46
|
-
const yesCmd =
|
|
47
|
-
console.log('Executing ', yesCmd);
|
|
48
|
-
|
|
76
|
+
const yesCmd = cmd.concat(['--yes']);
|
|
77
|
+
if (verbose) console.log('Executing ', yesCmd);
|
|
78
|
+
return execFileSync(agdBinary, yesCmd, { encoding: 'utf-8' });
|
|
49
79
|
}
|
|
50
80
|
};
|
|
51
81
|
harden(execSwingsetTransaction);
|
|
52
82
|
|
|
53
83
|
// xxx rpc should be able to query this by HTTP without shelling out
|
|
54
84
|
export const fetchSwingsetParams = net => {
|
|
55
|
-
const { chainName, rpcAddrs } = net;
|
|
56
|
-
const cmd =
|
|
57
|
-
|
|
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);
|
|
58
96
|
return JSON.parse(buffer.toString());
|
|
59
97
|
};
|
|
60
98
|
harden(fetchSwingsetParams);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {import('./rpc').MinimalNetworkConfig & {
|
|
102
|
+
* execFileSync: typeof import('child_process').execFileSync,
|
|
103
|
+
* delay: (ms: number) => Promise<void>,
|
|
104
|
+
* period?: number,
|
|
105
|
+
* retryMessage?: string,
|
|
106
|
+
* }} opts
|
|
107
|
+
* @returns {<T>(l: (b: { time: string, height: string }) => Promise<T>) => Promise<T>}
|
|
108
|
+
*/
|
|
109
|
+
export const pollBlocks = opts => async lookup => {
|
|
110
|
+
const { execFileSync, delay, rpcAddrs, period = 3 * 1000 } = opts;
|
|
111
|
+
const { retryMessage } = opts;
|
|
112
|
+
|
|
113
|
+
const nodeArgs = [`--node=${rpcAddrs[0]}`];
|
|
114
|
+
|
|
115
|
+
await null; // separate sync prologue
|
|
116
|
+
|
|
117
|
+
for (;;) {
|
|
118
|
+
const sTxt = execFileSync(agdBinary, ['status', ...nodeArgs]);
|
|
119
|
+
const status = JSON.parse(sTxt.toString());
|
|
120
|
+
const {
|
|
121
|
+
SyncInfo: { latest_block_time: time, latest_block_height: height },
|
|
122
|
+
} = status;
|
|
123
|
+
try {
|
|
124
|
+
// see await null above
|
|
125
|
+
// eslint-disable-next-line @jessie.js/no-nested-await, no-await-in-loop
|
|
126
|
+
const result = await lookup({ time, height });
|
|
127
|
+
return result;
|
|
128
|
+
} catch (_err) {
|
|
129
|
+
console.error(
|
|
130
|
+
time,
|
|
131
|
+
retryMessage || 'not in block',
|
|
132
|
+
height,
|
|
133
|
+
'retrying...',
|
|
134
|
+
);
|
|
135
|
+
// eslint-disable-next-line @jessie.js/no-nested-await, no-await-in-loop
|
|
136
|
+
await delay(period);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {string} txhash
|
|
143
|
+
* @param {import('./rpc').MinimalNetworkConfig & {
|
|
144
|
+
* execFileSync: typeof import('child_process').execFileSync,
|
|
145
|
+
* delay: (ms: number) => Promise<void>,
|
|
146
|
+
* period?: number,
|
|
147
|
+
* }} opts
|
|
148
|
+
*/
|
|
149
|
+
export const pollTx = async (txhash, opts) => {
|
|
150
|
+
const { execFileSync, rpcAddrs, chainName } = opts;
|
|
151
|
+
|
|
152
|
+
const nodeArgs = [`--node=${rpcAddrs[0]}`];
|
|
153
|
+
const outJson = ['--output', 'json'];
|
|
154
|
+
|
|
155
|
+
const lookup = async () => {
|
|
156
|
+
const out = execFileSync(
|
|
157
|
+
agdBinary,
|
|
158
|
+
[
|
|
159
|
+
'query',
|
|
160
|
+
'tx',
|
|
161
|
+
txhash,
|
|
162
|
+
`--chain-id=${chainName}`,
|
|
163
|
+
...nodeArgs,
|
|
164
|
+
...outJson,
|
|
165
|
+
],
|
|
166
|
+
{ stdio: ['ignore', 'pipe', 'ignore'] },
|
|
167
|
+
);
|
|
168
|
+
// XXX this type is defined in a .proto file somewhere
|
|
169
|
+
/** @type {{ height: string, txhash: string, code: number, timestamp: string }} */
|
|
170
|
+
const info = JSON.parse(out.toString());
|
|
171
|
+
return info;
|
|
172
|
+
};
|
|
173
|
+
return pollBlocks({ ...opts, retryMessage: 'tx not in block' })(lookup);
|
|
174
|
+
};
|
package/src/lib/format.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
|
|
2
|
+
import { makeBoardRemote } from '@agoric/vats/tools/board-utils.js';
|
|
3
3
|
// eslint-disable-next-line no-unused-vars -- typeof below
|
|
4
4
|
import { makeAgoricNames } from './rpc.js';
|
|
5
5
|
|
|
6
6
|
// ambient types
|
|
7
|
-
import '@agoric/ertp/src/types.js';
|
|
7
|
+
import '@agoric/ertp/src/types-ambient.js';
|
|
8
8
|
|
|
9
|
-
/** @typedef {import('@agoric/
|
|
10
|
-
|
|
11
|
-
export const COSMOS_UNIT = 1_000_000n;
|
|
9
|
+
/** @typedef {import('@agoric/vats/tools/board-utils.js').BoardRemote} BoardRemote */
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
12
|
* Like @endo/nat but coerces
|
|
@@ -19,44 +17,55 @@ export const COSMOS_UNIT = 1_000_000n;
|
|
|
19
17
|
export const Natural = str => {
|
|
20
18
|
const b = BigInt(str);
|
|
21
19
|
if (b < 0) {
|
|
22
|
-
throw
|
|
20
|
+
throw RangeError(`${b} is negative`);
|
|
23
21
|
}
|
|
24
22
|
return b;
|
|
25
23
|
};
|
|
26
24
|
|
|
25
|
+
/**
|
|
26
|
+
* JSON.stringify replacer to handle bigint
|
|
27
|
+
*
|
|
28
|
+
* @param {unknown} k
|
|
29
|
+
* @param {unknown} v
|
|
30
|
+
*/
|
|
31
|
+
export const bigintReplacer = (k, v) => (typeof v === 'bigint' ? `${v}` : v);
|
|
32
|
+
|
|
33
|
+
/** @type {import('@agoric/vats/tools/board-utils.js').VBankAssetDetail} */
|
|
27
34
|
// eslint-disable-next-line no-unused-vars
|
|
28
35
|
const exampleAsset = {
|
|
29
|
-
|
|
36
|
+
// @ts-expect-error cast
|
|
37
|
+
brand: makeBoardRemote({ boardId: 'board0425', iface: 'Alleged: BLD brand' }),
|
|
30
38
|
displayInfo: { assetKind: 'nat', decimalPlaces: 6 },
|
|
31
|
-
|
|
39
|
+
// @ts-expect-error cast
|
|
40
|
+
issuer: makeBoardRemote({ boardId: null, iface: undefined }),
|
|
32
41
|
petname: 'Agoric staking token',
|
|
33
42
|
};
|
|
34
|
-
/** @typedef {import('@agoric/
|
|
43
|
+
/** @typedef {import('@agoric/vats/tools/board-utils.js').VBankAssetDetail } AssetDescriptor */
|
|
35
44
|
|
|
36
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* @param {AssetDescriptor[]} assets
|
|
47
|
+
* @returns {(a: Amount & { brand: BoardRemote }) => [string, number | any[]]}
|
|
48
|
+
*/
|
|
37
49
|
export const makeAmountFormatter = assets => amt => {
|
|
50
|
+
const { brand, value } = amt;
|
|
51
|
+
const boardId = brand.getBoardId();
|
|
52
|
+
const asset = assets.find(a => a.brand.getBoardId() === boardId);
|
|
53
|
+
if (!asset) return [boardId, Number(value)]; // don't crash
|
|
38
54
|
const {
|
|
39
|
-
brand: { boardId },
|
|
40
|
-
value,
|
|
41
|
-
} = amt;
|
|
42
|
-
const asset = assets.find(a => a.brand.boardId === boardId);
|
|
43
|
-
if (!asset) return [NaN, boardId];
|
|
44
|
-
const {
|
|
45
|
-
petname,
|
|
46
55
|
displayInfo: { assetKind, decimalPlaces = 0 },
|
|
56
|
+
issuerName,
|
|
47
57
|
} = asset;
|
|
48
|
-
const name = Array.isArray(petname) ? petname.join('.') : petname;
|
|
49
58
|
switch (assetKind) {
|
|
50
59
|
case 'nat':
|
|
51
|
-
|
|
52
|
-
return [name, Number(value) / 10 ** decimalPlaces];
|
|
60
|
+
return [issuerName, Number(value) / 10 ** decimalPlaces];
|
|
53
61
|
case 'set':
|
|
62
|
+
assert(Array.isArray(value));
|
|
54
63
|
if (value[0]?.handle?.iface?.includes('InvitationHandle')) {
|
|
55
|
-
return [
|
|
64
|
+
return [issuerName, value.map(v => v.description)];
|
|
56
65
|
}
|
|
57
|
-
return [
|
|
66
|
+
return [issuerName, value];
|
|
58
67
|
default:
|
|
59
|
-
return [
|
|
68
|
+
return [issuerName, [NaN]];
|
|
60
69
|
}
|
|
61
70
|
};
|
|
62
71
|
|
|
@@ -66,15 +75,31 @@ export const asPercent = ratio => {
|
|
|
66
75
|
return (100 * Number(numerator.value)) / Number(denominator.value);
|
|
67
76
|
};
|
|
68
77
|
|
|
78
|
+
const { Fail, quote: q } = assert;
|
|
79
|
+
|
|
80
|
+
const isObject = x => typeof x === 'object' && x !== null;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {Amount} x
|
|
84
|
+
* @returns {Amount & { brand: BoardRemote }}
|
|
85
|
+
*/
|
|
86
|
+
export const asBoardRemote = x => {
|
|
87
|
+
isObject(x) || Fail`not object: ${q(x)}`;
|
|
88
|
+
isObject(x.brand) || Fail`not object: ${q(x.brand)}`;
|
|
89
|
+
'getBoardId' in x.brand || Fail`missing getBoardId: ${q(x.brand)}`;
|
|
90
|
+
// @ts-expect-error dynamic check
|
|
91
|
+
return x;
|
|
92
|
+
};
|
|
93
|
+
|
|
69
94
|
/**
|
|
70
95
|
* Summarize the balances array as user-facing informative tuples
|
|
71
|
-
|
|
96
|
+
*
|
|
72
97
|
* @param {import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord['purses']} purses
|
|
73
98
|
* @param {AssetDescriptor[]} assets
|
|
74
99
|
*/
|
|
75
100
|
export const purseBalanceTuples = (purses, assets) => {
|
|
76
101
|
const fmt = makeAmountFormatter(assets);
|
|
77
|
-
return purses.map(b => fmt(b.balance));
|
|
102
|
+
return purses.map(b => fmt(asBoardRemote(b.balance)));
|
|
78
103
|
};
|
|
79
104
|
|
|
80
105
|
/**
|
|
@@ -100,12 +125,8 @@ export const fmtRecordOfLines = record => {
|
|
|
100
125
|
* @param {Awaited<ReturnType<typeof makeAgoricNames>>} agoricNames
|
|
101
126
|
*/
|
|
102
127
|
export const offerStatusTuples = (state, agoricNames) => {
|
|
103
|
-
const {
|
|
104
|
-
const fmt = makeAmountFormatter(
|
|
105
|
-
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error -- https://github.com/Agoric/agoric-sdk/issues/4620 */
|
|
106
|
-
// @ts-ignore xxx RpcRemote
|
|
107
|
-
[...brands.values()],
|
|
108
|
-
);
|
|
128
|
+
const { offerStatuses } = state;
|
|
129
|
+
const fmt = makeAmountFormatter(Object.values(agoricNames.vbankAsset));
|
|
109
130
|
const fmtRecord = r =>
|
|
110
131
|
r
|
|
111
132
|
? Object.fromEntries(
|
|
@@ -116,9 +137,10 @@ export const offerStatusTuples = (state, agoricNames) => {
|
|
|
116
137
|
assert(o);
|
|
117
138
|
let when = '??';
|
|
118
139
|
try {
|
|
119
|
-
|
|
140
|
+
const timestamp = String(o.id).replace(/\D+/g, '');
|
|
141
|
+
when = new Date(Number(timestamp)).toISOString();
|
|
120
142
|
} catch {
|
|
121
|
-
console.debug('offer id', o.id, 'not a timestamp');
|
|
143
|
+
console.debug('offer id', o.id, 'does not contain a timestamp');
|
|
122
144
|
}
|
|
123
145
|
const {
|
|
124
146
|
proposal: { give, want },
|
|
@@ -136,7 +158,6 @@ export const offerStatusTuples = (state, agoricNames) => {
|
|
|
136
158
|
} = o;
|
|
137
159
|
// xxx could be O(1)
|
|
138
160
|
const entry = Object.entries(agoricNames.instance).find(
|
|
139
|
-
// @ts-ignore minimarshal types are off by a bit
|
|
140
161
|
([_name, candidate]) => candidate === instance,
|
|
141
162
|
);
|
|
142
163
|
const instanceName = entry ? entry[0] : '???';
|
|
@@ -156,27 +177,21 @@ export const offerStatusTuples = (state, agoricNames) => {
|
|
|
156
177
|
};
|
|
157
178
|
|
|
158
179
|
/**
|
|
159
|
-
*
|
|
160
180
|
* @param {import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord} current
|
|
161
181
|
* @param {ReturnType<import('@agoric/smart-wallet/src/utils.js').makeWalletStateCoalescer>['state']} coalesced
|
|
162
182
|
* @param {Awaited<ReturnType<typeof makeAgoricNames>>} agoricNames
|
|
163
183
|
*/
|
|
164
184
|
export const summarize = (current, coalesced, agoricNames) => {
|
|
165
185
|
return {
|
|
166
|
-
lastOfferId: [current.lastOfferId],
|
|
167
186
|
purses: purseBalanceTuples(
|
|
168
|
-
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error -- https://github.com/Agoric/agoric-sdk/issues/4620 */
|
|
169
|
-
// @ts-ignore xxx RpcRemote
|
|
170
187
|
[...current.purses.values()],
|
|
171
|
-
|
|
172
|
-
// @ts-ignore xxx RpcRemote
|
|
173
|
-
[...current.brands.values()],
|
|
188
|
+
Object.values(agoricNames.vbankAsset),
|
|
174
189
|
),
|
|
175
|
-
usedInvitations:
|
|
190
|
+
usedInvitations: current.offerToUsedInvitation.map(
|
|
176
191
|
([offerId, invitationAmt]) => [
|
|
177
192
|
agoricNames.reverse[invitationAmt.value[0].instance.boardId],
|
|
178
193
|
invitationAmt.value[0].description,
|
|
179
|
-
|
|
194
|
+
offerId,
|
|
180
195
|
],
|
|
181
196
|
),
|
|
182
197
|
offers: offerStatusTuples(coalesced, agoricNames),
|