From 4c7ef4fe45ddf12345531369c4b2ffd5b100c08f Mon Sep 17 00:00:00 2001 From: Kris Urbas <605420+krzysu@users.noreply.github.com> Date: Wed, 10 Apr 2024 15:26:37 +0200 Subject: [PATCH] add investigate command --- .vscode/settings.json | 3 +- packages/cli/.eslintrc.cjs | 10 -- .../cli/src/commands/createTestProfile.ts | 109 ++++++++-------- packages/cli/src/commands/investigate.ts | 118 ++++++++++++++++++ packages/cli/src/index.ts | 8 +- .../{utils => lib}/commandToEnvironment.ts | 11 +- packages/cli/src/lib/consts.ts | 2 + packages/cli/src/lib/output.ts | 16 +++ packages/cli/src/lib/safeRequest.ts | 22 ++++ packages/cli/src/utils/logger.ts | 12 -- 10 files changed, 220 insertions(+), 91 deletions(-) create mode 100644 packages/cli/src/commands/investigate.ts rename packages/cli/src/{utils => lib}/commandToEnvironment.ts (75%) create mode 100644 packages/cli/src/lib/consts.ts create mode 100644 packages/cli/src/lib/output.ts create mode 100644 packages/cli/src/lib/safeRequest.ts delete mode 100644 packages/cli/src/utils/logger.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 85fc8b2d5d..e9226b8c8f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,12 +1,13 @@ { "eslint.workingDirectories": [ - "examples/nextjs", + "examples/lens-next-app", "examples/node", "examples/react-native", "examples/shared", "examples/web", "packages/api-bindings", "packages/blockchain-bindings", + "packages/cli", "packages/client", "packages/domain", "packages/eslint-config", diff --git a/packages/cli/.eslintrc.cjs b/packages/cli/.eslintrc.cjs index 56e7eb44d3..9592a1cead 100644 --- a/packages/cli/.eslintrc.cjs +++ b/packages/cli/.eslintrc.cjs @@ -5,16 +5,6 @@ module.exports = { project: './tsconfig.json', }, extends: ['@lens-protocol/eslint-config'], -}; - -module.exports = { - root: true, - extends: ['@lens-protocol/eslint-config'], - parser: '@typescript-eslint/parser', - parserOptions: { - project: ['./tsconfig.json'], - tsconfigRootDir: __dirname, - }, rules: { 'no-console': 'off', }, diff --git a/packages/cli/src/commands/createTestProfile.ts b/packages/cli/src/commands/createTestProfile.ts index 86f2375b69..390acd6213 100644 --- a/packages/cli/src/commands/createTestProfile.ts +++ b/packages/cli/src/commands/createTestProfile.ts @@ -3,71 +3,70 @@ import { isAddress } from '@ethersproject/address'; import { isRelaySuccess, isValidHandle } from '@lens-protocol/client'; import { createSpinner } from 'nanospinner'; -import { - ensureParentCommand, - getHandlePrefix, - initLensClient, -} from '../utils/commandToEnvironment.js'; +import { ensureParentCommand, initLensClient } from '../lib/commandToEnvironment.js'; +import { output } from '../lib/output.js'; -const createTestProfile = new Command('create-profile') - .description('Create a new test profile, possible only in the development environment') - .requiredOption('-h, --handle ', 'Test profile handle') - .requiredOption('-a, --address
', 'Wallet address') - .action(async (options) => { - const validation = createSpinner(`Validating input data`).start(); +export function createTestProfile() { + const cmd = new Command('create-profile') + .description('Create a new test profile, possible only in the development environment') + .requiredOption('-h, --handle ', 'Test profile handle') + .requiredOption('-a, --address
', 'Wallet address') + .action(async (options) => { + const validation = createSpinner(`Validating input data`).start(); - if (!isValidHandle(options.handle)) { - validation.error(); - console.error(`Invalid handle: ${options.handle}`); - process.exit(1); - } + if (!isValidHandle(options.handle)) { + validation.error(); + output.error(`Invalid handle: ${options.handle}`); + process.exit(1); + } - if (!isAddress(options.address)) { - validation.error(); - console.error(`Invalid address: ${options.address}`); - process.exit(1); - } + if (!isAddress(options.address)) { + validation.error(); + output.error(`Invalid address: ${options.address}`); + process.exit(1); + } - const parentCommandName = ensureParentCommand(createTestProfile); - const client = initLensClient(parentCommandName); + const parentCommandName = ensureParentCommand(cmd); + const client = initLensClient(parentCommandName); - // check if the requested handle is available - const handleOwnerAddress = await client.handle.resolveAddress({ - handle: `${getHandlePrefix(parentCommandName)}/${options.handle}`, - }); + // check if the requested handle is available + const handleOwnerAddress = await client.handle.resolveAddress({ + handle: `lens/${options.handle}`, + }); + + if (handleOwnerAddress) { + validation.error(); + output.error(`The requested handle "${options.handle}" is not available.`); + process.exit(1); + } + validation.success(); - if (handleOwnerAddress) { - validation.error(); - console.error(`The requested handle "${options.handle}" is not available.`); - process.exit(1); - } - validation.success(); + const creation = createSpinner( + `Creating new test profile with handle "${options.handle}" for address "${options.address}"`, + ).start(); - const creation = createSpinner( - `Creating new test profile with handle "${options.handle}" for address "${options.address}"`, - ).start(); + try { + const profileCreateResult = await client.wallet.createProfileWithHandle({ + handle: options.handle, + to: options.address, + }); - try { - const profileCreateResult = await client.wallet.createProfileWithHandle({ - handle: options.handle, - to: options.address, - }); + if (!isRelaySuccess(profileCreateResult)) { + creation.error(); + output.error(`Something went wrong:`, profileCreateResult); + process.exit(1); + } + + await client.transaction.waitUntilComplete({ forTxId: profileCreateResult.txId }); - if (!isRelaySuccess(profileCreateResult)) { + creation.success(); + output.success(`Profile created successfully`); + } catch (error) { creation.error(); - console.error(`Something went wrong`, profileCreateResult); + output.error(error); process.exit(1); } + }); - await client.transaction.waitUntilComplete({ forTxId: profileCreateResult.txId }); - - creation.success(); - console.log(`Profile created successfully`); - } catch (error) { - creation.error(); - console.error(error); - process.exit(1); - } - }); - -export { createTestProfile }; + return cmd; +} diff --git a/packages/cli/src/commands/investigate.ts b/packages/cli/src/commands/investigate.ts new file mode 100644 index 0000000000..0dadae9642 --- /dev/null +++ b/packages/cli/src/commands/investigate.ts @@ -0,0 +1,118 @@ +import { Command } from '@commander-js/extra-typings'; +import { BigDecimal } from '@lens-protocol/shared-kernel'; +import chalk from 'chalk'; +import { createSpinner } from 'nanospinner'; + +import { ensureParentCommand, initLensClient } from '../lib/commandToEnvironment.js'; +import { LENS_HANDLES_CONTRACT, LENS_PROFILES_CONTRACT } from '../lib/consts.js'; +import { output } from '../lib/output.js'; +import { safeRequest } from '../lib/safeRequest.js'; + +const hexToDecimal = (hex: string) => BigDecimal.from(hex).toFixed(); + +export function investigate() { + const cmd = new Command('investigate') + .description('Investigate a Profile ID, Handle or Wallet Address') + .option('-h, --handle ', 'Handle with prefix (lens/handle)') + .option('-a, --address
', 'Wallet address') + .option('-p, --profile
', 'Profile ID') + .action(async (options) => { + if (!options.handle && !options.address && !options.profile) { + output.error('At least one of the options is required. See --help for more information.'); + process.exit(1); + } + + const parentCommandName = ensureParentCommand(cmd); + const client = initLensClient(parentCommandName); + + // investigate handle + if (options.handle) { + const fullHandle = options.handle; + + const spinner = createSpinner( + `Investigating handle: ${chalk.green(options.handle)}`, + ).start(); + + const address = await safeRequest( + async () => client.handle.resolveAddress({ handle: fullHandle }), + () => spinner.error(), + ); + + const profile = await safeRequest( + async () => client.profile.fetch({ forHandle: fullHandle }), + () => spinner.error(), + ); + + spinner.success(); + + output.value(`Resolved address:`, address); + output.info( + `Handle details:`, + profile && + profile.handle && { + linkedTo: { + profileId: profile.handle.linkedTo?.nftTokenId, + }, + ownedBy: profile.handle.ownedBy, + }, + ); + output.info( + `Linked profile:`, + profile && { + id: profile.id, + ownedBy: profile.ownedBy.address, + createdAt: profile.createdAt, + sponsor: profile.sponsor, + signless: profile.signless, + // guardian: profile.guardian, // can only be read by owner + metadata: profile.metadata && { + displayName: profile.metadata.displayName, + bio: profile.metadata.bio, + }, + stats: profile.stats, + }, + ); + + if (parentCommandName === 'production') { + output.value(`URL:`, `https://share.lens.xyz/u/${fullHandle}`); + profile && + profile.handle && + output.value( + `Lens Handles OpenSea:`, + `https://opensea.io/assets/matic/${LENS_HANDLES_CONTRACT}/${hexToDecimal( + profile.handle.id, + )}`, + ); + profile && + output.value( + `Lens Profiles OpenSea:`, + `https://opensea.io/assets/matic/${LENS_PROFILES_CONTRACT}/${hexToDecimal( + profile.id, + )}`, + ); + } + } + + // investigate address + if (options.address) { + output.value(`Investigating address:`, options.address); + + // validate + // owned/managed profiles + // owned handles + // rate limits + } + + // investigate profile + if (options.profile) { + output.value(`Investigating profile:`, options.profile); + + // owner + // managers + // linked handle + // lens share link + } + }); + + return cmd; +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index b44326fed7..50b19fcc03 100755 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -3,7 +3,7 @@ import { program } from '@commander-js/extra-typings'; import { createTestProfile } from './commands/createTestProfile.js'; -import './utils/logger.js'; +import { investigate } from './commands/investigate.js'; program.name('lens').description('Lens CLI'); @@ -11,11 +11,13 @@ program .command('development') .alias('dev') .description('Command will run in the development environment') - .addCommand(createTestProfile); + .addCommand(createTestProfile()) + .addCommand(investigate()); program .command('production') .alias('prod') - .description('Command will run in the production environment'); + .description('Command will run in the production environment') + .addCommand(investigate()); program.parse(); diff --git a/packages/cli/src/utils/commandToEnvironment.ts b/packages/cli/src/lib/commandToEnvironment.ts similarity index 75% rename from packages/cli/src/utils/commandToEnvironment.ts rename to packages/cli/src/lib/commandToEnvironment.ts index b126345b2e..dae60b02a3 100644 --- a/packages/cli/src/utils/commandToEnvironment.ts +++ b/packages/cli/src/lib/commandToEnvironment.ts @@ -1,4 +1,4 @@ -import { Command } from '@commander-js/extra-typings'; +import type { Command } from '@commander-js/extra-typings'; import { LensClient, production, development } from '@lens-protocol/client'; const commandToEnvironmentMap = { @@ -27,12 +27,3 @@ export function initLensClient(name: EnvCommandName) { environment: commandToEnvironmentMap[name], }); } - -export function getHandlePrefix(name: EnvCommandName) { - switch (name) { - case 'production': - return 'lens'; - case 'development': - return 'test'; - } -} diff --git a/packages/cli/src/lib/consts.ts b/packages/cli/src/lib/consts.ts new file mode 100644 index 0000000000..f1604b96f1 --- /dev/null +++ b/packages/cli/src/lib/consts.ts @@ -0,0 +1,2 @@ +export const LENS_HANDLES_CONTRACT = '0xe7e7ead361f3aacd73a61a9bd6c10ca17f38e945'; +export const LENS_PROFILES_CONTRACT = '0xdb46d1dc155634fbc732f92e853b10b288ad5a1d'; diff --git a/packages/cli/src/lib/output.ts b/packages/cli/src/lib/output.ts new file mode 100644 index 0000000000..441ff0406b --- /dev/null +++ b/packages/cli/src/lib/output.ts @@ -0,0 +1,16 @@ +import chalk from 'chalk'; + +export const output = { + success: function (...args: Parameters) { + console.log(chalk.green(...args)); + }, + error: function (...args: Parameters) { + console.log(chalk.red(...args)); + }, + info: function (...args: Parameters) { + console.log(...args); + }, + value: function (key: string, ...args: Parameters) { + console.log(key, chalk.green(...args)); + }, +}; diff --git a/packages/cli/src/lib/safeRequest.ts b/packages/cli/src/lib/safeRequest.ts new file mode 100644 index 0000000000..0c1c4dac91 --- /dev/null +++ b/packages/cli/src/lib/safeRequest.ts @@ -0,0 +1,22 @@ +import { assertError } from '@lens-protocol/shared-kernel'; + +import { output } from './output.js'; + +/* + * Safely execute a request and handle errors + */ +export async function safeRequest( + callback: () => Promise, + onError?: (error: Error) => void, +): Promise { + try { + return await callback(); + } catch (error) { + assertError(error); + if (onError) { + onError(error); + } + output.error(`Error: ${error.message}`); + process.exit(1); + } +} diff --git a/packages/cli/src/utils/logger.ts b/packages/cli/src/utils/logger.ts deleted file mode 100644 index b945268270..0000000000 --- a/packages/cli/src/utils/logger.ts +++ /dev/null @@ -1,12 +0,0 @@ -import chalk from 'chalk'; - -const log = console.log; -const error = console.error; - -console.log = (...args: Parameters) => { - log(chalk.green(...args)); -}; - -console.error = (...args: Parameters) => { - error(chalk.red(...args)); -};