Skip to content

Commit

Permalink
add investigate command
Browse files Browse the repository at this point in the history
  • Loading branch information
krzysu committed Apr 15, 2024
1 parent f611c94 commit e6fa52a
Show file tree
Hide file tree
Showing 12 changed files with 926 additions and 274 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
10 changes: 0 additions & 10 deletions packages/cli/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
"@lens-protocol/prettier-config": "workspace:*",
"@lens-protocol/tsconfig": "workspace:*",
"@swc/core": "^1.4.13",
"@types/node": "^18.18.12",
"eslint": "^8.54.0",
"prettier": "^3.1.0",
"@types/node": "^18.19.31",
"eslint": "^8.57.0",
"prettier": "^3.2.5",
"ts-node": "^10.9.2",
"tsup": "^8.0.2",
"typescript": "5.2.2"
Expand Down
109 changes: 54 additions & 55 deletions packages/cli/src/commands/createTestProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <handle>', 'Test profile handle')
.requiredOption('-a, --address <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 <handle>', 'Test profile handle')
.requiredOption('-a, --address <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;
}
118 changes: 118 additions & 0 deletions packages/cli/src/commands/investigate.ts
Original file line number Diff line number Diff line change
@@ -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>', 'Handle with prefix (lens/handle)')
.option('-a, --address <address>', 'Wallet address')
.option('-p, --profile <address>', '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;
}
8 changes: 5 additions & 3 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
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');

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();
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down Expand Up @@ -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';
}
}
2 changes: 2 additions & 0 deletions packages/cli/src/lib/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const LENS_HANDLES_CONTRACT = '0xe7e7ead361f3aacd73a61a9bd6c10ca17f38e945';
export const LENS_PROFILES_CONTRACT = '0xdb46d1dc155634fbc732f92e853b10b288ad5a1d';
16 changes: 16 additions & 0 deletions packages/cli/src/lib/output.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import chalk from 'chalk';

export const output = {
success: function (...args: Parameters<typeof console.log>) {
console.log(chalk.green(...args));
},
error: function (...args: Parameters<typeof console.log>) {
console.log(chalk.red(...args));
},
info: function (...args: Parameters<typeof console.log>) {
console.log(...args);
},
value: function (key: string, ...args: Parameters<typeof console.log>) {
console.log(key, chalk.green(...args));
},
};
22 changes: 22 additions & 0 deletions packages/cli/src/lib/safeRequest.ts
Original file line number Diff line number Diff line change
@@ -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<T>(
callback: () => Promise<T>,
onError?: (error: Error) => void,
): Promise<T> {
try {
return await callback();
} catch (error) {
assertError(error);
if (onError) {
onError(error);
}
output.error(`Error: ${error.message}`);
process.exit(1);
}
}
12 changes: 0 additions & 12 deletions packages/cli/src/utils/logger.ts

This file was deleted.

Loading

0 comments on commit e6fa52a

Please sign in to comment.