Skip to content

Commit d4f2864

Browse files
authored
new client-utils package (#10407)
closes: #10168 ## Description Provide a new package, `client-utils`, as a home for utilities that are useful to clients of an Agoric chain. This doesn't currently use `@agoric/rpc` but over time some of it may be pushed down into that package. Related work… - #9200 This will be where those client factories are kept. - #10369 This will solve most of what we need for functional testing. Some aspects are specific to the A3P synthetic chain (like account addresses and references to history) but most of what the tests are doing with the chain are operations that any client might do. - #9109 - #8963 This will contribute to those goals. ### Security Considerations Reduces authority needed to query chain (from child_process to fetch) ### Scaling Considerations This is a big package, but it's not to be run on chain. Most client apps use some form of code shaking so they'll only take what they need. ### Documentation Considerations Once this settles down it ought to be a part of docs.agoric.com ### Testing Considerations The only test yet is a live one to make sure a query to Emerynet for Swingset params succeeds as expected, even under SES. No package CI yet. Mostly it's refactoring of existing code so those uses serve as coverage. I do think this would benefit from some additional testing. ### Upgrade Considerations Will never be on chain
2 parents 8943c95 + c3f5438 commit d4f2864

36 files changed

+507
-223
lines changed

Diff for: .github/workflows/test-all-packages.yml

+3
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ jobs:
264264
- name: yarn test (casting)
265265
if: (success() || failure())
266266
run: cd packages/casting && yarn ${{ steps.vars.outputs.test }} | $TEST_COLLECT
267+
- name: yarn test (client-utils)
268+
if: (success() || failure())
269+
run: cd packages/client-utils && yarn ${{ steps.vars.outputs.test }} | $TEST_COLLECT
267270
- name: yarn test (create-dapp)
268271
run: cd packages/create-dapp && yarn ${{ steps.vars.outputs.test }} | $TEST_COLLECT
269272
- name: yarn test (fast-usdc)

Diff for: packages/agoric-cli/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@endo/errors": "^1.2.7",
4141
"@agoric/cache": "^0.3.2",
4242
"@agoric/casting": "^0.4.2",
43+
"@agoric/client-utils": "^0.1.0",
4344
"@agoric/cosmic-proto": "^0.4.0",
4445
"@agoric/ertp": "^0.16.2",
4546
"@agoric/governance": "^0.10.3",

Diff for: packages/agoric-cli/src/commands/auction.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
// @ts-check
2-
2+
/* eslint-env node */
33
import { InvalidArgumentError } from 'commander';
44
import { Fail } from '@endo/errors';
5-
import { makeRpcUtils } from '../lib/rpc.js';
5+
import { makeRpcUtils } from '@agoric/client-utils';
66
import { outputActionAndHint } from '../lib/wallet.js';
7+
import { getNetworkConfig } from '../lib/network-config.js';
78

89
/**
910
* @import {ParamTypesMap, ParamTypesMapFromRecord} from '@agoric/governance/src/contractGovernance/typedParamManager.js'
1011
* @import {ParamValueForType} from '@agoric/governance/src/types.js'
1112
*/
1213

14+
const networkConfig = await getNetworkConfig({ env: process.env, fetch });
15+
1316
/**
1417
* @template {ParamTypesMap} M
1518
* @typedef {{
@@ -86,7 +89,10 @@ export const makeAuctionCommand = (
8689
* }} opts
8790
*/
8891
async opts => {
89-
const { agoricNames, readLatestHead } = await makeRpcUtils({ fetch });
92+
const { agoricNames, readLatestHead } = await makeRpcUtils(
93+
{ fetch },
94+
networkConfig,
95+
);
9096

9197
/** @type {{ current: AuctionParamRecord }} */
9298
// @ts-expect-error XXX should runtime check?

Diff for: packages/agoric-cli/src/commands/gov.js

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// @ts-check
22
/* eslint-disable func-names */
3-
/* global globalThis, process, setTimeout */
3+
/* eslint-env node */
4+
import { makeRpcUtils } from '@agoric/client-utils';
45
import { execFileSync as execFileSyncAmbient } from 'child_process';
56
import { Command, CommanderError } from 'commander';
67
import { normalizeAddressWithOptions, pollBlocks } from '../lib/chain.js';
7-
import { getNetworkConfig, makeRpcUtils } from '../lib/rpc.js';
8+
import { getNetworkConfig } from '../lib/network-config.js';
89
import {
910
findContinuingIds,
1011
getCurrent,
@@ -25,6 +26,8 @@ const collectValues = (val, memo) => {
2526

2627
const defaultKeyring = process.env.AGORIC_KEYRING_BACKEND || 'test';
2728

29+
const networkConfig = await getNetworkConfig({ env: process.env, fetch });
30+
2831
/**
2932
* @param {import('anylogger').Logger} _logger
3033
* @param {{
@@ -40,10 +43,9 @@ export const makeGovCommand = (_logger, io = {}) => {
4043
const {
4144
// Allow caller to provide access explicitly, but
4245
// default to conventional ambient IO facilities.
43-
env = process.env,
4446
stdout = process.stdout,
4547
stderr = process.stderr,
46-
fetch = globalThis.fetch,
48+
fetch = global.fetch,
4749
execFileSync = execFileSyncAmbient,
4850
delay = ms => new Promise(resolve => setTimeout(resolve, ms)),
4951
} = io;
@@ -95,8 +97,7 @@ export const makeGovCommand = (_logger, io = {}) => {
9597
{ toOffer, sendFrom, keyringBackend },
9698
optUtils,
9799
) {
98-
const networkConfig = await getNetworkConfig(env);
99-
const utils = await (optUtils || makeRpcUtils({ fetch }));
100+
const utils = await (optUtils || makeRpcUtils({ fetch }, networkConfig));
100101
const { agoricNames, readLatestHead } = utils;
101102

102103
assert(keyringBackend, 'missing keyring-backend option');
@@ -264,7 +265,10 @@ export const makeGovCommand = (_logger, io = {}) => {
264265
)
265266
.requiredOption('--for <string>', 'description of the invitation')
266267
.action(async opts => {
267-
const { agoricNames, readLatestHead } = await makeRpcUtils({ fetch });
268+
const { agoricNames, readLatestHead } = await makeRpcUtils(
269+
{ fetch },
270+
networkConfig,
271+
);
268272
const current = await getCurrent(opts.from, { readLatestHead });
269273

270274
const known = findContinuingIds(current, agoricNames);
@@ -290,7 +294,10 @@ export const makeGovCommand = (_logger, io = {}) => {
290294
normalizeAddress,
291295
)
292296
.action(async opts => {
293-
const { agoricNames, readLatestHead } = await makeRpcUtils({ fetch });
297+
const { agoricNames, readLatestHead } = await makeRpcUtils(
298+
{ fetch },
299+
networkConfig,
300+
);
294301
const current = await getCurrent(opts.from, { readLatestHead });
295302

296303
const found = findContinuingIds(current, agoricNames);
@@ -326,7 +333,7 @@ export const makeGovCommand = (_logger, io = {}) => {
326333
normalizeAddress,
327334
)
328335
.action(async function (opts, options) {
329-
const utils = await makeRpcUtils({ fetch });
336+
const utils = await makeRpcUtils({ fetch }, networkConfig);
330337
const { readLatestHead } = utils;
331338

332339
const info = await readLatestHead(

Diff for: packages/agoric-cli/src/commands/inter.js

+8-13
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,20 @@
55

66
// @ts-check
77
import { CommanderError, InvalidArgumentError } from 'commander';
8-
// TODO: should get M from endo https://github.com/Agoric/agoric-sdk/issues/7090
8+
import { makeWalletUtils } from '@agoric/client-utils';
99
import { makeOfferSpecShape } from '@agoric/inter-protocol/src/auction/auctionBook.js';
1010
import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
1111
import { objectMap } from '@agoric/internal';
12-
import { M, matches } from '@agoric/store';
12+
import { M, matches } from '@endo/patterns';
1313

1414
import { normalizeAddressWithOptions, pollBlocks } from '../lib/chain.js';
15+
import { getNetworkConfig } from '../lib/network-config.js';
16+
import { getCurrent, outputActionAndHint, sendAction } from '../lib/wallet.js';
1517
import {
1618
asBoardRemote,
17-
bigintReplacer,
1819
makeAmountFormatter,
20+
bigintReplacer,
1921
} from '../lib/format.js';
20-
import { getNetworkConfig } from '../lib/rpc.js';
21-
import {
22-
getCurrent,
23-
makeWalletUtils,
24-
outputActionAndHint,
25-
sendAction,
26-
} from '../lib/wallet.js';
2722

2823
const { values } = Object;
2924

@@ -238,8 +233,8 @@ export const makeInterCommand = (
238233
try {
239234
// XXX pass fetch to getNetworkConfig() explicitly
240235
// await null above makes this await safe
241-
const networkConfig = await getNetworkConfig(env);
242-
return makeWalletUtils({ fetch, execFileSync, delay }, networkConfig);
236+
const networkConfig = await getNetworkConfig({ env, fetch });
237+
return makeWalletUtils({ fetch, delay }, networkConfig);
243238
} catch (err) {
244239
// CommanderError is a class constructor, and so
245240
// must be invoked with `new`.
@@ -334,7 +329,7 @@ inter auction status
334329
* @param {string} from
335330
* @param {import('@agoric/smart-wallet/src/offers.js').OfferSpec} offer
336331
* @param {Awaited<ReturnType<tryMakeUtils>>} tools
337-
* @param {boolean?} dryRun
332+
* @param {boolean | undefined} dryRun
338333
*/
339334
const placeBid = async (from, offer, tools, dryRun = false) => {
340335
const { networkConfig, agoricNames, pollOffer } = tools;

Diff for: packages/agoric-cli/src/commands/oracle.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
// @ts-check
22
/* eslint-disable func-names */
33
/* eslint-env node */
4+
import {
5+
makeRpcUtils,
6+
makeWalletUtils,
7+
storageHelper,
8+
} from '@agoric/client-utils';
49
import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
510
import { oracleBrandFeedName } from '@agoric/inter-protocol/src/proposals/utils.js';
611
import { Fail } from '@endo/errors';
@@ -9,15 +14,14 @@ import * as cp from 'child_process';
914
import { Command } from 'commander';
1015
import { inspect } from 'util';
1116
import { normalizeAddressWithOptions } from '../lib/chain.js';
12-
import { bigintReplacer } from '../lib/format.js';
13-
import { getNetworkConfig, makeRpcUtils, storageHelper } from '../lib/rpc.js';
17+
import { getNetworkConfig } from '../lib/network-config.js';
1418
import {
1519
getCurrent,
16-
makeWalletUtils,
1720
outputAction,
1821
sendAction,
1922
sendHint,
2023
} from '../lib/wallet.js';
24+
import { bigintReplacer } from '../lib/format.js';
2125

2226
/** @import {PriceAuthority, PriceDescription, PriceQuote, PriceQuoteValue, PriceQuery,} from '@agoric/zoe/tools/types.js'; */
2327

@@ -82,8 +86,8 @@ export const makeOracleCommand = (logger, io = {}) => {
8286

8387
const rpcTools = async () => {
8488
// XXX pass fetch to getNetworkConfig() explicitly
85-
const networkConfig = await getNetworkConfig(env);
86-
const utils = await makeRpcUtils({ fetch });
89+
const networkConfig = await getNetworkConfig({ env: process.env, fetch });
90+
const utils = await makeRpcUtils({ fetch }, networkConfig);
8791

8892
const lookupPriceAggregatorInstance = ([brandIn, brandOut]) => {
8993
const name = oracleBrandFeedName(brandIn, brandOut);
@@ -267,10 +271,7 @@ export const makeOracleCommand = (logger, io = {}) => {
267271
) => {
268272
const { readLatestHead, networkConfig, lookupPriceAggregatorInstance } =
269273
await rpcTools();
270-
const wutil = await makeWalletUtils(
271-
{ fetch, execFileSync, delay },
272-
networkConfig,
273-
);
274+
const wutil = await makeWalletUtils({ fetch, delay }, networkConfig);
274275
const unitPrice = scaleDecimals(price);
275276

276277
const feedPath = `published.priceFeed.${pair[0]}-${pair[1]}_price_feed`;

Diff for: packages/agoric-cli/src/commands/perf.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,23 @@ import {
77
makeFollower,
88
makeLeaderFromRpcAddresses,
99
} from '@agoric/casting';
10+
import { slotToRemotable } from '@agoric/internal/src/storage-test-utils.js';
11+
import { boardSlottingMarshaller } from '@agoric/vats/tools/board-utils.js';
1012
import { Command } from 'commander';
1113
import fs from 'fs';
1214
import { exit } from 'process';
13-
import { slotToRemotable } from '@agoric/internal/src/storage-test-utils.js';
14-
import { boardSlottingMarshaller } from '@agoric/vats/tools/board-utils.js';
1515
import { makeLeaderOptions } from '../lib/casting.js';
1616
import {
1717
execSwingsetTransaction,
1818
normalizeAddressWithOptions,
1919
} from '../lib/chain.js';
20-
import { networkConfig } from '../lib/rpc.js';
20+
import { getNetworkConfig } from '../lib/network-config.js';
2121

2222
// tight for perf testing but less than this tends to hang.
2323
const SLEEP_SECONDS = 0.1;
2424

25+
const networkConfig = await getNetworkConfig({ env: process.env, fetch });
26+
2527
/**
2628
* @param {import('anylogger').Logger} logger
2729
*/

Diff for: packages/agoric-cli/src/commands/psm.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// @ts-check
22
/* eslint-disable func-names */
33
/* eslint-env node */
4-
import { Command } from 'commander';
4+
import { makeRpcUtils, storageHelper } from '@agoric/client-utils';
55
import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
6-
import { asPercent } from '../lib/format.js';
7-
import { makeRpcUtils, storageHelper } from '../lib/rpc.js';
6+
import { Command } from 'commander';
7+
import { getNetworkConfig } from '../lib/network-config.js';
88
import { outputExecuteOfferAction } from '../lib/wallet.js';
9+
import { asPercent } from '../lib/format.js';
10+
11+
const networkConfig = await getNetworkConfig({ env: process.env, fetch });
912

1013
// Adapted from https://gist.github.com/dckc/8b5b2f16395cb4d7f2ff340e0bc6b610#file-psm-tool
1114

@@ -60,7 +63,7 @@ export const makePsmCommand = logger => {
6063
);
6164

6265
const rpcTools = async () => {
63-
const utils = await makeRpcUtils({ fetch });
66+
const utils = await makeRpcUtils({ fetch }, networkConfig);
6467

6568
const lookupPsmInstance = ([minted, anchor]) => {
6669
const name = `psm-${minted}-${anchor}`;

Diff for: packages/agoric-cli/src/commands/reserve.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// @ts-check
22
/* eslint-disable func-names */
33
/* eslint-env node */
4+
import { makeRpcUtils } from '@agoric/client-utils';
45
import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
56
import { Command } from 'commander';
6-
import { makeRpcUtils } from '../lib/rpc.js';
7+
import { getNetworkConfig } from '../lib/network-config.js';
78
import { outputActionAndHint } from '../lib/wallet.js';
89

10+
const networkConfig = await getNetworkConfig({ env: process.env, fetch });
11+
912
/**
1013
* @param {import('anylogger').Logger} _logger
1114
* @param {*} io
@@ -29,7 +32,7 @@ export const makeReserveCommand = (_logger, io = {}) => {
2932
* }} opts
3033
*/
3134
async ({ collateralBrand, ...opts }) => {
32-
const { agoricNames } = await makeRpcUtils({ fetch });
35+
const { agoricNames } = await makeRpcUtils({ fetch }, networkConfig);
3336

3437
const offer = Offers.reserve.AddCollateral(agoricNames, {
3538
collateralBrandKey: collateralBrand,
@@ -63,7 +66,7 @@ export const makeReserveCommand = (_logger, io = {}) => {
6366
1,
6467
)
6568
.action(async function (opts) {
66-
const { agoricNames } = await makeRpcUtils({ fetch });
69+
const { agoricNames } = await makeRpcUtils({ fetch }, networkConfig);
6770

6871
const reserveInstance = agoricNames.instance.reserve;
6972
assert(reserveInstance, 'missing reserve in names');

Diff for: packages/agoric-cli/src/commands/test-upgrade.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// @ts-check
22
/* eslint-env node */
3+
import { makeWalletUtils } from '@agoric/client-utils';
34
import { Fail } from '@endo/errors';
45
import { CommanderError } from 'commander';
56
import { normalizeAddressWithOptions } from '../lib/chain.js';
7+
import { getNetworkConfig } from '../lib/network-config.js';
8+
import { sendAction } from '../lib/wallet.js';
69
import { bigintReplacer } from '../lib/format.js';
7-
import { getNetworkConfig } from '../lib/rpc.js';
8-
import { makeWalletUtils, sendAction } from '../lib/wallet.js';
910

1011
/**
1112
* Make commands for testing.
@@ -38,8 +39,8 @@ export const makeTestCommand = (
3839
try {
3940
// XXX pass fetch to getNetworkConfig() explicitly
4041
// await null above makes this await safe
41-
const networkConfig = await getNetworkConfig(env);
42-
return makeWalletUtils({ fetch, execFileSync, delay }, networkConfig);
42+
const networkConfig = await getNetworkConfig({ env, fetch });
43+
return makeWalletUtils({ fetch, delay }, networkConfig);
4344
} catch (err) {
4445
// CommanderError is a class constructor, and so
4546
// must be invoked with `new`.

0 commit comments

Comments
 (0)