-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: cli tool to setup the sdk and circle console
- Loading branch information
Showing
7 changed files
with
467 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
dist/ | ||
*.log | ||
circle-recovery.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 (`circle-recovery.json`) | ||
|
||
⚠️ **Important**: After setup, store the `circle-recovery.json` 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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
#!/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) { | ||
console.log('Saving recovery file...'); | ||
const recoveryPath = path.resolve(process.cwd(), 'circle-recovery.json'); | ||
fs.writeFileSync(recoveryPath, JSON.stringify(registration, null, 2)); | ||
console.log('Recovery file saved'); | ||
} | ||
|
||
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!'); | ||
console.log('Configuration saved to .env file'); | ||
console.log('Recovery information saved to circle-recovery.json'); | ||
console.log( | ||
'\nIMPORTANT: Keep circle-recovery.json in a secure location and then remove it from this directory', | ||
); | ||
} 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); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.