Skip to content

Commit

Permalink
feat: cli tool to setup the sdk and circle console
Browse files Browse the repository at this point in the history
  • Loading branch information
krzysu committed Dec 8, 2024
1 parent bef2667 commit 2bcbe16
Show file tree
Hide file tree
Showing 7 changed files with 475 additions and 1 deletion.
5 changes: 5 additions & 0 deletions packages/circle-sdk-setup/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dist/
*.log
recovery_file_*.dat
node_modules
.env
34 changes: 34 additions & 0 deletions packages/circle-sdk-setup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Circle SDK Setup

CLI tool for setting up Circle SDK configuration. This tool helps you generate and configure the necessary secrets and environment variables for using the Circle SDK.

## Usage

Run the setup command with your Circle API key:

```bash
npx circle-sdk-setup --api-key YOUR_API_KEY
```

Options:

- `-k, --api-key <key>` (required): Your Circle API key

The tool will:

1. Generate a secret for the SDK
2. Fetch your app configuration from Circle
3. Save the configuration to `.env` file
4. Generate a recovery file (`recovery_file_YYYY-MM-DD.dat`)

⚠️ **Important**: After setup, store the `recovery_file_YYYY-MM-DD.dat` file in a secure location and remove it from your project directory.

## Development

```bash
# Build the package
yarn build

# Run in development mode
yarn dev
```
33 changes: 33 additions & 0 deletions packages/circle-sdk-setup/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@circle-libs/circle-sdk-setup",
"version": "0.0.1",
"description": "CLI tool for Circle SDK setup",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"circle-sdk-setup": "./dist/cli.js"
},
"scripts": {
"build": "tsc",
"start": "node dist/cli.js",
"dev": "tsx src/cli.ts"
},
"keywords": [
"circle",
"sdk",
"cli"
],
"author": "",
"license": "MIT",
"dependencies": {
"commander": "^11.0.0",
"dotenv": "^16.3.1",
"web3-circle-sdk": "*"
},
"devDependencies": {
"@types/node": "^20.0.0",
"tsx": "^4.6.2",
"typescript": "^5.0.0"
}
}
202 changes: 202 additions & 0 deletions packages/circle-sdk-setup/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#!/usr/bin/env node

import { CircleSdk, RegisteredEntity, SecretApi } from 'web3-circle-sdk';
import { Command } from 'commander';
import * as fs from 'fs';
import * as path from 'path';
import * as dotenv from 'dotenv';

interface SetupOptions {
apiKey: string;
envPath?: string;
}

