intuition-cli
Version:
A CLI for the Intuition protocol.
149 lines (148 loc) • 7.35 kB
JavaScript
import { intuitionDeployments } from '@0xintuition/protocol';
import { createAtomFromEthereumAccount, createAtomFromIpfsUri } from '@0xintuition/sdk';
import input from '@inquirer/input';
import select from '@inquirer/select';
import { Command, Flags } from '@oclif/core';
import chalk from 'chalk';
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { z } from 'zod';
import { getAccounts, getDefaultAccount, getDefaultNetwork } from '../../config.js';
import { base, baseSepolia, getNetworkByName } from '../../networks.js';
const AddressSchema = z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address format');
const IpfsUriSchema = z.string().regex(/^ipfs:\/\/[a-zA-Z0-9]+/, 'Invalid IPFS URI format (must start with ipfs://)');
export default class AtomCreate extends Command {
static description = 'Create a new atom on the blockchain.';
static examples = ['<%= config.bin %> <%= command.id %> --network base --deposit 0.01'];
static flags = {
deposit: Flags.string({
description: 'Deposit amount in ETH (optional)',
required: false,
}),
network: Flags.string({
description: 'Target network (base, base-sepolia)',
required: false,
}),
};
async run() {
const { flags } = await this.parse(AtomCreate);
try {
// Determine network
const networkName = flags.network || getDefaultNetwork() || 'base';
const network = getNetworkByName(networkName);
if (!network) {
this.log(chalk.red(`❌ Unsupported network: ${networkName}`));
this.log(chalk.gray('Supported: base, base-sepolia'));
return;
}
this.log(chalk.blue(`🌐 Using network: ${network.name}`));
// Get default account
const defaultAccountAddress = getDefaultAccount();
const accounts = getAccounts();
const defaultAccount = accounts.find((acc) => acc.address.toLowerCase() === (defaultAccountAddress || '').toLowerCase());
if (!defaultAccount) {
this.log(chalk.red('❌ No default account found.'));
this.log(chalk.gray('Set a default account: intuition account set-default <address>'));
this.log(chalk.gray('Or import an account: intuition account import <private-key>'));
return;
}
// Create public and wallet clients
const account = privateKeyToAccount(defaultAccount.privateKey);
const chain = network.id === 8453 ? base : baseSepolia;
const walletClient = createWalletClient({
account,
chain,
transport: http(),
});
// Use viem's createPublicClient for publicClient
const { createPublicClient } = await import('viem');
const publicClient = createPublicClient({
chain,
transport: http(),
});
// Get contract address
const chainId = network.id;
const contractAddress = intuitionDeployments.EthMultiVault?.[chainId];
if (!contractAddress) {
this.log(chalk.red(`❌ No contract deployment found for network: ${network.name}`));
return;
}
const atomConfig = {
address: contractAddress,
publicClient,
walletClient,
};
// Parse deposit
let depositAmount;
if (flags.deposit) {
const deposit = Number.parseFloat(flags.deposit);
if (Number.isNaN(deposit) || deposit < 0) {
this.log(chalk.red('❌ Invalid deposit amount'));
return;
}
depositAmount = BigInt(Math.floor(deposit * 10 ** 18));
}
// Prompt for atom type
const atomType = await select({
choices: [
{
description: 'Create an atom from an Ethereum address',
name: 'Ethereum Account',
value: 'ethereum-account',
},
{
description: 'Create an atom from an IPFS URI',
name: 'IPFS URI',
value: 'ipfs-uri',
},
],
message: 'Select atom type to create:',
});
if (atomType === 'ethereum-account') {
this.log(chalk.green('✅ Selected: Ethereum Account'));
const address = await input({
message: 'Enter Ethereum address:',
validate(value) {
const result = AddressSchema.safeParse(value);
return result.success ? true : result.error.issues[0].message;
},
});
this.log(chalk.blue(`📝 Creating atom for address: ${address}`));
this.log(chalk.yellow('⚠️ This will create an atom on the blockchain'));
const result = await createAtomFromEthereumAccount(atomConfig, address, depositAmount);
this.log(chalk.green('✅ Atom created successfully!'));
this.log(chalk.cyan(`Transaction Hash: ${result.transactionHash}`));
this.log(chalk.cyan(`URI: ${result.uri}`));
if (result.state) {
this.log(chalk.cyan(`Vault ID: ${result.state[0].args.vaultId}`));
this.log(chalk.cyan(`Shares: ${result.state[0].args.sharesForReceiver}`));
this.log(chalk.cyan(`Entry Fee: ${result.state[0].args.entryFee}`));
}
}
else if (atomType === 'ipfs-uri') {
this.log(chalk.green('✅ Selected: IPFS URI'));
const ipfsUri = await input({
message: 'Enter IPFS URI (e.g., ipfs://Qm...):',
validate(value) {
const result = IpfsUriSchema.safeParse(value);
return result.success ? true : result.error.issues[0].message;
},
});
this.log(chalk.blue(`📝 Creating atom for IPFS URI: ${ipfsUri}`));
this.log(chalk.yellow('⚠️ This will create an atom on the blockchain'));
const result = await createAtomFromIpfsUri(atomConfig, ipfsUri, depositAmount);
this.log(chalk.green('✅ Atom created successfully!'));
this.log(chalk.cyan(`Transaction Hash: ${result.transactionHash}`));
this.log(chalk.cyan(`URI: ${result.uri}`));
if (result.state) {
this.log(chalk.cyan(`Vault ID: ${result.state.vaultId}`));
this.log(chalk.cyan(`Shares: ${result.state.sharesForReceiver}`));
this.log(chalk.cyan(`Entry Fee: ${result.state.entryFee}`));
}
}
}
catch (error) {
this.log(chalk.red(`❌ Error: ${error instanceof Error ? error.message : String(error)}`));
}
}
}