diff --git a/a3p-integration/proposals/s:stake-bld/package.json b/a3p-integration/proposals/s:stake-bld/package.json index 2c0a5093c38..2432417370d 100644 --- a/a3p-integration/proposals/s:stake-bld/package.json +++ b/a3p-integration/proposals/s:stake-bld/package.json @@ -26,7 +26,8 @@ }, "packageManager": "yarn@4.5.3", "devDependencies": { - "@types/node": "^22.0.0" + "@types/node": "^22.0.0", + "typescript": "^5.7.2" }, "resolutions": { "@agoric/cosmos": "portal:../../agoric-sdk/golang/cosmos", diff --git a/a3p-integration/proposals/s:stake-bld/stakeBld.test.js b/a3p-integration/proposals/s:stake-bld/stakeBld.test.js index 549e4562545..13836f6cf3d 100644 --- a/a3p-integration/proposals/s:stake-bld/stakeBld.test.js +++ b/a3p-integration/proposals/s:stake-bld/stakeBld.test.js @@ -8,7 +8,7 @@ import { GOV1ADDR } from '@agoric/synthetic-chain'; import { Tendermint34Client } from '@cosmjs/tendermint-rpc'; import assert from 'node:assert'; import process from 'node:process'; -import { networkConfig, walletUtils } from './test-lib/index.js'; +import { networkConfig, agdWalletUtils } from './test-lib/index.js'; // XXX not the same as VALIDATOR_ADDRESS, which is actually the delegator const VALIDATOR_ADDRESS = process.env.VALIDATOR_ADDRESS; @@ -26,14 +26,15 @@ const currentDelegation = async () => { test('basic', async t => { assert(GOV1ADDR); - const { brand } = walletUtils.agoricNames; + const { brand } = agdWalletUtils.agoricNames; t.is((await currentDelegation()).length, 1, 'just the initial delegation'); /** @type {import('@agoric/ertp').Brand} */ + // @ts-expect-error actually a BoardRemote const BLDBrand = brand.BLD; - await walletUtils.broadcastBridgeAction(GOV1ADDR, { + await agdWalletUtils.broadcastBridgeAction(GOV1ADDR, { method: 'executeOffer', offer: { id: 'request-stake', @@ -50,7 +51,7 @@ test('basic', async t => { }, }); - await walletUtils.broadcastBridgeAction(GOV1ADDR, { + await agdWalletUtils.broadcastBridgeAction(GOV1ADDR, { method: 'executeOffer', offer: { id: 'request-delegate-6', @@ -75,7 +76,7 @@ test('basic', async t => { // omit 'delegation' because it has 'delegatorAddress' which is different every test run }); - await walletUtils.broadcastBridgeAction(GOV1ADDR, { + await agdWalletUtils.broadcastBridgeAction(GOV1ADDR, { method: 'executeOffer', offer: { id: 'request-undelegate', diff --git a/a3p-integration/proposals/s:stake-bld/test-lib/index.js b/a3p-integration/proposals/s:stake-bld/test-lib/index.js index 9c22b218e19..b56da4836cc 100644 --- a/a3p-integration/proposals/s:stake-bld/test-lib/index.js +++ b/a3p-integration/proposals/s:stake-bld/test-lib/index.js @@ -1,9 +1,9 @@ /* eslint-env node */ +import { makeSmartWalletKit, LOCAL_CONFIG } from '@agoric/client-utils'; import { execFileSync } from 'child_process'; -import { LOCAL_CONFIG as networkConfig } from '@agoric/client-utils'; -import { makeWalletUtils } from './wallet.js'; +import { makeAgdWalletKit } from './wallet.js'; -export { networkConfig }; +export const networkConfig = LOCAL_CONFIG; /** * Resolve after a delay in milliseconds. @@ -13,7 +13,12 @@ export { networkConfig }; */ const delay = ms => new Promise(resolve => setTimeout(() => resolve(), ms)); -export const walletUtils = await makeWalletUtils( - { execFileSync, delay, fetch }, +export const smartWalletKit = await makeSmartWalletKit( + { delay, fetch }, + networkConfig, +); + +export const agdWalletUtils = await makeAgdWalletKit( + { execFileSync, smartWalletKit, delay }, networkConfig, ); diff --git a/a3p-integration/proposals/s:stake-bld/test-lib/wallet.js b/a3p-integration/proposals/s:stake-bld/test-lib/wallet.js index e3ef23af929..fa8e8ca5112 100644 --- a/a3p-integration/proposals/s:stake-bld/test-lib/wallet.js +++ b/a3p-integration/proposals/s:stake-bld/test-lib/wallet.js @@ -1,16 +1,21 @@ // @ts-check -import { makeVstorageKit } from '@agoric/client-utils'; import { sendAction } from 'agoric/src/lib/index.js'; import { inspect } from 'util'; -export const makeWalletUtils = async ( - { delay, execFileSync, fetch }, +/** + * Stop-gap using execFileSync until we have a pure JS signing client. + * + * @param {object} root0 + * @param {import('child_process')['execFileSync']} root0.execFileSync + * @param {import('@agoric/client-utils').SmartWalletKit} root0.smartWalletKit + * @param {any} root0.delay + * @param {import('@agoric/client-utils').MinimalNetworkConfig} networkConfig + */ +export const makeAgdWalletKit = async ( + { execFileSync, smartWalletKit, delay }, networkConfig, ) => { - const { agoricNames, fromBoard, marshaller, readLatestHead, vstorage } = - await makeVstorageKit({ fetch }, networkConfig); - /** * * @param {string} from @@ -23,17 +28,12 @@ export const makeWalletUtils = async ( delay, execFileSync, from, - marshaller, keyring: { backend: 'test' }, }); }; return { - agoricNames, + ...smartWalletKit, broadcastBridgeAction, - fromBoard, - networkConfig, - readLatestHead, - vstorage, }; }; diff --git a/a3p-integration/proposals/s:stake-bld/yarn.lock b/a3p-integration/proposals/s:stake-bld/yarn.lock index a406cffd13a..cad8d9ce435 100644 --- a/a3p-integration/proposals/s:stake-bld/yarn.lock +++ b/a3p-integration/proposals/s:stake-bld/yarn.lock @@ -4617,6 +4617,7 @@ __metadata: agoric: "npm:dev" ava: "npm:^5.3.1" execa: "npm:^8.0.1" + typescript: "npm:^5.7.2" languageName: unknown linkType: soft @@ -5189,6 +5190,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5.7.2": + version: 5.7.2 + resolution: "typescript@npm:5.7.2" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/a873118b5201b2ef332127ef5c63fb9d9c155e6fdbe211cbd9d8e65877283797cca76546bad742eea36ed7efbe3424a30376818f79c7318512064e8625d61622 + languageName: node + linkType: hard + "typescript@patch:typescript@npm%3A5.1.6 - 5.6.x#optional!builtin": version: 5.6.3 resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=8c6c40" @@ -5199,6 +5210,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@npm%3A^5.7.2#optional!builtin": + version: 5.7.2 + resolution: "typescript@patch:typescript@npm%3A5.7.2#optional!builtin::version=5.7.2&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/f3b8082c9d1d1629a215245c9087df56cb784f9fb6f27b5d55577a20e68afe2a889c040aacff6d27e35be165ecf9dca66e694c42eb9a50b3b2c451b36b5675cb + languageName: node + linkType: hard + "undici-types@npm:~5.26.4": version: 5.26.5 resolution: "undici-types@npm:5.26.5" diff --git a/a3p-integration/proposals/z:acceptance/psm.test.js b/a3p-integration/proposals/z:acceptance/psm.test.js index 8496cc1d3b3..ae176ec51c8 100644 --- a/a3p-integration/proposals/z:acceptance/psm.test.js +++ b/a3p-integration/proposals/z:acceptance/psm.test.js @@ -21,7 +21,7 @@ import { } from '@agoric/synthetic-chain'; import { waitUntilAccountFunded } from '@agoric/client-utils'; import test from 'ava'; -import { NonNullish } from './test-lib/errors.js'; +import { NonNullish } from '@agoric/internal/src/errors.js'; import { adjustBalancesIfNotProvisioned, bankSend, diff --git a/a3p-integration/proposals/z:acceptance/test-lib/chain.js b/a3p-integration/proposals/z:acceptance/test-lib/chain.js deleted file mode 100644 index 74cc0d3c14f..00000000000 --- a/a3p-integration/proposals/z:acceptance/test-lib/chain.js +++ /dev/null @@ -1,140 +0,0 @@ -/** @file copied from packages/agoric-cli */ -// TODO DRY in https://github.com/Agoric/agoric-sdk/issues/9109 -// @ts-check -/* global process */ - -const agdBinary = 'agd'; - -/** - * @param {ReadonlyArray} swingsetArgs - * @param {import('./rpc.js').MinimalNetworkConfig & { - * from: string, - * fees?: string, - * dryRun?: boolean, - * verbose?: boolean, - * keyring?: {home?: string, backend: string} - * stdout?: Pick - * execFileSync: typeof import('child_process').execFileSync - * }} opts - */ -export const execSwingsetTransaction = (swingsetArgs, opts) => { - const { - from, - fees, - dryRun = false, - verbose = true, - keyring = undefined, - chainName, - rpcAddrs, - stdout = process.stdout, - execFileSync, - } = opts; - const homeOpt = keyring?.home ? [`--home=${keyring.home}`] : []; - const backendOpt = keyring?.backend - ? [`--keyring-backend=${keyring.backend}`] - : []; - const feeOpt = fees ? ['--fees', fees] : []; - const cmd = [`--node=${rpcAddrs[0]}`, `--chain-id=${chainName}`].concat( - homeOpt, - backendOpt, - feeOpt, - [`--from=${from}`, 'tx', 'swingset'], - swingsetArgs, - ); - - if (dryRun) { - stdout.write(`Run this interactive command in shell:\n\n`); - stdout.write(`${agdBinary} `); - stdout.write(cmd.join(' ')); - stdout.write('\n'); - } else { - const yesCmd = cmd.concat(['--yes']); - if (verbose) console.log('Executing ', agdBinary, yesCmd); - const out = execFileSync(agdBinary, yesCmd, { encoding: 'utf-8' }); - - // agd puts this diagnostic on stdout rather than stderr :-/ - // "Default sign-mode 'direct' not supported by Ledger, using sign-mode 'amino-json'. - if (out.startsWith('Default sign-mode')) { - const stripDiagnostic = out.replace(/^Default[^\n]+\n/, ''); - return stripDiagnostic; - } - return out; - } -}; -harden(execSwingsetTransaction); - -/** - * @param {import('./rpc.js').MinimalNetworkConfig & { - * execFileSync: typeof import('child_process').execFileSync, - * delay: (ms: number) => Promise, - * period?: number, - * retryMessage?: string, - * }} opts - * @returns {(l: (b: { time: string, height: string }) => Promise) => Promise} - */ -export const pollBlocks = opts => async lookup => { - const { execFileSync, delay, rpcAddrs, period = 3 * 1000 } = opts; - assert(execFileSync, 'missing execFileSync'); - const { retryMessage } = opts; - - const nodeArgs = [`--node=${rpcAddrs[0]}`]; - - await null; // separate sync prologue - - for (;;) { - const sTxt = execFileSync(agdBinary, ['status', ...nodeArgs]); - const status = JSON.parse(sTxt.toString()); - const { - SyncInfo: { latest_block_time: time, latest_block_height: height }, - } = status; - try { - // see await null above - const result = await lookup({ time, height }); - return result; - } catch (_err) { - console.error( - time, - retryMessage || 'not in block', - height, - 'retrying...', - ); - await delay(period); - } - } -}; - -/** - * @param {string} txhash - * @param {import('./rpc.js').MinimalNetworkConfig & { - * execFileSync: typeof import('child_process').execFileSync, - * delay: (ms: number) => Promise, - * period?: number, - * }} opts - */ -export const pollTx = async (txhash, opts) => { - const { execFileSync, rpcAddrs, chainName } = opts; - assert(execFileSync, 'missing execFileSync in pollTx'); - - const nodeArgs = [`--node=${rpcAddrs[0]}`]; - const outJson = ['--output', 'json']; - - const lookup = async () => { - const out = execFileSync( - agdBinary, - [ - 'query', - 'tx', - txhash, - `--chain-id=${chainName}`, - ...nodeArgs, - ...outJson, - ], - { stdio: ['ignore', 'pipe', 'ignore'] }, - ); - // XXX this type is defined in a .proto file somewhere - /** @type {{ height: string, txhash: string, code: number, timestamp: string }} */ - const info = JSON.parse(out.toString()); - return info; - }; - return pollBlocks({ ...opts, retryMessage: 'tx not in block' })(lookup); -}; diff --git a/a3p-integration/proposals/z:acceptance/test-lib/errors.js b/a3p-integration/proposals/z:acceptance/test-lib/errors.js deleted file mode 100644 index 57dc771e6a5..00000000000 --- a/a3p-integration/proposals/z:acceptance/test-lib/errors.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @file Copied from "@agoric/internal" - */ - -import { q } from '@endo/errors'; - -/** - * @template T - * @param {T | null | undefined} val - * @param {string} [optDetails] - * @returns {T} - */ -export const NonNullish = (val, optDetails = `unexpected ${q(val)}`) => { - if (val != null) { - // This `!= null` idiom checks that `val` is neither `null` nor `undefined`. - return val; - } - assert.fail(optDetails); -}; -harden(NonNullish); diff --git a/a3p-integration/proposals/z:acceptance/test-lib/index.js b/a3p-integration/proposals/z:acceptance/test-lib/index.js index 479d81503fc..b56da4836cc 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/index.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/index.js @@ -1,12 +1,9 @@ /* eslint-env node */ -import { makeWalletUtils } from '@agoric/client-utils'; +import { makeSmartWalletKit, LOCAL_CONFIG } from '@agoric/client-utils'; import { execFileSync } from 'child_process'; -import { makeAgdWalletUtils } from './wallet.js'; +import { makeAgdWalletKit } from './wallet.js'; -export const networkConfig = { - rpcAddrs: ['http://0.0.0.0:26657'], - chainName: 'agoriclocal', -}; +export const networkConfig = LOCAL_CONFIG; /** * Resolve after a delay in milliseconds. @@ -16,12 +13,12 @@ export const networkConfig = { */ const delay = ms => new Promise(resolve => setTimeout(() => resolve(), ms)); -export const walletUtils = await makeWalletUtils( +export const smartWalletKit = await makeSmartWalletKit( { delay, fetch }, networkConfig, ); -export const agdWalletUtils = await makeAgdWalletUtils( - { execFileSync, setTimeout, walletUtils }, +export const agdWalletUtils = await makeAgdWalletKit( + { execFileSync, smartWalletKit, delay }, networkConfig, ); diff --git a/a3p-integration/proposals/z:acceptance/test-lib/kread.js b/a3p-integration/proposals/z:acceptance/test-lib/kread.js index 01cd0d29b5f..1bb58b3eef1 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/kread.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/kread.js @@ -1,4 +1,5 @@ // @ts-nocheck FIXME +// XXX uses agoric.follow to read data through spawned processes; replace with VstorageKit import assert from 'node:assert'; import { diff --git a/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js b/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js index 7b5beceb1d1..74763e6f401 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js @@ -26,7 +26,7 @@ import { VALIDATORADDR, } from '@agoric/synthetic-chain'; import fsp from 'node:fs/promises'; -import { NonNullish } from './errors.js'; +import { NonNullish } from '@agoric/internal/src/errors.js'; import { getBalances } from './utils.js'; /** @import {Result as ExecaResult, ExecaError} from 'execa'; */ diff --git a/a3p-integration/proposals/z:acceptance/test-lib/rpc.js b/a3p-integration/proposals/z:acceptance/test-lib/rpc.js deleted file mode 100644 index 2a25b534911..00000000000 --- a/a3p-integration/proposals/z:acceptance/test-lib/rpc.js +++ /dev/null @@ -1,272 +0,0 @@ -/** @file copied from packages/agoric-cli */ -// TODO DRY in https://github.com/Agoric/agoric-sdk/issues/9109 -// @ts-check -/* global Buffer */ - -import { - boardSlottingMarshaller, - makeBoardRemote, -} from '@agoric/internal/src/marshal.js'; -import { Fail } from '@endo/errors'; - -export { boardSlottingMarshaller }; - -/** @type {(val: any) => string} */ -export const boardValToSlot = val => { - if ('getBoardId' in val) { - return val.getBoardId(); - } - throw Fail`unknown obj in boardSlottingMarshaller.valToSlot ${val}`; -}; - -/** @param {string} agoricNetSubdomain */ -export const networkConfigUrl = agoricNetSubdomain => - `https://${agoricNetSubdomain}.agoric.net/network-config`; -/** @param {string} agoricNetSubdomain */ -export const rpcUrl = agoricNetSubdomain => - `https://${agoricNetSubdomain}.rpc.agoric.net:443`; - -/** - * @typedef {{ rpcAddrs: string[], chainName: string }} MinimalNetworkConfig - */ - -/** @type {MinimalNetworkConfig} */ -const networkConfig = { - rpcAddrs: ['http://0.0.0.0:26657'], - chainName: 'agoriclocal', -}; -export { networkConfig }; -// console.warn('networkConfig', networkConfig); - -/** - * @param {object} powers - * @param {typeof window.fetch} powers.fetch - * @param {MinimalNetworkConfig} config - */ -export const makeVStorage = ({ fetch }, config = networkConfig) => { - /** @param {string} path */ - const getJSON = path => { - const url = config.rpcAddrs[0] + path; - // console.warn('fetching', url); - return fetch(url, { keepalive: true }).then(res => res.json()); - }; - // height=0 is the same as omitting height and implies the highest block - const url = (path = 'published', { kind = 'children', height = 0 } = {}) => - `/abci_query?path=%22/custom/vstorage/${kind}/${path}%22&height=${height}`; - - const readStorage = (path = 'published', { kind = 'children', height = 0 }) => - getJSON(url(path, { kind, height })) - .catch(err => { - throw Error(`cannot read ${kind} of ${path}: ${err.message}`); - }) - .then(data => { - const { - result: { response }, - } = data; - if (response?.code !== 0) { - /** @type {any} */ - const err = Error( - `error code ${response?.code} reading ${kind} of ${path}: ${response.log}`, - ); - err.code = response?.code; - err.codespace = response?.codespace; - throw err; - } - return data; - }); - - return { - url, - /** @param {{ result: { response: { code: number, value: string } } }} rawResponse */ - decode({ result: { response } }) { - const { code } = response; - if (code !== 0) { - throw response; - } - const { value } = response; - return Buffer.from(value, 'base64').toString(); - }, - /** - * - * @param {string} path - * @returns {Promise} latest vstorage value at path - */ - async readLatest(path = 'published') { - const raw = await readStorage(path, { kind: 'data' }); - return this.decode(raw); - }, - async keys(path = 'published') { - const raw = await readStorage(path, { kind: 'children' }); - return JSON.parse(this.decode(raw)).children; - }, - /** - * @param {string} path - * @param {number} [height] default is highest - * @returns {Promise<{blockHeight: number, values: string[]}>} - */ - async readAt(path, height = undefined) { - const raw = await readStorage(path, { kind: 'data', height }); - const txt = this.decode(raw); - /** @type {{ value: string }} */ - const { value } = JSON.parse(txt); - return JSON.parse(value); - }, - /** - * Read values going back as far as available - * - * @param {string} path - * @param {number | string} [minHeight] - * @returns {Promise} - */ - async readFully(path, minHeight = undefined) { - const parts = []; - // undefined the first iteration, to query at the highest - let blockHeight; - await null; - do { - // console.debug('READING', { blockHeight }); - let values; - try { - ({ blockHeight, values } = await this.readAt( - path, - blockHeight && Number(blockHeight) - 1, - )); - // console.debug('readAt returned', { blockHeight }); - } catch (err) { - if ( - // CosmosSDK ErrNotFound; there is no data at the path - (err.codespace === 'sdk' && err.code === 38) || - // CosmosSDK ErrUnknownRequest; misrepresentation of the same until - // https://github.com/Agoric/agoric-sdk/commit/dafc7c1708977aaa55e245dc09a73859cf1df192 - // TODO remove after upgrade-12 - err.message.match(/unknown request/) - ) { - // console.error(err); - break; - } - throw err; - } - parts.push(values); - // console.debug('PUSHED', values); - // console.debug('NEW', { blockHeight, minHeight }); - if (minHeight && Number(blockHeight) <= Number(minHeight)) break; - } while (blockHeight > 0); - return parts.flat(); - }, - }; -}; -/** @typedef {ReturnType} VStorage */ - -export const makeFromBoard = () => { - const cache = new Map(); - /** @type {(boardId: string, iface?: string) => ReturnType} */ - const convertSlotToVal = (boardId, iface) => { - if (cache.has(boardId)) { - return cache.get(boardId); - } - const val = makeBoardRemote({ boardId, iface }); - cache.set(boardId, val); - return val; - }; - return harden({ convertSlotToVal }); -}; -/** @typedef {ReturnType} IdMap */ - -export const storageHelper = { - /** @param { string } txt */ - parseCapData: txt => { - assert(typeof txt === 'string', typeof txt); - /** @type {{ value: string }} */ - const { value } = JSON.parse(txt); - const specimen = JSON.parse(value); - const { blockHeight, values } = specimen; - assert(values, `empty values in specimen ${value}`); - const capDatas = storageHelper.parseMany(values); - return { blockHeight, capDatas }; - }, - /** - * @param {string} txt - * @param {IdMap} ctx - */ - unserializeTxt: (txt, ctx) => { - const { capDatas } = storageHelper.parseCapData(txt); - return capDatas.map(capData => - boardSlottingMarshaller(ctx.convertSlotToVal).fromCapData(capData), - ); - }, - /** @param {string[]} capDataStrings array of stringified capData */ - parseMany: capDataStrings => { - assert(capDataStrings && capDataStrings.length); - /** @type {{ body: string, slots: string[] }[]} */ - const capDatas = capDataStrings.map(s => JSON.parse(s)); - for (const capData of capDatas) { - assert(typeof capData === 'object' && capData !== null); - assert('body' in capData && 'slots' in capData); - assert(typeof capData.body === 'string'); - assert(Array.isArray(capData.slots)); - } - return capDatas; - }, -}; -harden(storageHelper); - -/** - * @param {IdMap} ctx - * @param {VStorage} vstorage - * @returns {Promise} - */ -export const makeAgoricNames = async (ctx, vstorage) => { - /** @type {Record} */ - const reverse = {}; - const entries = await Promise.all( - ['brand', 'instance', 'vbankAsset'].map(async kind => { - const content = await vstorage.readLatest( - `published.agoricNames.${kind}`, - ); - /** @type {Array<[string, import('@agoric/vats/tools/board-utils.js').BoardRemote]>} */ - const parts = storageHelper.unserializeTxt(content, ctx).at(-1); - for (const [name, remote] of parts) { - if ('getBoardId' in remote) { - reverse[/** @type {string} */ (remote.getBoardId())] = name; - } - } - return [kind, Object.fromEntries(parts)]; - }), - ); - return { ...Object.fromEntries(entries), reverse }; -}; - -/** - * @param {{ fetch: typeof window.fetch }} io - * @param {MinimalNetworkConfig} config - */ -export const makeVstorageKit = async ({ fetch }, config = networkConfig) => { - await null; - try { - const vstorage = makeVStorage({ fetch }, config); - const fromBoard = makeFromBoard(); - const agoricNames = await makeAgoricNames(fromBoard, vstorage); - - const marshaller = boardSlottingMarshaller(fromBoard.convertSlotToVal); - - /** @type {(txt: string) => unknown} */ - const unserializeHead = txt => - storageHelper.unserializeTxt(txt, fromBoard).at(-1); - - /** @type {(path: string) => Promise} */ - const readLatestHead = path => - vstorage.readLatest(path).then(unserializeHead); - - return { - agoricNames, - fromBoard, - marshaller, - readLatestHead, - unserializeHead, - vstorage, - }; - } catch (err) { - throw Error(`RPC failure (${config.rpcAddrs}): ${err.message}`); - } -}; -/** @typedef {Awaited>} RpcUtils */ diff --git a/a3p-integration/proposals/z:acceptance/test-lib/utils.js b/a3p-integration/proposals/z:acceptance/test-lib/utils.js index 330fb799c6d..0826daa3660 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/utils.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/utils.js @@ -1,10 +1,13 @@ /* eslint-env node */ -import { makeStargateClient, makeVstorageKit } from '@agoric/client-utils'; +import { + LOCAL_CONFIG, + makeStargateClient, + makeVstorageKit, +} from '@agoric/client-utils'; import { readFile, writeFile } from 'node:fs/promises'; -import { networkConfig } from './rpc.js'; -export const stargateClientP = makeStargateClient(networkConfig, { fetch }); -export const vstorageKitP = makeVstorageKit({ fetch }, networkConfig); +export const stargateClientP = makeStargateClient(LOCAL_CONFIG, { fetch }); +export const vstorageKit = makeVstorageKit({ fetch }, LOCAL_CONFIG); /** * @import {WalletUtils} from '@agoric/client-utils'; diff --git a/a3p-integration/proposals/z:acceptance/test-lib/vaults.js b/a3p-integration/proposals/z:acceptance/test-lib/vaults.js index 88a07564ca9..1dabb741e95 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/vaults.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/vaults.js @@ -1,10 +1,6 @@ /* eslint-env node */ -import { - boardSlottingMarshaller, - makeFromBoard, - retryUntilCondition, -} from '@agoric/client-utils'; +import { makeAgoricNames, retryUntilCondition } from '@agoric/client-utils'; import { AmountMath } from '@agoric/ertp'; import { agops, @@ -18,19 +14,15 @@ import { ceilMultiplyBy, makeRatio, } from '@agoric/zoe/src/contractSupport/ratio.js'; -import { E } from '@endo/far'; -import { walletUtils } from './index.js'; -import { listVaults, vstorageKitP } from './utils.js'; - -const fromBoard = makeFromBoard(); -const marshaller = boardSlottingMarshaller(fromBoard.convertSlotToVal); +import { smartWalletKit } from './index.js'; +import { listVaults, vstorageKit } from './utils.js'; /** * @param {string} address * @returns {Promise<{ vaultID: string, debt: bigint, collateral: bigint, state: string }>} */ export const getLastVaultFromAddress = async address => { - const activeVaults = await listVaults(address, walletUtils); + const activeVaults = await listVaults(address, smartWalletKit); const vaultPath = activeVaults[activeVaults.length - 1]; const vaultID = vaultPath.split('.').pop(); @@ -97,7 +89,10 @@ export const getMinInitialDebt = async () => { * @returns {Promise<{ mintFee: import('@agoric/ertp/src/types.js').NatAmount, adjustedToMintAmount: import('@agoric/ertp/src/types.js').NatAmount }>} */ export const calculateMintFee = async (toMintValue, vaultManager) => { - const { brand } = await E.get(vstorageKitP).agoricNames; + const { brand } = await makeAgoricNames( + vstorageKit.fromBoard, + vstorageKit.vstorage, + ); /** @type {import('@agoric/ertp').Brand} */ // @ts-expect-error let this BoardRemote masquerade as a Brand const ISTBrand = brand.IST; @@ -157,11 +152,16 @@ const paramChangeOfferGeneration = async ( ) => { const ISTunit = 1_000_000n; // aka displayInfo: { decimalPlaces: 6 } - const { brand } = await E.get(vstorageKitP).agoricNames; + const agoricNames = await makeAgoricNames( + vstorageKit.fromBoard, + vstorageKit.vstorage, + ); + + const { brand } = agoricNames; assert(brand.IST); assert(brand.ATOM); - const { instance } = await E.get(vstorageKitP).agoricNames; + const { instance } = agoricNames; assert(instance.VaultFactory); const voteDurSec = BigInt(voteDur); @@ -203,7 +203,7 @@ const paramChangeOfferGeneration = async ( }; // @ts-expect-error tolerate BoardRemote instances with getBoardId methods - return JSON.stringify(marshaller.toCapData(harden(body))); + return JSON.stringify(vstorageKit.marshaller.toCapData(harden(body))); }; /** diff --git a/a3p-integration/proposals/z:acceptance/test-lib/wallet.js b/a3p-integration/proposals/z:acceptance/test-lib/wallet.js index 11813654bbc..fa8e8ca5112 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/wallet.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/wallet.js @@ -1,74 +1,21 @@ -// TODO DRY in https://github.com/Agoric/agoric-sdk/issues/9109 // @ts-check -/* global */ +import { sendAction } from 'agoric/src/lib/index.js'; import { inspect } from 'util'; -import { execSwingsetTransaction, pollTx } from './chain.js'; -import { makeTimerUtils } from './utils.js'; - -/** - * Sign and broadcast a wallet-action. - * - * @throws { Error & { code: number } } if transaction fails - * @param {import('@agoric/smart-wallet/src/smartWallet.js').BridgeAction} bridgeAction - * @param {import('./rpc.js').MinimalNetworkConfig & { - * from: string, - * marshaller: Pick, 'toCapData'>, - * fees?: string, - * verbose?: boolean, - * keyring?: {home?: string, backend: string}, - * stdout?: Pick, - * execFileSync: typeof import('child_process').execFileSync, - * delay: (ms: number) => Promise, - * dryRun?: boolean, - * }} opts - */ -export const sendAction = async (bridgeAction, opts) => { - const { marshaller } = opts; - // @ts-expect-error BridgeAction has methods disallowed by Passable - const offerBody = JSON.stringify(marshaller.toCapData(harden(bridgeAction))); - - // tryExit should not require --allow-spend - // https://github.com/Agoric/agoric-sdk/issues/7291 - const spendMethods = ['executeOffer', 'tryExitOffer']; - const spendArg = spendMethods.includes(bridgeAction.method) - ? ['--allow-spend'] - : []; - - const act = ['wallet-action', ...spendArg, offerBody]; - const out = execSwingsetTransaction([...act, '--output', 'json'], opts); - if (opts.dryRun) { - return; - } - - assert(out); // not dry run - const tx = JSON.parse(out); - if (tx.code !== 0) { - const err = Error(`failed to send tx: ${tx.raw_log} code: ${tx.code}`); - // @ts-expect-error XXX how to add properties to an error? - err.code = tx.code; - throw err; - } - - return pollTx(tx.txhash, opts); -}; /** * Stop-gap using execFileSync until we have a pure JS signing client. * * @param {object} root0 - * @param {import('@agoric/client-utils').WalletUtils} root0.walletUtils * @param {import('child_process')['execFileSync']} root0.execFileSync - * @param {typeof setTimeout} root0.setTimeout + * @param {import('@agoric/client-utils').SmartWalletKit} root0.smartWalletKit + * @param {any} root0.delay * @param {import('@agoric/client-utils').MinimalNetworkConfig} networkConfig */ -export const makeAgdWalletUtils = async ( - { execFileSync, walletUtils, setTimeout }, +export const makeAgdWalletKit = async ( + { execFileSync, smartWalletKit, delay }, networkConfig, ) => { - const { marshaller } = walletUtils; - - const { delay } = await makeTimerUtils({ setTimeout }); /** * * @param {string} from @@ -81,13 +28,12 @@ export const makeAgdWalletUtils = async ( delay, execFileSync, from, - // @ts-expect-error version skew in @endo/marshal and/or @endo/pass-style - marshaller, keyring: { backend: 'test' }, }); }; return { + ...smartWalletKit, broadcastBridgeAction, }; }; diff --git a/a3p-integration/proposals/z:acceptance/valueVow.test.js b/a3p-integration/proposals/z:acceptance/valueVow.test.js index 7e62e16420d..8490351eb58 100644 --- a/a3p-integration/proposals/z:acceptance/valueVow.test.js +++ b/a3p-integration/proposals/z:acceptance/valueVow.test.js @@ -10,7 +10,7 @@ import { GOV1ADDR as GETTER, // not particular to governance, just a handy wallet GOV2ADDR as SETTER, } from '@agoric/synthetic-chain'; -import { agdWalletUtils, walletUtils } from './test-lib/index.js'; +import { agdWalletUtils } from './test-lib/index.js'; const START_VALUEVOW_DIR = 'start-valueVow'; const RESTART_VALUEVOW_DIR = 'restart-valueVow'; @@ -37,7 +37,7 @@ test('vow survives restart', async t => { t.log('confirm the value is not in offer results'); let getterStatus = await retryUntilCondition( /** @type {() => Promise} */ - async () => walletUtils.readLatestHead(`published.wallet.${GETTER}`), + async () => agdWalletUtils.readLatestHead(`published.wallet.${GETTER}`), value => value.status.id === 'get-value' && value.updated === 'offerStatus', 'Offer get-value not succeeded', { @@ -79,7 +79,9 @@ test('vow survives restart', async t => { }); t.log('confirm the value is now in offer results'); - getterStatus = await walletUtils.readLatestHead(`published.wallet.${GETTER}`); + getterStatus = await agdWalletUtils.readLatestHead( + `published.wallet.${GETTER}`, + ); t.like(getterStatus, { status: { result: offerArgs.value } }); }); diff --git a/a3p-integration/proposals/z:acceptance/vaults.test.js b/a3p-integration/proposals/z:acceptance/vaults.test.js index 62086e7ad68..d99c8fe45e1 100644 --- a/a3p-integration/proposals/z:acceptance/vaults.test.js +++ b/a3p-integration/proposals/z:acceptance/vaults.test.js @@ -17,7 +17,7 @@ import { openVault, USER1ADDR, } from '@agoric/synthetic-chain'; -import { agdWalletUtils, walletUtils } from './test-lib/index.js'; +import { agdWalletUtils } from './test-lib/index.js'; import { getPriceFeedRoundId, verifyPushedPrice, @@ -55,7 +55,7 @@ const exec = { offerId = `openVault-${Date.now()}`, collateralBrandKey = 'ATOM', ) => { - const offer = Offers.vaults.OpenVault(walletUtils.agoricNames, { + const offer = Offers.vaults.OpenVault(agdWalletUtils.agoricNames, { giveCollateral, wantMinted, offerId, @@ -73,14 +73,14 @@ test.serial('open new vault', async t => { await bankSend(USER1ADDR, `20000000${ATOM_DENOM}`); const istBalanceBefore = await getISTBalance(USER1ADDR); - const activeVaultsBefore = await listVaults(USER1ADDR, walletUtils); + const activeVaultsBefore = await listVaults(USER1ADDR, agdWalletUtils); const mint = 5.0; const collateral = 10.0; await exec.vaults.OpenVault(USER1ADDR, mint, collateral); const istBalanceAfter = await getISTBalance(USER1ADDR); - const activeVaultsAfter = await listVaults(USER1ADDR, walletUtils); + const activeVaultsAfter = await listVaults(USER1ADDR, agdWalletUtils); await tryISTBalances( t, @@ -178,7 +178,7 @@ test.serial( 'user cannot open a vault under the minimum initial debt', async t => { await bankSend(GOV1ADDR, `200000000000000000${ATOM_DENOM}`); - const activeVaultsBefore = await listVaults(GOV1ADDR, walletUtils); + const activeVaultsBefore = await listVaults(GOV1ADDR, agdWalletUtils); const minInitialDebt = await getMinInitialDebt(); @@ -192,7 +192,7 @@ test.serial( }, ); - const activeVaultsAfter = await listVaults(GOV1ADDR, walletUtils); + const activeVaultsAfter = await listVaults(GOV1ADDR, agdWalletUtils); t.is( activeVaultsAfter.length, @@ -203,7 +203,7 @@ test.serial( ); test.serial('user cannot open a vault above debt limit', async t => { - const activeVaultsBefore = await listVaults(GOV1ADDR, walletUtils); + const activeVaultsBefore = await listVaults(GOV1ADDR, agdWalletUtils); const { availableDebtForMint } = await getAvailableDebtForMint(VAULT_MANAGER); @@ -217,7 +217,7 @@ test.serial('user cannot open a vault above debt limit', async t => { }, ); - const activeVaultsAfter = await listVaults(GOV1ADDR, walletUtils); + const activeVaultsAfter = await listVaults(GOV1ADDR, agdWalletUtils); t.is( activeVaultsAfter.length, @@ -228,7 +228,7 @@ test.serial('user cannot open a vault above debt limit', async t => { test.serial('user can open a vault under debt limit', async t => { const istBalanceBefore = await getISTBalance(GOV1ADDR); - const activeVaultsBefore = await listVaults(GOV1ADDR, walletUtils); + const activeVaultsBefore = await listVaults(GOV1ADDR, agdWalletUtils); const { availableDebtForMint } = await getAvailableDebtForMint(VAULT_MANAGER); @@ -238,7 +238,7 @@ test.serial('user can open a vault under debt limit', async t => { await openVault(GOV1ADDR, mint.toString(), collateral.toString()); const istBalanceAfter = await getISTBalance(GOV1ADDR); - const activeVaultsAfter = await listVaults(GOV1ADDR, walletUtils); + const activeVaultsAfter = await listVaults(GOV1ADDR, agdWalletUtils); await tryISTBalances( t, diff --git a/packages/agoric-cli/src/commands/auction.js b/packages/agoric-cli/src/commands/auction.js index ac9306ffb45..684cd4356a4 100644 --- a/packages/agoric-cli/src/commands/auction.js +++ b/packages/agoric-cli/src/commands/auction.js @@ -1,6 +1,10 @@ // @ts-check /* eslint-env node */ -import { fetchEnvNetworkConfig, makeVstorageKit } from '@agoric/client-utils'; +import { + fetchEnvNetworkConfig, + makeAgoricNames, + makeVstorageKit, +} from '@agoric/client-utils'; import { Fail } from '@endo/errors'; import { InvalidArgumentError } from 'commander'; import { outputActionAndHint } from '../lib/wallet.js'; @@ -88,10 +92,11 @@ export const makeAuctionCommand = ( * }} opts */ async opts => { - const { agoricNames, readPublished } = await makeVstorageKit( + const { readPublished, ...vsk } = makeVstorageKit( { fetch }, networkConfig, ); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const { current } = await readPublished(`auction.governance`); diff --git a/packages/agoric-cli/src/commands/gov.js b/packages/agoric-cli/src/commands/gov.js index be116b87514..007b6b1a2ea 100644 --- a/packages/agoric-cli/src/commands/gov.js +++ b/packages/agoric-cli/src/commands/gov.js @@ -1,7 +1,11 @@ // @ts-check /* eslint-disable func-names */ /* eslint-env node */ -import { fetchEnvNetworkConfig, makeVstorageKit } from '@agoric/client-utils'; +import { + fetchEnvNetworkConfig, + makeAgoricNames, + makeVstorageKit, +} from '@agoric/client-utils'; import { execFileSync as execFileSyncAmbient } from 'child_process'; import { Command, CommanderError } from 'commander'; import { normalizeAddressWithOptions, pollBlocks } from '../lib/chain.js'; @@ -14,8 +18,10 @@ import { } from '../lib/wallet.js'; /** - * @import {OfferSpec} from '@agoric/smart-wallet/src/offers.js' - * @import {QuestionDetails} from '@agoric/governance/src/types.js' + * @import {OfferSpec} from '@agoric/smart-wallet/src/offers.js'; + * @import {AgoricNamesRemotes} from '@agoric/vats/tools/board-utils.js'; + * @import {CurrentWalletRecord} from '@agoric/smart-wallet/src/smartWallet.js'; + * @import {VstorageKit} from '@agoric/client-utils'; */ const collectValues = (val, memo) => { @@ -85,19 +91,21 @@ export const makeGovCommand = (_logger, io = {}) => { * given a sendFrom address; else print it. * * @param {{ - * toOffer: (agoricNames: *, current: import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord | undefined) => OfferSpec, + * toOffer: (agoricNames: AgoricNamesRemotes, current: CurrentWalletRecord | undefined) => OfferSpec, * sendFrom?: string | undefined, * keyringBackend: string, * instanceName?: string, * }} detail - * @param {Awaited>} [optUtils] + * @param {VstorageKit} [vsk] */ const processOffer = async function ( { toOffer, sendFrom, keyringBackend }, - optUtils, + vsk, ) { - const utils = await (optUtils || makeVstorageKit({ fetch }, networkConfig)); - const { agoricNames, readPublished } = utils; + await null; + vsk ||= makeVstorageKit({ fetch }, networkConfig); + const { readPublished } = vsk; + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); assert(keyringBackend, 'missing keyring-backend option'); @@ -264,10 +272,11 @@ export const makeGovCommand = (_logger, io = {}) => { ) .requiredOption('--for ', 'description of the invitation') .action(async opts => { - const { agoricNames, readPublished } = await makeVstorageKit( + const { readPublished, ...vsk } = makeVstorageKit( { fetch }, networkConfig, ); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const current = await getCurrent(opts.from, { readPublished }); const known = findContinuingIds(current, agoricNames); @@ -293,10 +302,11 @@ export const makeGovCommand = (_logger, io = {}) => { normalizeAddress, ) .action(async opts => { - const { agoricNames, readPublished } = await makeVstorageKit( + const { readPublished, ...vsk } = makeVstorageKit( { fetch }, networkConfig, ); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const current = await getCurrent(opts.from, { readPublished }); const found = findContinuingIds(current, agoricNames); @@ -332,8 +342,8 @@ export const makeGovCommand = (_logger, io = {}) => { normalizeAddress, ) .action(async function (opts, options) { - const utils = await makeVstorageKit({ fetch }, networkConfig); - const { readPublished } = utils; + const vsk = makeVstorageKit({ fetch }, networkConfig); + const { readPublished } = vsk; const questionDesc = await readPublished( `committees.${opts.pathname}.latestQuestion`, @@ -383,7 +393,7 @@ export const makeGovCommand = (_logger, io = {}) => { sendFrom: opts.sendFrom, keyringBackend: options.optsWithGlobals().keyringBackend, }, - utils, + vsk, ); }); diff --git a/packages/agoric-cli/src/commands/oracle.js b/packages/agoric-cli/src/commands/oracle.js index 2882cef094a..e91d9967be7 100644 --- a/packages/agoric-cli/src/commands/oracle.js +++ b/packages/agoric-cli/src/commands/oracle.js @@ -3,6 +3,7 @@ /* eslint-env node */ import { fetchEnvNetworkConfig, + makeAgoricNames, makeVstorageKit, makeWalletUtils, storageHelper, @@ -90,19 +91,20 @@ export const makeOracleCommand = (logger, io = {}) => { env: process.env, fetch, }); - const utils = await makeVstorageKit({ fetch }, networkConfig); + const vsk = makeVstorageKit({ fetch }, networkConfig); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const lookupPriceAggregatorInstance = ([brandIn, brandOut]) => { const name = oracleBrandFeedName(brandIn, brandOut); - const instance = utils.agoricNames.instance[name]; + const instance = agoricNames.instance[name]; if (!instance) { - logger.debug('known instances:', utils.agoricNames.instance); + logger.debug('known instances:', agoricNames.instance); throw Error(`Unknown instance ${name}`); } return instance; }; - return { ...utils, networkConfig, lookupPriceAggregatorInstance }; + return { ...vsk, networkConfig, lookupPriceAggregatorInstance }; }; oracle diff --git a/packages/agoric-cli/src/commands/psm.js b/packages/agoric-cli/src/commands/psm.js index a59a8b81639..e800b5f1c16 100644 --- a/packages/agoric-cli/src/commands/psm.js +++ b/packages/agoric-cli/src/commands/psm.js @@ -3,6 +3,7 @@ /* eslint-env node */ import { fetchEnvNetworkConfig, + makeAgoricNames, makeVstorageKit, storageHelper, } from '@agoric/client-utils'; @@ -66,13 +67,14 @@ export const makePsmCommand = logger => { ); const rpcTools = async () => { - const utils = await makeVstorageKit({ fetch }, networkConfig); + const vsk = await makeVstorageKit({ fetch }, networkConfig); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const lookupPsmInstance = ([minted, anchor]) => { const name = `psm-${minted}-${anchor}`; - const instance = utils.agoricNames.instance[name]; + const instance = agoricNames.instance[name]; if (!instance) { - logger.debug('known instances:', utils.agoricNames.instance); + logger.debug('known instances:', agoricNames.instance); throw Error(`Unknown instance ${name}`); } return instance; @@ -83,20 +85,19 @@ export const makePsmCommand = logger => { * @param {[Minted: string, Anchor: string]} pair */ const getGovernanceState = async ([Minted, Anchor]) => { - const govContent = await utils.vstorage.readLatest( + const govContent = await vsk.vstorage.readLatest( `published.psm.${Minted}.${Anchor}.governance`, ); assert(govContent, 'no gov content'); const { current: governance } = last( - storageHelper.unserializeTxt(govContent, utils.fromBoard), + storageHelper.unserializeTxt(govContent, vsk.fromBoard), ); - const { [`psm.${Minted}.${Anchor}`]: instance } = - utils.agoricNames.instance; + const { [`psm.${Minted}.${Anchor}`]: instance } = agoricNames.instance; return { instance, governance }; }; - return { ...utils, lookupPsmInstance, getGovernanceState }; + return { ...vsk, agoricNames, lookupPsmInstance, getGovernanceState }; }; psm diff --git a/packages/agoric-cli/src/commands/reserve.js b/packages/agoric-cli/src/commands/reserve.js index eebb6c7d73d..9716590402e 100644 --- a/packages/agoric-cli/src/commands/reserve.js +++ b/packages/agoric-cli/src/commands/reserve.js @@ -1,7 +1,11 @@ // @ts-check /* eslint-disable func-names */ /* eslint-env node */ -import { fetchEnvNetworkConfig, makeVstorageKit } from '@agoric/client-utils'; +import { + fetchEnvNetworkConfig, + makeAgoricNames, + makeVstorageKit, +} from '@agoric/client-utils'; import { Offers } from '@agoric/inter-protocol/src/clientSupport.js'; import { Command } from 'commander'; import { outputActionAndHint } from '../lib/wallet.js'; @@ -31,7 +35,8 @@ export const makeReserveCommand = (_logger, io = {}) => { * }} opts */ async ({ collateralBrand, ...opts }) => { - const { agoricNames } = await makeVstorageKit({ fetch }, networkConfig); + const vsk = makeVstorageKit({ fetch }, networkConfig); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const offer = Offers.reserve.AddCollateral(agoricNames, { collateralBrandKey: collateralBrand, @@ -65,7 +70,8 @@ export const makeReserveCommand = (_logger, io = {}) => { 1, ) .action(async function (opts) { - const { agoricNames } = await makeVstorageKit({ fetch }, networkConfig); + const vsk = makeVstorageKit({ fetch }, networkConfig); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const reserveInstance = agoricNames.instance.reserve; assert(reserveInstance, 'missing reserve in names'); diff --git a/packages/agoric-cli/src/commands/vaults.js b/packages/agoric-cli/src/commands/vaults.js index 51032d89482..006831862e7 100644 --- a/packages/agoric-cli/src/commands/vaults.js +++ b/packages/agoric-cli/src/commands/vaults.js @@ -1,7 +1,11 @@ // @ts-check /* eslint-disable func-names */ /* eslint-env node */ -import { fetchEnvNetworkConfig, makeVstorageKit } from '@agoric/client-utils'; +import { + fetchEnvNetworkConfig, + makeAgoricNames, + makeVstorageKit, +} from '@agoric/client-utils'; import { lookupOfferIdForVault, Offers, @@ -63,7 +67,8 @@ export const makeVaultsCommand = logger => { .option('--collateralBrand ', 'Collateral brand key', 'ATOM') .action(async function (opts) { logger.warn('running with options', opts); - const { agoricNames } = await makeVstorageKit({ fetch }, networkConfig); + const vsk = makeVstorageKit({ fetch }, networkConfig); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const offer = Offers.vaults.OpenVault(agoricNames, { giveCollateral: opts.giveCollateral, @@ -98,10 +103,11 @@ export const makeVaultsCommand = logger => { .requiredOption('--vaultId ', 'Key of vault (e.g. vault1)') .action(async function (opts) { logger.warn('running with options', opts); - const { agoricNames, readPublished } = await makeVstorageKit( + const { readPublished, ...vsk } = makeVstorageKit( { fetch }, networkConfig, ); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const previousOfferId = await lookupOfferIdForVault( opts.vaultId, @@ -142,10 +148,11 @@ export const makeVaultsCommand = logger => { ) .action(async function (opts) { logger.warn('running with options', opts); - const { agoricNames, readPublished } = await makeVstorageKit( + const { readPublished, ...vsk } = makeVstorageKit( { fetch }, networkConfig, ); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const previousOfferId = await lookupOfferIdForVault( opts.vaultId, diff --git a/packages/agoric-cli/src/commands/wallet.js b/packages/agoric-cli/src/commands/wallet.js index e41258716d4..18d3d597c2b 100644 --- a/packages/agoric-cli/src/commands/wallet.js +++ b/packages/agoric-cli/src/commands/wallet.js @@ -8,7 +8,11 @@ import { makeLeader, makeLeaderFromRpcAddresses, } from '@agoric/casting'; -import { makeVstorageKit, fetchEnvNetworkConfig } from '@agoric/client-utils'; +import { + makeVstorageKit, + fetchEnvNetworkConfig, + makeAgoricNames, +} from '@agoric/client-utils'; import { execFileSync } from 'child_process'; import fs from 'fs'; import util from 'util'; @@ -214,13 +218,11 @@ export const makeWalletCommand = async command => { normalizeAddress, ) .action(async function (opts) { - const { agoricNames, unserializer, readPublished } = - await makeVstorageKit( - { - fetch, - }, - networkConfig, - ); + const { readPublished, unserializer, ...vsk } = makeVstorageKit( + { fetch }, + networkConfig, + ); + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); const leader = makeLeader(networkConfig.rpcAddrs[0]); const follower = await makeFollower( diff --git a/packages/agoric-cli/src/lib/wallet.js b/packages/agoric-cli/src/lib/wallet.js index e9eff2f2ca1..c4e66cf2437 100644 --- a/packages/agoric-cli/src/lib/wallet.js +++ b/packages/agoric-cli/src/lib/wallet.js @@ -149,7 +149,7 @@ export const coalesceWalletState = async (follower, invitationBrand) => { * fees?: string, * verbose?: boolean, * keyring?: {home?: string, backend: string}, - * stdout: Pick, + * stdout?: Pick, * execFileSync: typeof import('child_process').execFileSync, * delay: (ms: number) => Promise, * dryRun?: boolean, diff --git a/packages/client-utils/src/main.js b/packages/client-utils/src/main.js index fad3e63225e..decf0366b5b 100644 --- a/packages/client-utils/src/main.js +++ b/packages/client-utils/src/main.js @@ -1,6 +1,7 @@ export * from './cli.js'; export * from './network-config.js'; export * from './rpc.js'; +export * from './smart-wallet-kit.js'; export * from './sync-tools.js'; export * from './vstorage.js'; export * from './vstorage-kit.js'; diff --git a/packages/client-utils/src/smart-wallet-kit.js b/packages/client-utils/src/smart-wallet-kit.js new file mode 100644 index 00000000000..a570fe9855a --- /dev/null +++ b/packages/client-utils/src/smart-wallet-kit.js @@ -0,0 +1,113 @@ +import { makeWalletStateCoalescer } from '@agoric/smart-wallet/src/utils.js'; +import { pollBlocks } from './chain.js'; +import { makeStargateClient } from './rpc.js'; +import { makeAgoricNames, makeVstorageKit } from './vstorage-kit.js'; + +/** + * @import {Amount, Brand} from '@agoric/ertp/src/types.js' + * @import {CurrentWalletRecord, UpdateRecord} from '@agoric/smart-wallet/src/smartWallet.js'; + * @import {MinimalNetworkConfig} from './network-config.js'; + */ + +/** + * Augment VstorageKit with addtional convenience methods for working with + * Agoric smart wallets. + * + * @param {object} root0 + * @param {typeof globalThis.fetch} root0.fetch + * @param {(ms: number) => Promise} root0.delay + * @param {MinimalNetworkConfig} networkConfig + */ +export const makeSmartWalletKit = async ({ fetch, delay }, networkConfig) => { + const vsk = makeVstorageKit({ fetch }, networkConfig); + + const client = await makeStargateClient(networkConfig, { fetch }); + + const agoricNames = await makeAgoricNames(vsk.fromBoard, vsk.vstorage); + + /** + * @param {string} from + * @param {number|string} [minHeight] + */ + const storedWalletState = async (from, minHeight = undefined) => { + const history = await vsk.vstorage.readFully( + `published.wallet.${from}`, + minHeight, + ); + + /** @type {{ Invitation: Brand<'set'> }} */ + // @ts-expect-error XXX how to narrow AssetKind to set? + const { Invitation } = agoricNames.brand; + const coalescer = makeWalletStateCoalescer(Invitation); + // update with oldest first + for (const txt of history.reverse()) { + const { body, slots } = JSON.parse(txt); + const record = vsk.marshaller.fromCapData({ body, slots }); + coalescer.update(record); + } + const coalesced = coalescer.state; + harden(coalesced); + return coalesced; + }; + + /** + * Get OfferStatus by id, polling until available. + * + * @param {string} from + * @param {string|number} id + * @param {number|string} minHeight + * @param {boolean} [untilNumWantsSatisfied] + */ + const pollOffer = async ( + from, + id, + minHeight, + untilNumWantsSatisfied = false, + ) => { + const poll = pollBlocks({ + client, + delay, + retryMessage: 'offer not in wallet at block', + }); + + const lookup = async () => { + const { offerStatuses } = await storedWalletState(from, minHeight); + const offerStatus = [...offerStatuses.values()].find(s => s.id === id); + if (!offerStatus) throw Error('retry'); + harden(offerStatus); + if (untilNumWantsSatisfied && !('numWantsSatisfied' in offerStatus)) { + throw Error('retry (no numWantsSatisfied yet)'); + } + return offerStatus; + }; + return poll(lookup); + }; + + /** + * @param {string} addr + * @returns {Promise} + */ + const getLastUpdate = addr => { + return vsk.readPublished(`wallet.${addr}`); + }; + + /** + * @param {string} addr + * @returns {Promise} + */ + const getCurrentWalletRecord = addr => { + return vsk.readPublished(`wallet.${addr}.current`); + }; + + return { + // pass along all of VstorageKit + ...vsk, + agoricNames, + networkConfig, + getLastUpdate, + getCurrentWalletRecord, + storedWalletState, + pollOffer, + }; +}; +/** @typedef {Awaited>} SmartWalletKit */ diff --git a/packages/client-utils/src/vstorage-kit.js b/packages/client-utils/src/vstorage-kit.js index bca8d7b074e..6643d81fe63 100644 --- a/packages/client-utils/src/vstorage-kit.js +++ b/packages/client-utils/src/vstorage-kit.js @@ -2,6 +2,7 @@ import { boardSlottingMarshaller, makeBoardRemote, } from '@agoric/vats/tools/board-utils.js'; +import { assertAllDefined } from '@agoric/internal'; import { makeVStorage } from './vstorage.js'; export { boardSlottingMarshaller }; @@ -73,6 +74,7 @@ harden(storageHelper); * @returns {Promise} */ export const makeAgoricNames = async (ctx, vstorage) => { + assertAllDefined({ ctx, vstorage }); const reverse = {}; const entries = await Promise.all( ['brand', 'instance', 'vbankAsset'].map(async kind => { @@ -96,12 +98,10 @@ export const makeAgoricNames = async (ctx, vstorage) => { * @param {{ fetch: typeof window.fetch }} io * @param {MinimalNetworkConfig} config */ -export const makeVstorageKit = async ({ fetch }, config) => { - await null; +export const makeVstorageKit = ({ fetch }, config) => { try { const vstorage = makeVStorage({ fetch }, config); const fromBoard = makeFromBoard(); - const agoricNames = await makeAgoricNames(fromBoard, vstorage); const marshaller = boardSlottingMarshaller(fromBoard.convertSlotToVal); @@ -131,7 +131,6 @@ export const makeVstorageKit = async ({ fetch }, config) => { readLatestHead(`published.${subpath}`); return { - agoricNames, fromBoard, marshaller, readLatestHead, @@ -143,4 +142,4 @@ export const makeVstorageKit = async ({ fetch }, config) => { throw Error(`RPC failure (${config.rpcAddrs}): ${err.message}`); } }; -/** @typedef {Awaited>} VstorageKit */ +/** @typedef {ReturnType} VstorageKit */ diff --git a/packages/client-utils/src/wallet-utils.js b/packages/client-utils/src/wallet-utils.js index 2cd63a1498d..79f0d9edc96 100644 --- a/packages/client-utils/src/wallet-utils.js +++ b/packages/client-utils/src/wallet-utils.js @@ -1,113 +1,8 @@ -import { makeWalletStateCoalescer } from '@agoric/smart-wallet/src/utils.js'; -import { pollBlocks } from './chain.js'; -import { makeStargateClient } from './rpc.js'; -import { boardSlottingMarshaller, makeVstorageKit } from './vstorage-kit.js'; +/** @file backwards compat */ -/** - * @import {Amount, Brand} from '@agoric/ertp/src/types.js' - * @import {CurrentWalletRecord, UpdateRecord} from '@agoric/smart-wallet/src/smartWallet.js'; - * @import {MinimalNetworkConfig} from './network-config.js'; - */ +import { makeSmartWalletKit } from './smart-wallet-kit.js'; -// XXX this is really a SmartWalletKit -/** - * Augment VstorageKit with addtional convenience methods for working with - * Agoric smart wallets. - * - * @param {object} root0 - * @param {typeof globalThis.fetch} root0.fetch - * @param {(ms: number) => Promise} root0.delay - * @param {MinimalNetworkConfig} networkConfig - */ -export const makeWalletUtils = async ({ fetch, delay }, networkConfig) => { - const vsk = await makeVstorageKit({ fetch }, networkConfig); +/** @typedef {import('./smart-wallet-kit.js').SmartWalletKit} WalletUtils */ - const m = boardSlottingMarshaller(vsk.fromBoard.convertSlotToVal); - - const client = await makeStargateClient(networkConfig, { fetch }); - - /** - * @param {string} from - * @param {number|string} [minHeight] - */ - const storedWalletState = async (from, minHeight = undefined) => { - const history = await vsk.vstorage.readFully( - `published.wallet.${from}`, - minHeight, - ); - - /** @type {{ Invitation: Brand<'set'> }} */ - // @ts-expect-error XXX how to narrow AssetKind to set? - const { Invitation } = vsk.agoricNames.brand; - const coalescer = makeWalletStateCoalescer(Invitation); - // update with oldest first - for (const txt of history.reverse()) { - const { body, slots } = JSON.parse(txt); - const record = m.fromCapData({ body, slots }); - coalescer.update(record); - } - const coalesced = coalescer.state; - harden(coalesced); - return coalesced; - }; - - /** - * Get OfferStatus by id, polling until available. - * - * @param {string} from - * @param {string|number} id - * @param {number|string} minHeight - * @param {boolean} [untilNumWantsSatisfied] - */ - const pollOffer = async ( - from, - id, - minHeight, - untilNumWantsSatisfied = false, - ) => { - const poll = pollBlocks({ - client, - delay, - retryMessage: 'offer not in wallet at block', - }); - - const lookup = async () => { - const { offerStatuses } = await storedWalletState(from, minHeight); - const offerStatus = [...offerStatuses.values()].find(s => s.id === id); - if (!offerStatus) throw Error('retry'); - harden(offerStatus); - if (untilNumWantsSatisfied && !('numWantsSatisfied' in offerStatus)) { - throw Error('retry (no numWantsSatisfied yet)'); - } - return offerStatus; - }; - return poll(lookup); - }; - - /** - * @param {string} addr - * @returns {Promise} - */ - const getLastUpdate = addr => { - return vsk.readPublished(`wallet.${addr}`); - }; - - /** - * @param {string} addr - * @returns {Promise} - */ - const getCurrentWalletRecord = addr => { - return vsk.readPublished(`wallet.${addr}.current`); - }; - - return { - // pass along all of VstorageKit - ...vsk, - networkConfig, - getLastUpdate, - getCurrentWalletRecord, - storedWalletState, - pollOffer, - }; -}; -/** @typedef {Awaited>} WalletUtils */ +/** @deprecated use `makeSmartWalletKit` */ +export const makeWalletUtils = makeSmartWalletKit; diff --git a/packages/client-utils/test/snapshots/exports.test.js.md b/packages/client-utils/test/snapshots/exports.test.js.md index 22f45e43b82..1dc0f08ff90 100644 --- a/packages/client-utils/test/snapshots/exports.test.js.md +++ b/packages/client-utils/test/snapshots/exports.test.js.md @@ -16,6 +16,7 @@ Generated by [AVA](https://avajs.dev). 'fetchNetworkConfig', 'makeAgoricNames', 'makeFromBoard', + 'makeSmartWalletKit', 'makeStargateClient', 'makeTendermint34Client', 'makeVStorage', diff --git a/packages/client-utils/test/snapshots/exports.test.js.snap b/packages/client-utils/test/snapshots/exports.test.js.snap index c6c052973b5..f44f9a7329c 100644 Binary files a/packages/client-utils/test/snapshots/exports.test.js.snap and b/packages/client-utils/test/snapshots/exports.test.js.snap differ diff --git a/packages/client-utils/test/snapshots/vstorage-kit.test.js.md b/packages/client-utils/test/snapshots/vstorage-kit.test.js.md new file mode 100644 index 00000000000..fb788564f20 --- /dev/null +++ b/packages/client-utils/test/snapshots/vstorage-kit.test.js.md @@ -0,0 +1,352 @@ +# Snapshot report for `test/vstorage-kit.test.js` + +The actual snapshot is saved in `vstorage-kit.test.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## agoricNames contains expected structure + +> agoricNames from A3P + + { + brand: { + ATOM: Object @Alleged: BoardRemoteATOM brand { + getBoardId: Function getBoardId {}, + }, + BLD: Object @Alleged: BoardRemoteBLD brand { + getBoardId: Function getBoardId {}, + }, + DAI_axl: Object @Alleged: BoardRemoteDAI_axl brand { + getBoardId: Function getBoardId {}, + }, + DAI_grv: Object @Alleged: BoardRemoteDAI_grv brand { + getBoardId: Function getBoardId {}, + }, + IST: Object @Alleged: BoardRemoteIST brand { + getBoardId: Function getBoardId {}, + }, + Invitation: Object @Alleged: BoardRemoteZoe Invitation brand { + getBoardId: Function getBoardId {}, + }, + KREAdCHARACTER: Object @Alleged: BoardRemoteKREAdCHARACTER brand { + getBoardId: Function getBoardId {}, + }, + KREAdITEM: Object @Alleged: BoardRemoteKREAdITEM brand { + getBoardId: Function getBoardId {}, + }, + USDC_axl: Object @Alleged: BoardRemoteUSDC_axl brand { + getBoardId: Function getBoardId {}, + }, + USDC_grv: Object @Alleged: BoardRemoteUSDC_grv brand { + getBoardId: Function getBoardId {}, + }, + USDT_axl: Object @Alleged: BoardRemoteUSDT_axl brand { + getBoardId: Function getBoardId {}, + }, + USDT_grv: Object @Alleged: BoardRemoteUSDT_grv brand { + getBoardId: Function getBoardId {}, + }, + stATOM: Object @Alleged: BoardRemotestATOM brand { + getBoardId: Function getBoardId {}, + }, + timer: Object @Alleged: BoardRemotetimerBrand { + getBoardId: Function getBoardId {}, + }, + }, + instance: { + 'ATOM-USD price feed': Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + Crabble: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + CrabbleCommittee: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + CrabbleGovernor: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + VaultFactory: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + VaultFactoryGovernor: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + auctioneer: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + econCommitteeCharter: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + economicCommittee: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + feeDistributor: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + kread: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + kreadCommittee: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + kreadCommitteeCharter: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + provisionPool: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + 'psm-IST-DAI_axl': Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + 'psm-IST-DAI_grv': Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + 'psm-IST-USDC_axl': Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + 'psm-IST-USDC_grv': Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + 'psm-IST-USDT_axl': Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + 'psm-IST-USDT_grv': Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + reserve: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + reserveGovernor: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + 'scaledPriceAuthority-stATOM': Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + 'stATOM-USD price feed': Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + walletFactory: Object @Alleged: BoardRemoteInstanceHandle { + getBoardId: Function getBoardId {}, + }, + }, + reverse: { + board00282: 'KREAdITEM', + board00360: 'VaultFactory', + board0074: 'Invitation', + board00990: 'stATOM', + board01272: 'psm-IST-USDT_grv', + board01664: 'provisionPool', + board01744: 'USDT_axl', + board01867: 'psm-IST-DAI_axl', + board01985: 'kreadCommittee', + board02271: 'psm-IST-USDT_axl', + board02393: 'CrabbleCommittee', + board02568: 'psm-IST-DAI_grv', + board0257: 'IST', + board02963: 'ATOM-USD price feed', + board03040: 'USDC_axl', + board03138: 'DAI_grv', + board03281: 'KREAdCHARACTER', + board03365: 'reserveGovernor', + board03446: 'USDT_grv', + board03773: 'VaultFactoryGovernor', + board04091: 'stATOM-USD price feed', + board04149: 'economicCommittee', + board0425: 'timer', + board04395: 'Crabble', + board04542: 'USDC_grv', + board04661: 'econCommitteeCharter', + board04783: 'kread', + board05262: 'feeDistributor', + board05396: 'CrabbleGovernor', + board05557: 'ATOM', + board0566: 'BLD', + board05669: 'psm-IST-USDC_axl', + board05736: 'DAI_axl', + board05892: 'scaledPriceAuthority-stATOM', + board05970: 'psm-IST-USDC_grv', + board06284: 'kreadCommitteeCharter', + board06299: 'auctioneer', + board06366: 'walletFactory', + board06458: 'reserve', + }, + vbankAsset: { + 'ibc/295548A78785A1007F232DE286149A6FF512F180AF5657780FC89C009E2C348F': { + brand: Object @Alleged: BoardRemoteUSDC_axl brand { + getBoardId: Function getBoardId {}, + }, + denom: 'ibc/295548A78785A1007F232DE286149A6FF512F180AF5657780FC89C009E2C348F', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 6, + }, + issuer: Object @Alleged: BoardRemoteUSDC_axl issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'USDC_axl', + proposedName: 'USD Coin', + }, + 'ibc/386D09AE31DA7C0C93091BB45D08CB7A0730B1F697CD813F06A5446DCF02EEB2': { + brand: Object @Alleged: BoardRemoteUSDT_grv brand { + getBoardId: Function getBoardId {}, + }, + denom: 'ibc/386D09AE31DA7C0C93091BB45D08CB7A0730B1F697CD813F06A5446DCF02EEB2', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 6, + }, + issuer: Object @Alleged: BoardRemoteUSDT_grv issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'USDT_grv', + proposedName: 'Tether USD', + }, + 'ibc/3914BDEF46F429A26917E4D8D434620EC4817DC6B6E68FB327E190902F1E9242': { + brand: Object @Alleged: BoardRemoteDAI_axl brand { + getBoardId: Function getBoardId {}, + }, + denom: 'ibc/3914BDEF46F429A26917E4D8D434620EC4817DC6B6E68FB327E190902F1E9242', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 18, + }, + issuer: Object @Alleged: BoardRemoteDAI_axl issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'DAI_axl', + proposedName: 'DAI', + }, + 'ibc/3D5291C23D776C3AA7A7ABB34C7B023193ECD2BC42EA19D3165B2CF9652117E7': { + brand: Object @Alleged: BoardRemoteDAI_grv brand { + getBoardId: Function getBoardId {}, + }, + denom: 'ibc/3D5291C23D776C3AA7A7ABB34C7B023193ECD2BC42EA19D3165B2CF9652117E7', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 18, + }, + issuer: Object @Alleged: BoardRemoteDAI_grv issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'DAI_grv', + proposedName: 'DAI', + }, + 'ibc/42225F147137DDEB5FEF0F1D0A92F2AD57557AFA2C4D6F30B21E0D983001C002': { + brand: Object @Alleged: BoardRemotestATOM brand { + getBoardId: Function getBoardId {}, + }, + denom: 'ibc/42225F147137DDEB5FEF0F1D0A92F2AD57557AFA2C4D6F30B21E0D983001C002', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 6, + }, + issuer: Object @Alleged: BoardRemotestATOM issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'stATOM', + proposedName: 'stATOM', + }, + 'ibc/6831292903487E58BF9A195FDDC8A2E626B3DF39B88F4E7F41C935CADBAF54AC': { + brand: Object @Alleged: BoardRemoteUSDC_grv brand { + getBoardId: Function getBoardId {}, + }, + denom: 'ibc/6831292903487E58BF9A195FDDC8A2E626B3DF39B88F4E7F41C935CADBAF54AC', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 6, + }, + issuer: Object @Alleged: BoardRemoteUSDC_grv issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'USDC_grv', + proposedName: 'USC Coin', + }, + 'ibc/BA313C4A19DFBF943586C0387E6B11286F9E416B4DD27574E6909CABE0E342FA': { + brand: Object @Alleged: BoardRemoteATOM brand { + getBoardId: Function getBoardId {}, + }, + denom: 'ibc/BA313C4A19DFBF943586C0387E6B11286F9E416B4DD27574E6909CABE0E342FA', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 6, + }, + issuer: Object @Alleged: BoardRemoteATOM issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'ATOM', + proposedName: 'ATOM', + }, + 'ibc/F2331645B9683116188EF36FC04A809C28BD36B54555E8705A37146D0182F045': { + brand: Object @Alleged: BoardRemoteUSDT_axl brand { + getBoardId: Function getBoardId {}, + }, + denom: 'ibc/F2331645B9683116188EF36FC04A809C28BD36B54555E8705A37146D0182F045', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 6, + }, + issuer: Object @Alleged: BoardRemoteUSDT_axl issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'USDT_axl', + proposedName: 'Tether USD', + }, + ubld: { + brand: Object @Alleged: BoardRemoteBLD brand { + getBoardId: Function getBoardId {}, + }, + denom: 'ubld', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 6, + }, + issuer: Object @Alleged: BoardRemoteBLD issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'BLD', + proposedName: 'Agoric staking token', + }, + uist: { + brand: Object @Alleged: BoardRemoteIST brand { + getBoardId: Function getBoardId {}, + }, + denom: 'uist', + displayInfo: { + assetKind: 'nat', + decimalPlaces: 6, + }, + issuer: Object @Alleged: BoardRemoteIST issuer { + getBoardId: Function getBoardId {}, + }, + issuerName: 'IST', + proposedName: 'Agoric stable token', + }, + }, + } + +> priceFeed from A3P + + { + amountIn: { + brand: Object @Alleged: BoardRemoteBrand { + getBoardId: Function getBoardId {}, + }, + value: 1000000n, + }, + amountOut: { + brand: Object @Alleged: BoardRemoteBrand { + getBoardId: Function getBoardId {}, + }, + value: 12010000n, + }, + timer: Object @Alleged: BoardRemotetimerService { + getBoardId: Function getBoardId {}, + }, + timestamp: { + absValue: 1729176290n, + timerBrand: Object @Alleged: BoardRemotetimerBrand { + getBoardId: Function getBoardId {}, + }, + }, + } diff --git a/packages/client-utils/test/snapshots/vstorage-kit.test.js.snap b/packages/client-utils/test/snapshots/vstorage-kit.test.js.snap new file mode 100644 index 00000000000..b36c073f925 Binary files /dev/null and b/packages/client-utils/test/snapshots/vstorage-kit.test.js.snap differ diff --git a/packages/client-utils/test/vstorage-kit.test.js b/packages/client-utils/test/vstorage-kit.test.js new file mode 100644 index 00000000000..a6d11c6972f --- /dev/null +++ b/packages/client-utils/test/vstorage-kit.test.js @@ -0,0 +1,88 @@ +/* eslint-env node */ +import test from 'ava'; +import { makeAgoricNames, makeVstorageKit } from '../src/vstorage-kit.js'; + +const makeMockFetch = (responses = {}) => { + return async url => { + const response = responses[url] || { + result: { + response: { + code: 0, + value: Buffer.from( + JSON.stringify({ value: '{"blockHeight":1,"values":[]}' }), + ).toString('base64'), + }, + }, + }; + return { json: () => Promise.resolve(response) }; + }; +}; + +const makeTestConfig = () => ({ + chainName: 'test-chain', + rpcAddrs: ['http://localhost:26657'], +}); + +test('agoricNames contains expected structure', async t => { + /** @type {typeof window.fetch} */ + // @ts-expect-error mock + const fetch = makeMockFetch({ + 'http://localhost:26657/abci_query?path=%22/custom/vstorage/data/published.agoricNames.brand%22&height=0': + { + result: { + response: { + code: 0, + value: + // observed in a3p + 'ewogICJ2YWx1ZSI6ICJ7XCJibG9ja0hlaWdodFwiOlwiNzY5XCIsXCJ2YWx1ZXNcIjpbXCJ7XFxcImJvZHlcXFwiOlxcXCIjW1tcXFxcXFxcIkFUT01cXFxcXFxcIixcXFxcXFxcIiQwLkFsbGVnZWQ6IEFUT00gYnJhbmRcXFxcXFxcIl0sW1xcXFxcXFwiQkxEXFxcXFxcXCIsXFxcXFxcXCIkMS5BbGxlZ2VkOiBCTEQgYnJhbmRcXFxcXFxcIl0sW1xcXFxcXFwiREFJX2F4bFxcXFxcXFwiLFxcXFxcXFwiJDIuQWxsZWdlZDogREFJX2F4bCBicmFuZFxcXFxcXFwiXSxbXFxcXFxcXCJEQUlfZ3J2XFxcXFxcXCIsXFxcXFxcXCIkMy5BbGxlZ2VkOiBEQUlfZ3J2IGJyYW5kXFxcXFxcXCJdLFtcXFxcXFxcIklTVFxcXFxcXFwiLFxcXFxcXFwiJDQuQWxsZWdlZDogSVNUIGJyYW5kXFxcXFxcXCJdLFtcXFxcXFxcIkludml0YXRpb25cXFxcXFxcIixcXFxcXFxcIiQ1LkFsbGVnZWQ6IFpvZSBJbnZpdGF0aW9uIGJyYW5kXFxcXFxcXCJdLFtcXFxcXFxcIktSRUFkQ0hBUkFDVEVSXFxcXFxcXCIsXFxcXFxcXCIkNi5BbGxlZ2VkOiBLUkVBZENIQVJBQ1RFUiBicmFuZFxcXFxcXFwiXSxbXFxcXFxcXCJLUkVBZElURU1cXFxcXFxcIixcXFxcXFxcIiQ3LkFsbGVnZWQ6IEtSRUFkSVRFTSBicmFuZFxcXFxcXFwiXSxbXFxcXFxcXCJVU0RDX2F4bFxcXFxcXFwiLFxcXFxcXFwiJDguQWxsZWdlZDogVVNEQ19heGwgYnJhbmRcXFxcXFxcIl0sW1xcXFxcXFwiVVNEQ19ncnZcXFxcXFxcIixcXFxcXFxcIiQ5LkFsbGVnZWQ6IFVTRENfZ3J2IGJyYW5kXFxcXFxcXCJdLFtcXFxcXFxcIlVTRFRfYXhsXFxcXFxcXCIsXFxcXFxcXCIkMTAuQWxsZWdlZDogVVNEVF9heGwgYnJhbmRcXFxcXFxcIl0sW1xcXFxcXFwiVVNEVF9ncnZcXFxcXFxcIixcXFxcXFxcIiQxMS5BbGxlZ2VkOiBVU0RUX2dydiBicmFuZFxcXFxcXFwiXSxbXFxcXFxcXCJ0aW1lclxcXFxcXFwiLFxcXFxcXFwiJDEyLkFsbGVnZWQ6IHRpbWVyQnJhbmRcXFxcXFxcIl0sW1xcXFxcXFwic3RBVE9NXFxcXFxcXCIsXFxcXFxcXCIkMTMuQWxsZWdlZDogc3RBVE9NIGJyYW5kXFxcXFxcXCJdXVxcXCIsXFxcInNsb3RzXFxcIjpbXFxcImJvYXJkMDU1NTdcXFwiLFxcXCJib2FyZDA1NjZcXFwiLFxcXCJib2FyZDA1NzM2XFxcIixcXFwiYm9hcmQwMzEzOFxcXCIsXFxcImJvYXJkMDI1N1xcXCIsXFxcImJvYXJkMDA3NFxcXCIsXFxcImJvYXJkMDMyODFcXFwiLFxcXCJib2FyZDAwMjgyXFxcIixcXFwiYm9hcmQwMzA0MFxcXCIsXFxcImJvYXJkMDQ1NDJcXFwiLFxcXCJib2FyZDAxNzQ0XFxcIixcXFwiYm9hcmQwMzQ0NlxcXCIsXFxcImJvYXJkMDQyNVxcXCIsXFxcImJvYXJkMDA5OTBcXFwiXX1cIl19Igp9', + }, + }, + }, + 'http://localhost:26657/abci_query?path=%22/custom/vstorage/data/published.agoricNames.instance%22&height=0': + { + result: { + response: { + code: 0, + value: + // observed in a3p + 'ewogICJ2YWx1ZSI6ICJ7XCJibG9ja0hlaWdodFwiOlwiMTE0MVwiLFwidmFsdWVzXCI6W1wie1xcXCJib2R5XFxcIjpcXFwiI1tbXFxcXFxcXCJBVE9NLVVTRCBwcmljZSBmZWVkXFxcXFxcXCIsXFxcXFxcXCIkMC5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJDcmFiYmxlXFxcXFxcXCIsXFxcXFxcXCIkMS5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJDcmFiYmxlQ29tbWl0dGVlXFxcXFxcXCIsXFxcXFxcXCIkMi5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJDcmFiYmxlR292ZXJub3JcXFxcXFxcIixcXFxcXFxcIiQzLkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcIlZhdWx0RmFjdG9yeVxcXFxcXFwiLFxcXFxcXFwiJDQuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiVmF1bHRGYWN0b3J5R292ZXJub3JcXFxcXFxcIixcXFxcXFxcIiQ1LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcImVjb25Db21taXR0ZWVDaGFydGVyXFxcXFxcXCIsXFxcXFxcXCIkNi5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJlY29ub21pY0NvbW1pdHRlZVxcXFxcXFwiLFxcXFxcXFwiJDcuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiZmVlRGlzdHJpYnV0b3JcXFxcXFxcIixcXFxcXFxcIiQ4LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcImtyZWFkXFxcXFxcXCIsXFxcXFxcXCIkOS5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJrcmVhZENvbW1pdHRlZVxcXFxcXFwiLFxcXFxcXFwiJDEwLkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcImtyZWFkQ29tbWl0dGVlQ2hhcnRlclxcXFxcXFwiLFxcXFxcXFwiJDExLkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInByb3Zpc2lvblBvb2xcXFxcXFxcIixcXFxcXFxcIiQxMi5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJwc20tSVNULURBSV9heGxcXFxcXFxcIixcXFxcXFxcIiQxMy5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJwc20tSVNULURBSV9ncnZcXFxcXFxcIixcXFxcXFxcIiQxNC5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJwc20tSVNULVVTRENfYXhsXFxcXFxcXCIsXFxcXFxcXCIkMTUuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwicHNtLUlTVC1VU0RDX2dydlxcXFxcXFwiLFxcXFxcXFwiJDE2LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInBzbS1JU1QtVVNEVF9heGxcXFxcXFxcIixcXFxcXFxcIiQxNy5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJwc20tSVNULVVTRFRfZ3J2XFxcXFxcXCIsXFxcXFxcXCIkMTguQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwicmVzZXJ2ZVxcXFxcXFwiLFxcXFxcXFwiJDE5LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInJlc2VydmVHb3Zlcm5vclxcXFxcXFwiLFxcXFxcXFwiJDIwLkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInNjYWxlZFByaWNlQXV0aG9yaXR5LXN0QVRPTVxcXFxcXFwiLFxcXFxcXFwiJDIxLkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInN0QVRPTS1VU0QgcHJpY2UgZmVlZFxcXFxcXFwiLFxcXFxcXFwiJDIyLkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcIndhbGxldEZhY3RvcnlcXFxcXFxcIixcXFxcXFxcIiQyMy5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXV1cXFwiLFxcXCJzbG90c1xcXCI6W1xcXCJib2FyZDAyOTYzXFxcIixcXFwiYm9hcmQwNDM5NVxcXCIsXFxcImJvYXJkMDIzOTNcXFwiLFxcXCJib2FyZDA1Mzk2XFxcIixcXFwiYm9hcmQwMDM2MFxcXCIsXFxcImJvYXJkMDM3NzNcXFwiLFxcXCJib2FyZDA0NjYxXFxcIixcXFwiYm9hcmQwNDE0OVxcXCIsXFxcImJvYXJkMDUyNjJcXFwiLFxcXCJib2FyZDA0NzgzXFxcIixcXFwiYm9hcmQwMTk4NVxcXCIsXFxcImJvYXJkMDYyODRcXFwiLFxcXCJib2FyZDAxNjY0XFxcIixcXFwiYm9hcmQwMTg2N1xcXCIsXFxcImJvYXJkMDI1NjhcXFwiLFxcXCJib2FyZDA1NjY5XFxcIixcXFwiYm9hcmQwNTk3MFxcXCIsXFxcImJvYXJkMDIyNzFcXFwiLFxcXCJib2FyZDAxMjcyXFxcIixcXFwiYm9hcmQwNjQ1OFxcXCIsXFxcImJvYXJkMDMzNjVcXFwiLFxcXCJib2FyZDA1ODkyXFxcIixcXFwiYm9hcmQwNDA5MVxcXCIsXFxcImJvYXJkMDYzNjZcXFwiXX1cIixcIntcXFwiYm9keVxcXCI6XFxcIiNbW1xcXFxcXFwiQVRPTS1VU0QgcHJpY2UgZmVlZFxcXFxcXFwiLFxcXFxcXFwiJDAuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiQ3JhYmJsZVxcXFxcXFwiLFxcXFxcXFwiJDEuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiQ3JhYmJsZUNvbW1pdHRlZVxcXFxcXFwiLFxcXFxcXFwiJDIuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiQ3JhYmJsZUdvdmVybm9yXFxcXFxcXCIsXFxcXFxcXCIkMy5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJWYXVsdEZhY3RvcnlcXFxcXFxcIixcXFxcXFxcIiQ0LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcIlZhdWx0RmFjdG9yeUdvdmVybm9yXFxcXFxcXCIsXFxcXFxcXCIkNS5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJlY29uQ29tbWl0dGVlQ2hhcnRlclxcXFxcXFwiLFxcXFxcXFwiJDYuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiZWNvbm9taWNDb21taXR0ZWVcXFxcXFxcIixcXFxcXFxcIiQ3LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcImZlZURpc3RyaWJ1dG9yXFxcXFxcXCIsXFxcXFxcXCIkOC5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJrcmVhZFxcXFxcXFwiLFxcXFxcXFwiJDkuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwia3JlYWRDb21taXR0ZWVcXFxcXFxcIixcXFxcXFxcIiQxMC5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJrcmVhZENvbW1pdHRlZUNoYXJ0ZXJcXFxcXFxcIixcXFxcXFxcIiQxMS5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJwcm92aXNpb25Qb29sXFxcXFxcXCIsXFxcXFxcXCIkMTIuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwicHNtLUlTVC1EQUlfYXhsXFxcXFxcXCIsXFxcXFxcXCIkMTMuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwicHNtLUlTVC1EQUlfZ3J2XFxcXFxcXCIsXFxcXFxcXCIkMTQuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwicHNtLUlTVC1VU0RDX2F4bFxcXFxcXFwiLFxcXFxcXFwiJDE1LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInBzbS1JU1QtVVNEQ19ncnZcXFxcXFxcIixcXFxcXFxcIiQxNi5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJwc20tSVNULVVTRFRfYXhsXFxcXFxcXCIsXFxcXFxcXCIkMTcuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwicHNtLUlTVC1VU0RUX2dydlxcXFxcXFwiLFxcXFxcXFwiJDE4LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInJlc2VydmVcXFxcXFxcIixcXFxcXFxcIiQxOS5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJyZXNlcnZlR292ZXJub3JcXFxcXFxcIixcXFxcXFxcIiQyMC5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJzY2FsZWRQcmljZUF1dGhvcml0eS1zdEFUT01cXFxcXFxcIixcXFxcXFxcIiQyMS5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJzdEFUT00tVVNEIHByaWNlIGZlZWRcXFxcXFxcIixcXFxcXFxcIiQyMi5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJ3YWxsZXRGYWN0b3J5XFxcXFxcXCIsXFxcXFxcXCIkMjMuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiYXVjdGlvbmVlclxcXFxcXFwiLFxcXFxcXFwiJDI0LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdXVxcXCIsXFxcInNsb3RzXFxcIjpbXFxcImJvYXJkMDI5NjNcXFwiLFxcXCJib2FyZDA0Mzk1XFxcIixcXFwiYm9hcmQwMjM5M1xcXCIsXFxcImJvYXJkMDUzOTZcXFwiLFxcXCJib2FyZDAwMzYwXFxcIixcXFwiYm9hcmQwMzc3M1xcXCIsXFxcImJvYXJkMDQ2NjFcXFwiLFxcXCJib2FyZDA0MTQ5XFxcIixcXFwiYm9hcmQwNTI2MlxcXCIsXFxcImJvYXJkMDQ3ODNcXFwiLFxcXCJib2FyZDAxOTg1XFxcIixcXFwiYm9hcmQwNjI4NFxcXCIsXFxcImJvYXJkMDE2NjRcXFwiLFxcXCJib2FyZDAxODY3XFxcIixcXFwiYm9hcmQwMjU2OFxcXCIsXFxcImJvYXJkMDU2NjlcXFwiLFxcXCJib2FyZDA1OTcwXFxcIixcXFwiYm9hcmQwMjI3MVxcXCIsXFxcImJvYXJkMDEyNzJcXFwiLFxcXCJib2FyZDA2NDU4XFxcIixcXFwiYm9hcmQwMzM2NVxcXCIsXFxcImJvYXJkMDU4OTJcXFwiLFxcXCJib2FyZDA0MDkxXFxcIixcXFwiYm9hcmQwNjM2NlxcXCIsXFxcImJvYXJkMDYyOTlcXFwiXX1cIixcIntcXFwiYm9keVxcXCI6XFxcIiNbW1xcXFxcXFwiQVRPTS1VU0QgcHJpY2UgZmVlZFxcXFxcXFwiLFxcXFxcXFwiJDAuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiQ3JhYmJsZVxcXFxcXFwiLFxcXFxcXFwiJDEuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiQ3JhYmJsZUNvbW1pdHRlZVxcXFxcXFwiLFxcXFxcXFwiJDIuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiQ3JhYmJsZUdvdmVybm9yXFxcXFxcXCIsXFxcXFxcXCIkMy5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJWYXVsdEZhY3RvcnlcXFxcXFxcIixcXFxcXFxcIiQ0LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcIlZhdWx0RmFjdG9yeUdvdmVybm9yXFxcXFxcXCIsXFxcXFxcXCIkNS5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJhdWN0aW9uZWVyXFxcXFxcXCIsXFxcXFxcXCIkNi5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJlY29uQ29tbWl0dGVlQ2hhcnRlclxcXFxcXFwiLFxcXFxcXFwiJDcuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwiZWNvbm9taWNDb21taXR0ZWVcXFxcXFxcIixcXFxcXFxcIiQ4LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcImZlZURpc3RyaWJ1dG9yXFxcXFxcXCIsXFxcXFxcXCIkOS5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJrcmVhZFxcXFxcXFwiLFxcXFxcXFwiJDEwLkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcImtyZWFkQ29tbWl0dGVlXFxcXFxcXCIsXFxcXFxcXCIkMTEuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwia3JlYWRDb21taXR0ZWVDaGFydGVyXFxcXFxcXCIsXFxcXFxcXCIkMTIuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwicHJvdmlzaW9uUG9vbFxcXFxcXFwiLFxcXFxcXFwiJDEzLkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInBzbS1JU1QtREFJX2F4bFxcXFxcXFwiLFxcXFxcXFwiJDE0LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInBzbS1JU1QtREFJX2dydlxcXFxcXFwiLFxcXFxcXFwiJDE1LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInBzbS1JU1QtVVNEQ19heGxcXFxcXFxcIixcXFxcXFxcIiQxNi5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJwc20tSVNULVVTRENfZ3J2XFxcXFxcXCIsXFxcXFxcXCIkMTcuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwicHNtLUlTVC1VU0RUX2F4bFxcXFxcXFwiLFxcXFxcXFwiJDE4LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdLFtcXFxcXFxcInBzbS1JU1QtVVNEVF9ncnZcXFxcXFxcIixcXFxcXFxcIiQxOS5BbGxlZ2VkOiBJbnN0YW5jZUhhbmRsZVxcXFxcXFwiXSxbXFxcXFxcXCJyZXNlcnZlXFxcXFxcXCIsXFxcXFxcXCIkMjAuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwicmVzZXJ2ZUdvdmVybm9yXFxcXFxcXCIsXFxcXFxcXCIkMjEuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwic2NhbGVkUHJpY2VBdXRob3JpdHktc3RBVE9NXFxcXFxcXCIsXFxcXFxcXCIkMjIuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwic3RBVE9NLVVTRCBwcmljZSBmZWVkXFxcXFxcXCIsXFxcXFxcXCIkMjMuQWxsZWdlZDogSW5zdGFuY2VIYW5kbGVcXFxcXFxcIl0sW1xcXFxcXFwid2FsbGV0RmFjdG9yeVxcXFxcXFwiLFxcXFxcXFwiJDI0LkFsbGVnZWQ6IEluc3RhbmNlSGFuZGxlXFxcXFxcXCJdXVxcXCIsXFxcInNsb3RzXFxcIjpbXFxcImJvYXJkMDI5NjNcXFwiLFxcXCJib2FyZDA0Mzk1XFxcIixcXFwiYm9hcmQwMjM5M1xcXCIsXFxcImJvYXJkMDUzOTZcXFwiLFxcXCJib2FyZDAwMzYwXFxcIixcXFwiYm9hcmQwMzc3M1xcXCIsXFxcImJvYXJkMDYyOTlcXFwiLFxcXCJib2FyZDA0NjYxXFxcIixcXFwiYm9hcmQwNDE0OVxcXCIsXFxcImJvYXJkMDUyNjJcXFwiLFxcXCJib2FyZDA0NzgzXFxcIixcXFwiYm9hcmQwMTk4NVxcXCIsXFxcImJvYXJkMDYyODRcXFwiLFxcXCJib2FyZDAxNjY0XFxcIixcXFwiYm9hcmQwMTg2N1xcXCIsXFxcImJvYXJkMDI1NjhcXFwiLFxcXCJib2FyZDA1NjY5XFxcIixcXFwiYm9hcmQwNTk3MFxcXCIsXFxcImJvYXJkMDIyNzFcXFwiLFxcXCJib2FyZDAxMjcyXFxcIixcXFwiYm9hcmQwNjQ1OFxcXCIsXFxcImJvYXJkMDMzNjVcXFwiLFxcXCJib2FyZDA1ODkyXFxcIixcXFwiYm9hcmQwNDA5MVxcXCIsXFxcImJvYXJkMDYzNjZcXFwiXX1cIl19Igp9', + }, + }, + }, + 'http://localhost:26657/abci_query?path=%22/custom/vstorage/data/published.agoricNames.vbankAsset%22&height=0': + { + result: { + response: { + code: 0, + value: + // observed in a3p + 'ewogICJ2YWx1ZSI6ICJ7XCJibG9ja0hlaWdodFwiOlwiNzY5XCIsXCJ2YWx1ZXNcIjpbXCJ7XFxcImJvZHlcXFwiOlxcXCIjW1tcXFxcXFxcImliYy8yOTU1NDhBNzg3ODVBMTAwN0YyMzJERTI4NjE0OUE2RkY1MTJGMTgwQUY1NjU3NzgwRkM4OUMwMDlFMkMzNDhGXFxcXFxcXCIse1xcXFxcXFwiYnJhbmRcXFxcXFxcIjpcXFxcXFxcIiQwLkFsbGVnZWQ6IFVTRENfYXhsIGJyYW5kXFxcXFxcXCIsXFxcXFxcXCJkZW5vbVxcXFxcXFwiOlxcXFxcXFwiaWJjLzI5NTU0OEE3ODc4NUExMDA3RjIzMkRFMjg2MTQ5QTZGRjUxMkYxODBBRjU2NTc3ODBGQzg5QzAwOUUyQzM0OEZcXFxcXFxcIixcXFxcXFxcImRpc3BsYXlJbmZvXFxcXFxcXCI6e1xcXFxcXFwiYXNzZXRLaW5kXFxcXFxcXCI6XFxcXFxcXCJuYXRcXFxcXFxcIixcXFxcXFxcImRlY2ltYWxQbGFjZXNcXFxcXFxcIjo2fSxcXFxcXFxcImlzc3VlclxcXFxcXFwiOlxcXFxcXFwiJDEuQWxsZWdlZDogVVNEQ19heGwgaXNzdWVyXFxcXFxcXCIsXFxcXFxcXCJpc3N1ZXJOYW1lXFxcXFxcXCI6XFxcXFxcXCJVU0RDX2F4bFxcXFxcXFwiLFxcXFxcXFwicHJvcG9zZWROYW1lXFxcXFxcXCI6XFxcXFxcXCJVU0QgQ29pblxcXFxcXFwifV0sW1xcXFxcXFwiaWJjLzM4NkQwOUFFMzFEQTdDMEM5MzA5MUJCNDVEMDhDQjdBMDczMEIxRjY5N0NEODEzRjA2QTU0NDZEQ0YwMkVFQjJcXFxcXFxcIix7XFxcXFxcXCJicmFuZFxcXFxcXFwiOlxcXFxcXFwiJDIuQWxsZWdlZDogVVNEVF9ncnYgYnJhbmRcXFxcXFxcIixcXFxcXFxcImRlbm9tXFxcXFxcXCI6XFxcXFxcXCJpYmMvMzg2RDA5QUUzMURBN0MwQzkzMDkxQkI0NUQwOENCN0EwNzMwQjFGNjk3Q0Q4MTNGMDZBNTQ0NkRDRjAyRUVCMlxcXFxcXFwiLFxcXFxcXFwiZGlzcGxheUluZm9cXFxcXFxcIjp7XFxcXFxcXCJhc3NldEtpbmRcXFxcXFxcIjpcXFxcXFxcIm5hdFxcXFxcXFwiLFxcXFxcXFwiZGVjaW1hbFBsYWNlc1xcXFxcXFwiOjZ9LFxcXFxcXFwiaXNzdWVyXFxcXFxcXCI6XFxcXFxcXCIkMy5BbGxlZ2VkOiBVU0RUX2dydiBpc3N1ZXJcXFxcXFxcIixcXFxcXFxcImlzc3Vlck5hbWVcXFxcXFxcIjpcXFxcXFxcIlVTRFRfZ3J2XFxcXFxcXCIsXFxcXFxcXCJwcm9wb3NlZE5hbWVcXFxcXFxcIjpcXFxcXFxcIlRldGhlciBVU0RcXFxcXFxcIn1dLFtcXFxcXFxcImliYy8zOTE0QkRFRjQ2RjQyOUEyNjkxN0U0RDhENDM0NjIwRUM0ODE3REM2QjZFNjhGQjMyN0UxOTA5MDJGMUU5MjQyXFxcXFxcXCIse1xcXFxcXFwiYnJhbmRcXFxcXFxcIjpcXFxcXFxcIiQ0LkFsbGVnZWQ6IERBSV9heGwgYnJhbmRcXFxcXFxcIixcXFxcXFxcImRlbm9tXFxcXFxcXCI6XFxcXFxcXCJpYmMvMzkxNEJERUY0NkY0MjlBMjY5MTdFNEQ4RDQzNDYyMEVDNDgxN0RDNkI2RTY4RkIzMjdFMTkwOTAyRjFFOTI0MlxcXFxcXFwiLFxcXFxcXFwiZGlzcGxheUluZm9cXFxcXFxcIjp7XFxcXFxcXCJhc3NldEtpbmRcXFxcXFxcIjpcXFxcXFxcIm5hdFxcXFxcXFwiLFxcXFxcXFwiZGVjaW1hbFBsYWNlc1xcXFxcXFwiOjE4fSxcXFxcXFxcImlzc3VlclxcXFxcXFwiOlxcXFxcXFwiJDUuQWxsZWdlZDogREFJX2F4bCBpc3N1ZXJcXFxcXFxcIixcXFxcXFxcImlzc3Vlck5hbWVcXFxcXFxcIjpcXFxcXFxcIkRBSV9heGxcXFxcXFxcIixcXFxcXFxcInByb3Bvc2VkTmFtZVxcXFxcXFwiOlxcXFxcXFwiREFJXFxcXFxcXCJ9XSxbXFxcXFxcXCJpYmMvM0Q1MjkxQzIzRDc3NkMzQUE3QTdBQkIzNEM3QjAyMzE5M0VDRDJCQzQyRUExOUQzMTY1QjJDRjk2NTIxMTdFN1xcXFxcXFwiLHtcXFxcXFxcImJyYW5kXFxcXFxcXCI6XFxcXFxcXCIkNi5BbGxlZ2VkOiBEQUlfZ3J2IGJyYW5kXFxcXFxcXCIsXFxcXFxcXCJkZW5vbVxcXFxcXFwiOlxcXFxcXFwiaWJjLzNENTI5MUMyM0Q3NzZDM0FBN0E3QUJCMzRDN0IwMjMxOTNFQ0QyQkM0MkVBMTlEMzE2NUIyQ0Y5NjUyMTE3RTdcXFxcXFxcIixcXFxcXFxcImRpc3BsYXlJbmZvXFxcXFxcXCI6e1xcXFxcXFwiYXNzZXRLaW5kXFxcXFxcXCI6XFxcXFxcXCJuYXRcXFxcXFxcIixcXFxcXFxcImRlY2ltYWxQbGFjZXNcXFxcXFxcIjoxOH0sXFxcXFxcXCJpc3N1ZXJcXFxcXFxcIjpcXFxcXFxcIiQ3LkFsbGVnZWQ6IERBSV9ncnYgaXNzdWVyXFxcXFxcXCIsXFxcXFxcXCJpc3N1ZXJOYW1lXFxcXFxcXCI6XFxcXFxcXCJEQUlfZ3J2XFxcXFxcXCIsXFxcXFxcXCJwcm9wb3NlZE5hbWVcXFxcXFxcIjpcXFxcXFxcIkRBSVxcXFxcXFwifV0sW1xcXFxcXFwiaWJjLzQyMjI1RjE0NzEzN0RERUI1RkVGMEYxRDBBOTJGMkFENTc1NTdBRkEyQzRENkYzMEIyMUUwRDk4MzAwMUMwMDJcXFxcXFxcIix7XFxcXFxcXCJicmFuZFxcXFxcXFwiOlxcXFxcXFwiJDguQWxsZWdlZDogc3RBVE9NIGJyYW5kXFxcXFxcXCIsXFxcXFxcXCJkZW5vbVxcXFxcXFwiOlxcXFxcXFwiaWJjLzQyMjI1RjE0NzEzN0RERUI1RkVGMEYxRDBBOTJGMkFENTc1NTdBRkEyQzRENkYzMEIyMUUwRDk4MzAwMUMwMDJcXFxcXFxcIixcXFxcXFxcImRpc3BsYXlJbmZvXFxcXFxcXCI6e1xcXFxcXFwiYXNzZXRLaW5kXFxcXFxcXCI6XFxcXFxcXCJuYXRcXFxcXFxcIixcXFxcXFxcImRlY2ltYWxQbGFjZXNcXFxcXFxcIjo2fSxcXFxcXFxcImlzc3VlclxcXFxcXFwiOlxcXFxcXFwiJDkuQWxsZWdlZDogc3RBVE9NIGlzc3VlclxcXFxcXFwiLFxcXFxcXFwiaXNzdWVyTmFtZVxcXFxcXFwiOlxcXFxcXFwic3RBVE9NXFxcXFxcXCIsXFxcXFxcXCJwcm9wb3NlZE5hbWVcXFxcXFxcIjpcXFxcXFxcInN0QVRPTVxcXFxcXFwifV0sW1xcXFxcXFwiaWJjLzY4MzEyOTI5MDM0ODdFNThCRjlBMTk1RkREQzhBMkU2MjZCM0RGMzlCODhGNEU3RjQxQzkzNUNBREJBRjU0QUNcXFxcXFxcIix7XFxcXFxcXCJicmFuZFxcXFxcXFwiOlxcXFxcXFwiJDEwLkFsbGVnZWQ6IFVTRENfZ3J2IGJyYW5kXFxcXFxcXCIsXFxcXFxcXCJkZW5vbVxcXFxcXFwiOlxcXFxcXFwiaWJjLzY4MzEyOTI5MDM0ODdFNThCRjlBMTk1RkREQzhBMkU2MjZCM0RGMzlCODhGNEU3RjQxQzkzNUNBREJBRjU0QUNcXFxcXFxcIixcXFxcXFxcImRpc3BsYXlJbmZvXFxcXFxcXCI6e1xcXFxcXFwiYXNzZXRLaW5kXFxcXFxcXCI6XFxcXFxcXCJuYXRcXFxcXFxcIixcXFxcXFxcImRlY2ltYWxQbGFjZXNcXFxcXFxcIjo2fSxcXFxcXFxcImlzc3VlclxcXFxcXFwiOlxcXFxcXFwiJDExLkFsbGVnZWQ6IFVTRENfZ3J2IGlzc3VlclxcXFxcXFwiLFxcXFxcXFwiaXNzdWVyTmFtZVxcXFxcXFwiOlxcXFxcXFwiVVNEQ19ncnZcXFxcXFxcIixcXFxcXFxcInByb3Bvc2VkTmFtZVxcXFxcXFwiOlxcXFxcXFwiVVNDIENvaW5cXFxcXFxcIn1dLFtcXFxcXFxcImliYy9CQTMxM0M0QTE5REZCRjk0MzU4NkMwMzg3RTZCMTEyODZGOUU0MTZCNEREMjc1NzRFNjkwOUNBQkUwRTM0MkZBXFxcXFxcXCIse1xcXFxcXFwiYnJhbmRcXFxcXFxcIjpcXFxcXFxcIiQxMi5BbGxlZ2VkOiBBVE9NIGJyYW5kXFxcXFxcXCIsXFxcXFxcXCJkZW5vbVxcXFxcXFwiOlxcXFxcXFwiaWJjL0JBMzEzQzRBMTlERkJGOTQzNTg2QzAzODdFNkIxMTI4NkY5RTQxNkI0REQyNzU3NEU2OTA5Q0FCRTBFMzQyRkFcXFxcXFxcIixcXFxcXFxcImRpc3BsYXlJbmZvXFxcXFxcXCI6e1xcXFxcXFwiYXNzZXRLaW5kXFxcXFxcXCI6XFxcXFxcXCJuYXRcXFxcXFxcIixcXFxcXFxcImRlY2ltYWxQbGFjZXNcXFxcXFxcIjo2fSxcXFxcXFxcImlzc3VlclxcXFxcXFwiOlxcXFxcXFwiJDEzLkFsbGVnZWQ6IEFUT00gaXNzdWVyXFxcXFxcXCIsXFxcXFxcXCJpc3N1ZXJOYW1lXFxcXFxcXCI6XFxcXFxcXCJBVE9NXFxcXFxcXCIsXFxcXFxcXCJwcm9wb3NlZE5hbWVcXFxcXFxcIjpcXFxcXFxcIkFUT01cXFxcXFxcIn1dLFtcXFxcXFxcImliYy9GMjMzMTY0NUI5NjgzMTE2MTg4RUYzNkZDMDRBODA5QzI4QkQzNkI1NDU1NUU4NzA1QTM3MTQ2RDAxODJGMDQ1XFxcXFxcXCIse1xcXFxcXFwiYnJhbmRcXFxcXFxcIjpcXFxcXFxcIiQxNC5BbGxlZ2VkOiBVU0RUX2F4bCBicmFuZFxcXFxcXFwiLFxcXFxcXFwiZGVub21cXFxcXFxcIjpcXFxcXFxcImliYy9GMjMzMTY0NUI5NjgzMTE2MTg4RUYzNkZDMDRBODA5QzI4QkQzNkI1NDU1NUU4NzA1QTM3MTQ2RDAxODJGMDQ1XFxcXFxcXCIsXFxcXFxcXCJkaXNwbGF5SW5mb1xcXFxcXFwiOntcXFxcXFxcImFzc2V0S2luZFxcXFxcXFwiOlxcXFxcXFwibmF0XFxcXFxcXCIsXFxcXFxcXCJkZWNpbWFsUGxhY2VzXFxcXFxcXCI6Nn0sXFxcXFxcXCJpc3N1ZXJcXFxcXFxcIjpcXFxcXFxcIiQxNS5BbGxlZ2VkOiBVU0RUX2F4bCBpc3N1ZXJcXFxcXFxcIixcXFxcXFxcImlzc3Vlck5hbWVcXFxcXFxcIjpcXFxcXFxcIlVTRFRfYXhsXFxcXFxcXCIsXFxcXFxcXCJwcm9wb3NlZE5hbWVcXFxcXFxcIjpcXFxcXFxcIlRldGhlciBVU0RcXFxcXFxcIn1dLFtcXFxcXFxcInVibGRcXFxcXFxcIix7XFxcXFxcXCJicmFuZFxcXFxcXFwiOlxcXFxcXFwiJDE2LkFsbGVnZWQ6IEJMRCBicmFuZFxcXFxcXFwiLFxcXFxcXFwiZGVub21cXFxcXFxcIjpcXFxcXFxcInVibGRcXFxcXFxcIixcXFxcXFxcImRpc3BsYXlJbmZvXFxcXFxcXCI6e1xcXFxcXFwiYXNzZXRLaW5kXFxcXFxcXCI6XFxcXFxcXCJuYXRcXFxcXFxcIixcXFxcXFxcImRlY2ltYWxQbGFjZXNcXFxcXFxcIjo2fSxcXFxcXFxcImlzc3VlclxcXFxcXFwiOlxcXFxcXFwiJDE3LkFsbGVnZWQ6IEJMRCBpc3N1ZXJcXFxcXFxcIixcXFxcXFxcImlzc3Vlck5hbWVcXFxcXFxcIjpcXFxcXFxcIkJMRFxcXFxcXFwiLFxcXFxcXFwicHJvcG9zZWROYW1lXFxcXFxcXCI6XFxcXFxcXCJBZ29yaWMgc3Rha2luZyB0b2tlblxcXFxcXFwifV0sW1xcXFxcXFwidWlzdFxcXFxcXFwiLHtcXFxcXFxcImJyYW5kXFxcXFxcXCI6XFxcXFxcXCIkMTguQWxsZWdlZDogSVNUIGJyYW5kXFxcXFxcXCIsXFxcXFxcXCJkZW5vbVxcXFxcXFwiOlxcXFxcXFwidWlzdFxcXFxcXFwiLFxcXFxcXFwiZGlzcGxheUluZm9cXFxcXFxcIjp7XFxcXFxcXCJhc3NldEtpbmRcXFxcXFxcIjpcXFxcXFxcIm5hdFxcXFxcXFwiLFxcXFxcXFwiZGVjaW1hbFBsYWNlc1xcXFxcXFwiOjZ9LFxcXFxcXFwiaXNzdWVyXFxcXFxcXCI6XFxcXFxcXCIkMTkuQWxsZWdlZDogSVNUIGlzc3VlclxcXFxcXFwiLFxcXFxcXFwiaXNzdWVyTmFtZVxcXFxcXFwiOlxcXFxcXFwiSVNUXFxcXFxcXCIsXFxcXFxcXCJwcm9wb3NlZE5hbWVcXFxcXFxcIjpcXFxcXFxcIkFnb3JpYyBzdGFibGUgdG9rZW5cXFxcXFxcIn1dXVxcXCIsXFxcInNsb3RzXFxcIjpbXFxcImJvYXJkMDMwNDBcXFwiLFxcXCJib2FyZDA1MTQxXFxcIixcXFwiYm9hcmQwMzQ0NlxcXCIsXFxcImJvYXJkMDE1NDdcXFwiLFxcXCJib2FyZDA1NzM2XFxcIixcXFwiYm9hcmQwMjQzN1xcXCIsXFxcImJvYXJkMDMxMzhcXFwiLFxcXCJib2FyZDA1MDM5XFxcIixcXFwiYm9hcmQwMDk5MFxcXCIsXFxcImJvYXJkMDA2ODlcXFwiLFxcXCJib2FyZDA0NTQyXFxcIixcXFwiYm9hcmQwMDQ0M1xcXCIsXFxcImJvYXJkMDU1NTdcXFwiLFxcXCJib2FyZDAyNjU2XFxcIixcXFwiYm9hcmQwMTc0NFxcXCIsXFxcImJvYXJkMDY0NDVcXFwiLFxcXCJib2FyZDA1NjZcXFwiLFxcXCJib2FyZDA1OTJcXFwiLFxcXCJib2FyZDAyNTdcXFwiLFxcXCJib2FyZDAyMjNcXFwiXX1cIl19Igp9', + }, + }, + }, + 'http://localhost:26657/abci_query?path=%22/custom/vstorage/data/published.priceFeed.ATOM-USD_price_feed%22&height=0': + { + result: { + response: { + code: 0, + value: + // observed in a3p + 'ewogICJ2YWx1ZSI6ICJ7XCJibG9ja0hlaWdodFwiOlwiNTA4XCIsXCJ2YWx1ZXNcIjpbXCJ7XFxcImJvZHlcXFwiOlxcXCIje1xcXFxcXFwiYW1vdW50SW5cXFxcXFxcIjp7XFxcXFxcXCJicmFuZFxcXFxcXFwiOlxcXFxcXFwiJDAuQWxsZWdlZDogQnJhbmRcXFxcXFxcIixcXFxcXFxcInZhbHVlXFxcXFxcXCI6XFxcXFxcXCIrMTAwMDAwMFxcXFxcXFwifSxcXFxcXFxcImFtb3VudE91dFxcXFxcXFwiOntcXFxcXFxcImJyYW5kXFxcXFxcXCI6XFxcXFxcXCIkMS5BbGxlZ2VkOiBCcmFuZFxcXFxcXFwiLFxcXFxcXFwidmFsdWVcXFxcXFxcIjpcXFxcXFxcIisxMjAxMDAwMFxcXFxcXFwifSxcXFxcXFxcInRpbWVyXFxcXFxcXCI6XFxcXFxcXCIkMi5BbGxlZ2VkOiB0aW1lclNlcnZpY2VcXFxcXFxcIixcXFxcXFxcInRpbWVzdGFtcFxcXFxcXFwiOntcXFxcXFxcImFic1ZhbHVlXFxcXFxcXCI6XFxcXFxcXCIrMTcyOTE3NjI5MFxcXFxcXFwiLFxcXFxcXFwidGltZXJCcmFuZFxcXFxcXFwiOlxcXFxcXFwiJDMuQWxsZWdlZDogdGltZXJCcmFuZFxcXFxcXFwifX1cXFwiLFxcXCJzbG90c1xcXCI6W1xcXCJib2FyZDAzOTM1XFxcIixcXFwiYm9hcmQwMTAzNFxcXCIsXFxcImJvYXJkMDU2NzRcXFwiLFxcXCJib2FyZDA0MjVcXFwiXX1cIixcIntcXFwiYm9keVxcXCI6XFxcIiN7XFxcXFxcXCJhbW91bnRJblxcXFxcXFwiOntcXFxcXFxcImJyYW5kXFxcXFxcXCI6XFxcXFxcXCIkMC5BbGxlZ2VkOiBCcmFuZFxcXFxcXFwiLFxcXFxcXFwidmFsdWVcXFxcXFxcIjpcXFxcXFxcIisxMDAwMDAwXFxcXFxcXCJ9LFxcXFxcXFwiYW1vdW50T3V0XFxcXFxcXCI6e1xcXFxcXFwiYnJhbmRcXFxcXFxcIjpcXFxcXFxcIiQxLkFsbGVnZWQ6IEJyYW5kXFxcXFxcXCIsXFxcXFxcXCJ2YWx1ZVxcXFxcXFwiOlxcXFxcXFwiKzEyMDEwMDAwXFxcXFxcXCJ9LFxcXFxcXFwidGltZXJcXFxcXFxcIjpcXFxcXFxcIiQyLkFsbGVnZWQ6IHRpbWVyU2VydmljZVxcXFxcXFwiLFxcXFxcXFwidGltZXN0YW1wXFxcXFxcXCI6e1xcXFxcXFwiYWJzVmFsdWVcXFxcXFxcIjpcXFxcXFxcIisxNzI5MTc2MjkwXFxcXFxcXCIsXFxcXFxcXCJ0aW1lckJyYW5kXFxcXFxcXCI6XFxcXFxcXCIkMy5BbGxlZ2VkOiB0aW1lckJyYW5kXFxcXFxcXCJ9fVxcXCIsXFxcInNsb3RzXFxcIjpbXFxcImJvYXJkMDM5MzVcXFwiLFxcXCJib2FyZDAxMDM0XFxcIixcXFwiYm9hcmQwNTY3NFxcXCIsXFxcImJvYXJkMDQyNVxcXCJdfVwiXX0iCn0', + }, + }, + }, + }); + + const vstorageKit = makeVstorageKit({ fetch }, makeTestConfig()); + const agoricNames = await makeAgoricNames( + vstorageKit.fromBoard, + vstorageKit.vstorage, + ); + + t.snapshot(agoricNames, 'agoricNames from A3P'); + + const priceFeed = await vstorageKit.readPublished( + 'priceFeed.ATOM-USD_price_feed', + ); + t.snapshot(priceFeed, 'priceFeed from A3P'); +}); diff --git a/packages/fast-usdc/src/cli/lp-commands.js b/packages/fast-usdc/src/cli/lp-commands.js index 4b829cfbfba..afcbd063650 100644 --- a/packages/fast-usdc/src/cli/lp-commands.js +++ b/packages/fast-usdc/src/cli/lp-commands.js @@ -1,3 +1,4 @@ +/* eslint-env node */ /** * @import {Command} from 'commander'; * @import {OfferSpec} from '@agoric/smart-wallet/src/offers.js'; @@ -5,17 +6,22 @@ * @import {USDCProposalShapes} from '../pool-share-math.js'; */ -import { fetchEnvNetworkConfig, makeVstorageKit } from '@agoric/client-utils'; -import { InvalidArgumentError } from 'commander'; +import { + fetchEnvNetworkConfig, + makeSmartWalletKit, +} from '@agoric/client-utils'; +import { AmountMath } from '@agoric/ertp'; import { assertParsableNumber, ceilDivideBy, multiplyBy, parseRatio, } from '@agoric/zoe/src/contractSupport/ratio.js'; -import { AmountMath } from '@agoric/ertp'; +import { InvalidArgumentError } from 'commander'; import { outputActionAndHint } from './bridge-action.js'; +export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); + /** @param {string} arg */ const parseDecimal = arg => { try { @@ -41,7 +47,7 @@ const parseUSDCAmount = (amountString, usdc) => { * @param {Command} program * @param {{ * fetch?: Window['fetch']; - * vstorageKit?: Awaited>; + * smartWalletKit?: import('@agoric/client-utils').SmartWalletKit; * stdout: typeof process.stdout; * stderr: typeof process.stderr; * env: typeof process.env; @@ -50,18 +56,18 @@ const parseUSDCAmount = (amountString, usdc) => { */ export const addLPCommands = ( program, - { fetch, vstorageKit, stderr, stdout, env, now }, + { fetch, smartWalletKit, stderr, stdout, env, now }, ) => { - const loadVsk = async () => { - if (vstorageKit) { - return vstorageKit; + const loadSwk = async () => { + if (smartWalletKit) { + return smartWalletKit; } assert(fetch); const networkConfig = await fetchEnvNetworkConfig({ env, fetch }); - return makeVstorageKit({ fetch }, networkConfig); + return makeSmartWalletKit({ delay, fetch }, networkConfig); }; - /** @type {undefined | ReturnType} */ - let vskP; + /** @type {undefined | ReturnType} */ + let swkP; program .command('deposit') @@ -73,11 +79,11 @@ export const addLPCommands = ( .requiredOption('--amount ', 'USDC amount', parseDecimal) .option('--offerId ', 'Offer id', String, `lpDeposit-${now()}`) .action(async opts => { - vskP ||= loadVsk(); - const vsk = await vskP; + swkP ||= loadSwk(); + const swk = await swkP; /** @type {Brand<'nat'>} */ // @ts-expect-error it doesnt recognize usdc as a Brand type - const usdc = vsk.agoricNames.brand.USDC; + const usdc = swk.agoricNames.brand.USDC; assert(usdc, 'USDC brand not in agoricNames'); const usdcAmount = parseUSDCAmount(opts.amount, usdc); @@ -106,7 +112,7 @@ export const addLPCommands = ( offer, }; - outputActionAndHint(bridgeAction, { stderr, stdout }, vsk.marshaller); + outputActionAndHint(bridgeAction, { stderr, stdout }, swk.marshaller); }); program @@ -119,24 +125,25 @@ export const addLPCommands = ( .requiredOption('--amount ', 'USDC amount', parseDecimal) .option('--offerId ', 'Offer id', String, `lpWithdraw-${now()}`) .action(async opts => { - vskP ||= loadVsk(); - const vsk = await vskP; + swkP ||= loadSwk(); + swkP ||= loadSwk(); + const swk = await swkP; /** @type {Brand<'nat'>} */ // @ts-expect-error it doesnt recognize FastLP as a Brand type - const poolShare = vsk.agoricNames.brand.FastLP; + const poolShare = swk.agoricNames.brand.FastLP; assert(poolShare, 'FastLP brand not in agoricNames'); /** @type {Brand<'nat'>} */ // @ts-expect-error it doesnt recognize usdc as a Brand type - const usdc = vsk.agoricNames.brand.USDC; + const usdc = swk.agoricNames.brand.USDC; assert(usdc, 'USDC brand not in agoricNames'); const usdcAmount = parseUSDCAmount(opts.amount, usdc); /** @type {import('../types.js').PoolMetrics} */ // @ts-expect-error it treats this as "unknown" - const metrics = await vsk.readPublished('fastUsdc.poolMetrics'); + const metrics = await swk.readPublished('fastUsdc.poolMetrics'); const fastLPAmount = ceilDivideBy(usdcAmount, metrics.shareWorth); /** @type {USDCProposalShapes['withdraw']} */ @@ -163,7 +170,7 @@ export const addLPCommands = ( outputActionAndHint( { method: 'executeOffer', offer }, { stderr, stdout }, - vsk.marshaller, + swk.marshaller, ); }); diff --git a/packages/fast-usdc/src/cli/operator-commands.js b/packages/fast-usdc/src/cli/operator-commands.js index 196a48af8a4..8f849f760db 100644 --- a/packages/fast-usdc/src/cli/operator-commands.js +++ b/packages/fast-usdc/src/cli/operator-commands.js @@ -1,3 +1,4 @@ +/* eslint-env node */ /** * @import {Command} from 'commander'; * @import {OfferSpec} from '@agoric/smart-wallet/src/offers.js'; @@ -5,7 +6,10 @@ * @import {OperatorKit} from '../exos/operator-kit.js'; */ -import { fetchEnvNetworkConfig, makeVstorageKit } from '@agoric/client-utils'; +import { + fetchEnvNetworkConfig, + makeSmartWalletKit, +} from '@agoric/client-utils'; import { mustMatch } from '@agoric/internal'; import { Nat } from '@endo/nat'; import { InvalidArgumentError } from 'commander'; @@ -13,6 +17,8 @@ import { INVITATION_MAKERS_DESC } from '../exos/transaction-feed.js'; import { CctpTxEvidenceShape } from '../type-guards.js'; import { outputActionAndHint } from './bridge-action.js'; +export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); + /** @param {string} arg */ const parseNat = arg => { const n = Nat(BigInt(arg)); @@ -53,8 +59,9 @@ export const addOperatorCommands = ( .option('--offerId ', 'Offer id', String, `operatorAccept-${now()}`) .action(async opts => { const networkConfig = await fetchEnvNetworkConfig({ env, fetch }); - const vsk = await makeVstorageKit({ fetch }, networkConfig); - const instance = vsk.agoricNames.instance.fastUsdc; + + const swk = await makeSmartWalletKit({ delay, fetch }, networkConfig); + const instance = swk.agoricNames.instance.fastUsdc; assert(instance, 'fastUsdc instance not in agoricNames'); /** @type {OfferSpec} */ diff --git a/packages/fast-usdc/test/cli/lp-commands.test.ts b/packages/fast-usdc/test/cli/lp-commands.test.ts index 367dc9a78f6..0ca642aa704 100644 --- a/packages/fast-usdc/test/cli/lp-commands.test.ts +++ b/packages/fast-usdc/test/cli/lp-commands.test.ts @@ -32,7 +32,7 @@ const makeTestContext = () => { const now = () => 1234; addLPCommands(program, { - vstorageKit: { + smartWalletKit: { // @ts-expect-error fake brands agoricNames: { brand: { FastLP, USDC } }, marshaller,