class CircleSdkSetup {
private options: Required<SetupOptions>;

constructor(options: SetupOptions) {
this.options = {
envPath: path.resolve(process.cwd(), '.env'),
...options,
};
}

async checkExistingConfig(): Promise<boolean> {
console.log('Checking existing configuration...');
try {
if (fs.existsSync(this.options.envPath)) {
const envContent = dotenv.parse(fs.readFileSync(this.options.envPath));
return !!(envContent.CIRCLE_SECRET && envContent.CIRCLE_APP_ID);
}
return false;
} catch (error) {
console.error('Error checking existing configuration:', error);
return false;
}
}

async generateSecret(): Promise<string> {
console.log('Generating secret...');
const secret = SecretApi.generateSecret();
console.log('Secret generated:', secret);
return secret;
}

async initializeSdk(apiKey: string, secret: string): Promise<CircleSdk> {
console.log('Initializing Circle SDK...');
return new CircleSdk(apiKey, secret);
}

async fetchConfiguration(sdk: CircleSdk) {
console.log('Fetching configuration...');
const config = await sdk.secret.getConfig();
console.log('Configuration fetched:', JSON.stringify(config, null, 2));
return config;
}

async fetchPublicKey(sdk: CircleSdk) {
console.log('Fetching public key...');
const publicKey = await sdk.secret.getPublicKey();
console.log('Public key retrieved');
return publicKey;
}

generateCiphertext(secret: string, publicKey: string) {
console.log('Generating entity secret ciphertext...');
const cipherText = SecretApi.getEntitySecretCiphertext(secret, publicKey);
console.log('Ciphertext generated');
return cipherText;
}

async registerCiphertext(sdk: CircleSdk, cipherText: string) {
try {
console.log('Registering entity secret ciphertext...');
const registration = await sdk.secret.registerEntitySecretCiphertext(cipherText);
console.log('Entity secret ciphertext registered');
return registration;
} catch (error: unknown) {
if (
(typeof error === 'object' && (error as any).code === -1) ||
((error as Error).message &&
(error as Error).message.includes('Something went wrong'))
) {
console.error('\n🚫 Error: Unable to register entity secret ciphertext.');
console.error('This might be due to the ciphertext already being registered.');
console.error('Please check your existing configuration in the Circle Console.');
process.exit(1);
}

// Re-throw other types of errors
throw error;
}
}

prepareEnvFile(config: any, secret: string, apiKey: string) {
console.log('Preparing .env file...');
let envContent = '';
if (fs.existsSync(this.options.envPath)) {
envContent = fs.readFileSync(this.options.envPath, 'utf-8');
// Remove existing Circle config if any
envContent = envContent
.split('\n')
.filter((line) => !line.startsWith('CIRCLE_'))
.join('\n');
if (envContent && !envContent.endsWith('\n')) {
envContent += '\n';
}
}

// Add new Circle config
console.log('Adding new configuration to .env...');
const newConfig = `CIRCLE_API_KEY=${apiKey}
CIRCLE_SECRET=${secret}
CIRCLE_APP_ID=${config.appId}\n`;

fs.writeFileSync(this.options.envPath, envContent + newConfig);
console.log('.env file updated successfully');
}

saveRecoveryFile(registration: RegisteredEntity) {
const currentDate = new Date().toISOString().split('T')[0];
const recoveryFilePath = path.resolve(
process.cwd(),
`recovery_file_${currentDate}.dat`,
);

try {
fs.writeFileSync(recoveryFilePath, registration.recoveryFile, 'utf-8');
console.log(`Recovery file saved to: ${recoveryFilePath}`);
console.log(
'\nIMPORTANT: Keep the recovery file in a secure location and then remove it from this directory',
);
} catch (error) {
console.error('Error saving recovery file:', error);
}
}

async run() {
// Check if config already exists
const configExists = await this.checkExistingConfig();
if (configExists) {
console.error('Circle SDK configuration already exists in .env file.');
console.error(
'Please remove the existing configuration before setting up a new one.',
);
process.exit(1);
}

try {
// Generate secret
const secret = await this.generateSecret();

// Initialize SDK
const sdk = await this.initializeSdk(this.options.apiKey, secret);

// Fetch configuration
const config = await this.fetchConfiguration(sdk);

// Fetch public key
const publicKey = await this.fetchPublicKey(sdk);

// Generate ciphertext
const cipherText = this.generateCiphertext(secret, publicKey);

// Register ciphertext
const registration = await this.registerCiphertext(sdk, cipherText);

// Prepare .env file
this.prepareEnvFile(config, secret, this.options.apiKey);

// Save recovery file
this.saveRecoveryFile(registration);

console.log('Circle SDK setup completed successfully!');
} catch (error) {
console.error('Error during setup:', error);
process.exit(1);
}
}
}

async function main() {
const program = new Command();

program
.name('circle-sdk-setup')
.description('Setup tool for Circle SDK')
.requiredOption('-k, --api-key <key>', 'Circle API Key')
.parse(process.argv);

const options = program.opts();

const setup = new CircleSdkSetup({
apiKey: options.apiKey,
});

await setup.run();
}

main().catch((error) => {
console.error('Unhandled error:', error);
process.exit(1);
});
16 changes: 16 additions & 0 deletions packages/circle-sdk-setup/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es2020",
"module": "es2020",
"moduleResolution": "node",
"lib": ["es2020"],
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"]
}
7 changes: 7 additions & 0 deletions packages/web3-circle-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@
"name": "web3-circle-sdk",
"version": "0.1.0",
"description": "Web3 Circle SDK",
"type": "module",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"import": "./lib/index.js",
"types": "./lib/index.d.ts"
}
},
"homepage": "https://github.com/ChainSafe/web3-circle-libs#readme",
"bugs": {
"url": "https://github.com/ChainSafe/web3-circle-libs/issues"
Expand Down
Loading

0 comments on commit 2bcbe16

Please sign in to comment.