Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 4 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
9 changes: 9 additions & 0 deletions src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ResolveEventVersionDataAfterResponse,
StringMap,
StringKeyMap,
SearchEventVersionsResponse,
} from '../types'

const formatAuthHeader = (sessionToken: string): StringMap => ({
Expand Down Expand Up @@ -148,6 +149,13 @@ async function getContractGroupEvents(group: string): Promise<GetContractGroupEv
return error ? { error } : { events: data?.events || [] }
}

async function searchEventVersions(name: string): Promise<SearchEventVersionsResponse> {
const { error, data } = await get(buildUrl(routes.SEARCH_EVENT_VERSIONS), {
name,
})
return error ? { error } : { events: data || [] }
}

async function resolveEventVersionCursors(
givenName: string
): Promise<ResolveEventVersionCursorsResponse> {
Expand All @@ -174,6 +182,7 @@ export const client = {
createContractGroup,
getContractGroup,
getContractGroupEvents,
searchEventVersions,
resolveEventVersionCursors,
getEventVersionDataAfter,
}
1 change: 1 addition & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,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_VERSION + 's', 'search'].join('/'),
RESOLVE_EVENT_VERSION_CURSORS: [prefix.EVENT_VERSION + 's', 'resolve', 'cursors'].join('/'),
GET_EVENT_VERSION_DATA_AFTER: [prefix.EVENT_VERSION + 's', 'data', 'after'].join('/'),
}
Expand Down
28 changes: 26 additions & 2 deletions src/cmds/create/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -15,6 +16,7 @@ function addCreateGroupCmd(cmd) {
.argument('[group]', 'Name of the contract group in "nsp.ContractName" format', null)
.option('--chains <chains>', `The chain ids of the group's future contracts`, null)
.option('--abi <abi>', `Path to the group's ABI`, null)
.option('--isFactory <isFactory>', `Factory contract?`, null)
.action(createGroup)
}

Expand All @@ -26,6 +28,7 @@ async function createGroup(
opts: {
chains: string
abi: string
isFactory: boolean
}
) {
// Get authed user's session token (if any).
Expand All @@ -40,7 +43,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)) {
Expand All @@ -66,6 +74,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,
Expand Down
28 changes: 28 additions & 0 deletions src/services/eventVersionServices.ts
Original file line number Diff line number Diff line change
@@ -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<StringKeyMap> => {
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
}
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,8 @@ export interface Migration {
name: string
version: string
}

export interface SearchEventVersionsResponse {
error?: string
events?: StringKeyMap
}
74 changes: 72 additions & 2 deletions src/utils/prompt.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand Down Expand Up @@ -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] === '"') {
Expand Down Expand Up @@ -135,7 +145,8 @@ export async function promptNewLiveObjectDetails(
export async function promptCreateContractGroupDetails(
group?: string,
chainIds?: string,
abi?: string
abi?: string,
isFactory?: boolean
): Promise<StringKeyMap> {
// Required
while (!group) {
Expand All @@ -152,7 +163,12 @@ 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 }
}

export async function promptAddContractsDetails(
Expand Down Expand Up @@ -181,3 +197,57 @@ export async function promptAddContractsDetails(

return { addresses, chainId, group, abi }
}

export async function getFactoryEvent(contractGroup: string): Promise<StringKeyMap[]> {
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]
}