From 7ec102af50c99f2a7172c43a4f0f6b8fd57c402d Mon Sep 17 00:00:00 2001 From: Carly Doppelheuer Date: Tue, 5 Sep 2023 13:58:03 -0500 Subject: [PATCH 1/2] Add factory event prompts to create group cmd --- package-lock.json | 19 ++----- package.json | 1 + src/api/client.ts | 10 ++++ src/api/routes.ts | 2 + src/cmds/create/group.ts | 28 ++++++++++- src/services/eventVersionServices.ts | 28 +++++++++++ src/types.ts | 5 ++ src/utils/prompt.ts | 75 +++++++++++++++++++++++++++- 8 files changed, 149 insertions(+), 19 deletions(-) create mode 100644 src/services/eventVersionServices.ts diff --git a/package-lock.json b/package-lock.json index 3b5d546..7a51ff4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "ora": "5.4.1", "parse-database-url": "^0.3.0", "progress-string": "^1.2.2", + "prompts": "^2.4.2", "qoa": "^0.2.0", "uuid4": "^2.0.3" }, @@ -5314,8 +5315,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "peer": true, "engines": { "node": ">=6" } @@ -6580,8 +6579,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "peer": true, "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -7437,9 +7434,7 @@ "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "peer": true + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "node_modules/slash": { "version": "3.0.0", @@ -13324,9 +13319,7 @@ "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "peer": true + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" }, "leven": { "version": "3.1.0", @@ -14291,8 +14284,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "peer": true, "requires": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -14985,9 +14976,7 @@ "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "peer": true + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "slash": { "version": "3.0.0", diff --git a/package.json b/package.json index 0169049..073fc08 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "ora": "5.4.1", "parse-database-url": "^0.3.0", "progress-string": "^1.2.2", + "prompts": "^2.4.2", "qoa": "^0.2.0", "uuid4": "^2.0.3" }, diff --git a/src/api/client.ts b/src/api/client.ts index 031af6e..c88a10b 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -12,6 +12,7 @@ import { GetContractGroupEventsResponse, StringMap, StringKeyMap, + SearchEventVersionsResponse, } from '../types' const formatAuthHeader = (sessionToken: string): StringMap => ({ @@ -147,6 +148,14 @@ async function getContractGroupEvents(group: string): Promise { + const { error, data } = await get(buildUrl(routes.SEARCH_EVENT_VERSIONS), { + name, + }) + + return error ? { error } : { events: data || [] } +} + export const client = { login, getProject, @@ -157,4 +166,5 @@ export const client = { createContractGroup, getContractGroup, getContractGroupEvents, + searchEventVersions, } diff --git a/src/api/routes.ts b/src/api/routes.ts index 788a08e..2f94ace 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -9,6 +9,7 @@ const prefix = { CONTRACT_REGISTRATION_JOB: 'contract-registration-job', CONTRACT: 'contract', ABI: 'abi', + EVENT_VERSIONS: 'event-versions', } export const routes = { @@ -22,6 +23,7 @@ export const routes = { CREATE_CONTRACT_GROUP: [prefix.CONTRACT, 'group'].join('/'), GET_CONTRACT_GROUP: [prefix.CONTRACT, 'group'].join('/'), GET_CONTRACT_GROUP_EVENTS: [prefix.CONTRACT, 'group', 'events'].join('/'), + SEARCH_EVENT_VERSIONS: [prefix.EVENT_VERSIONS, 'search'].join('/'), } export const buildUrl = (route: string) => { diff --git a/src/cmds/create/group.ts b/src/cmds/create/group.ts index 36f0c00..ba70bc4 100644 --- a/src/cmds/create/group.ts +++ b/src/cmds/create/group.ts @@ -5,7 +5,8 @@ import { client } from '../../api/client' import { chainIdsSet } from '../../utils/chains' import { isValidContractGroup } from '../../utils/validators' import { resolveAbi } from '../../utils/abi' -import { promptCreateContractGroupDetails } from '../../utils/prompt' +import { getFactoryEvent, promptCreateContractGroupDetails } from '../../utils/prompt' +import chalk from 'chalk' const CMD = 'group' @@ -14,6 +15,7 @@ function addCreateGroupCmd(cmd) { .argument('[group]', 'Name of the contract group in "nsp.ContractName" format', null) .option('--chains ', `The chain ids of the group's future contracts`, null) .option('--abi ', `Path to the group's ABI`, null) + .option('--isFactory ', `Factory contract?`, null) .action(createGroup) } @@ -25,6 +27,7 @@ async function createGroup( opts: { chains: string abi: string + isFactory: boolean } ) { // Get authed user's session token (if any). @@ -39,7 +42,12 @@ async function createGroup( } // Prompt user for inputs if not given directly. - const promptResp = await promptCreateContractGroupDetails(group, opts.chains, opts.abi) + const promptResp = await promptCreateContractGroupDetails( + group, + opts.chains, + opts.abi, + opts.isFactory + ) // Validate contract group structure (e.g. "nsp.ContractName") if (!isValidContractGroup(promptResp.group)) { @@ -65,6 +73,22 @@ async function createGroup( return } + // Get factory event details for factory group. + if (promptResp.isFactory) { + const [factoryEventId, addressProperty] = await getFactoryEvent(promptResp.group) + const [name, version] = factoryEventId?.split('@') || [] + + // Log factory event details (temp formatting). + log(` + ${chalk.cyanBright(contractName)} | Factory group + ${ + factoryEventId + ? `${chalk.green(name.split('.')[2])} | ${chalk.gray(version)} + Address property: ${chalk.gray(addressProperty ? addressProperty : 'No property selected')}` + : chalk.gray('No factory event selected') + }`) + } + // Create empty contract group. const { error: createError } = await client.createContractGroup( sessionToken, diff --git a/src/services/eventVersionServices.ts b/src/services/eventVersionServices.ts new file mode 100644 index 0000000..cdd8a7b --- /dev/null +++ b/src/services/eventVersionServices.ts @@ -0,0 +1,28 @@ +import chalk from 'chalk' +import { client } from '../api/client' +import { logFailure } from '../logger' +import { StringKeyMap } from '../types' + +export const getEventVersions = async (name) => { + const { error, events } = await client.searchEventVersions(name) + if (error) { + logFailure(`Event retreival failed: ${error}`) + return + } + return events +} + +export const formatEventVersions = async (events: StringKeyMap): Promise => { + const cliEvents = events.map((event) => { + const [cliName, version] = event.searchId.split('@') + const formattedEventVersion = chalk.gray( + ` | ${version.slice(0, 10)}${version.length > 10 ? '...' : ''}` + ) + return { + title: cliName + formattedEventVersion, + value: event.searchId, + } + }) + + return cliEvents +} diff --git a/src/types.ts b/src/types.ts index dcd61f7..24de533 100644 --- a/src/types.ts +++ b/src/types.ts @@ -86,3 +86,8 @@ export interface Migration { name: string version: string } + +export interface SearchEventVersionsResponse { + error?: string + events?: StringKeyMap +} diff --git a/src/utils/prompt.ts b/src/utils/prompt.ts index 6964330..69bdf9e 100644 --- a/src/utils/prompt.ts +++ b/src/utils/prompt.ts @@ -1,6 +1,8 @@ import { StringKeyMap } from '../types' import qoa from 'qoa' import chalk from 'chalk' +import prompts from 'prompts' +import { formatEventVersions, getEventVersions } from '../services/eventVersionServices' const EMAIL_PROMPT = { type: 'input', @@ -74,6 +76,14 @@ const CONTRACT_ADDRESSES_PROMPT = { handle: 'addresses', } +const IS_FACTORY_PROMPT = { + type: 'confirm', + name: 'value', + message: 'Factory contract? ', + accept: 'Y', + deny: 'n', +} + function stripWrappedQuotes(val: string): string { if (!val) return val if (val[0] === '"') { @@ -135,7 +145,8 @@ export async function promptNewLiveObjectDetails( export async function promptCreateContractGroupDetails( group?: string, chainIds?: string, - abi?: string + abi?: string, + isFactory?: boolean ): Promise { // Required while (!group) { @@ -152,7 +163,13 @@ export async function promptCreateContractGroupDetails( abi = stripWrappedQuotes((await qoa.prompt([ABI_PROMPT])).abi) } - return { group, chainIds, abi } + // Required + while (isFactory === null) { + isFactory = (await prompts(IS_FACTORY_PROMPT)).value + } + + // return { group, chainIds, abi, isFactory } + return { group, chainIds, isFactory } } export async function promptAddContractsDetails( @@ -181,3 +198,57 @@ export async function promptAddContractsDetails( return { addresses, chainId, group, abi } } + +export async function getFactoryEvent(contractGroup: string): Promise { + let factoryEventId + let addressProperty + let cachedResults + let useInitialInput = true + + factoryEventId = ( + await prompts({ + type: 'autocomplete', + name: 'searchId', + message: 'Search for factory event: ', + choices: ['', '', '', '', '', ''], + fallback: ' ', + suggest: async (input, choices) => { + let results = useInitialInput + ? await getEventVersions(contractGroup) + : await getEventVersions(input) + useInitialInput = false + if (!results.length) { + return [] + } + cachedResults = results + const displayResults = await formatEventVersions(results) + return displayResults + }, + }) + ).searchId + + if (!factoryEventId) return [] + + const factoryEvent = cachedResults.find((result) => result.searchId === factoryEventId) + const properties = factoryEvent.addressProperties.map((prop) => { + return { + title: prop, + value: prop, + } + }) + + if (!properties.length) return [factoryEventId] + + addressProperty = ( + await prompts({ + type: 'select', + message: 'Which property is the contract address?', + name: 'property', + symbol: '>', + fallback: 'No properties available', + choices: properties, + }) + ).property + + return [factoryEventId, addressProperty] +} From d4582990d1f424bd973de65d65543ca9dba49811 Mon Sep 17 00:00:00 2001 From: Carly Doppelheuer Date: Tue, 5 Sep 2023 14:32:31 -0500 Subject: [PATCH 2/2] update return --- src/utils/prompt.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/prompt.ts b/src/utils/prompt.ts index 69bdf9e..86af07e 100644 --- a/src/utils/prompt.ts +++ b/src/utils/prompt.ts @@ -168,8 +168,7 @@ export async function promptCreateContractGroupDetails( isFactory = (await prompts(IS_FACTORY_PROMPT)).value } - // return { group, chainIds, abi, isFactory } - return { group, chainIds, isFactory } + return { group, chainIds, abi, isFactory } } export async function promptAddContractsDetails(