UNPKG

arnacon-sdk

Version:

A comprehensive SDK for deploying and managing Arnacon smart contracts across multiple networks

429 lines (408 loc) 18.8 kB
#!/usr/bin/env node require('dotenv').config(); const fs = require('fs'); const path = require('path'); const ArnaconSDK = require('./index'); function print(msg) { process.stdout.write(String(msg) + '\n'); } function printError(msg) { process.stderr.write(String(msg) + '\n'); } function exitWithError(message, code = 1) { printError(`Error: ${message}`); process.exit(code); } function getFlagValue(names, argv) { for (const name of names) { const idx = argv.indexOf(name); if (idx !== -1) { if (idx + 1 >= argv.length || argv[idx + 1].startsWith('-')) return ''; return argv[idx + 1]; } // Support --key=value style const withEq = argv.find(arg => arg.startsWith(name + '=')); if (withEq) return withEq.slice(name.length + 1); } return undefined; } function flagExists(name, argv) { return argv.includes(name); } function parseJsonInput(input) { if (!input) return undefined; try { const abs = path.isAbsolute(input) ? input : path.join(process.cwd(), input); if (fs.existsSync(abs) && fs.statSync(abs).isFile()) { const content = fs.readFileSync(abs, 'utf8'); return JSON.parse(content); } } catch (_) { // fallthrough to try parsing as JSON } try { return JSON.parse(input); } catch (e) { exitWithError(`Failed to parse JSON input. Provide a valid JSON string or a path to a JSON file. Received: ${input}`); } } function usage() { print(`Arnacon CLI Usage: arnacon --private-key <hex> --rpc-url <url> <command> [options] Auth options (or use env SECNUM_PRIVATE_KEY / RPC_URL): -k, --private-key <hex> Private key (0x...) -r, --rpc-url <url> JSON-RPC URL Commands: deploy-contracts [--tld <tld>] deploy-gsm register-sp --name <serviceProviderName> purchase-name --name <serviceProviderName> [--days <n>] get-owner --name <ensName> get-ens-owner --name <ensName> get-wrapper-owner --name <ensName> register-subdomain --owner <address> --label <label> --name <serviceProviderName> get-second-level-controller get-second-level-interactor get-product-registry --name <serviceProviderName> get-product-types --name <serviceProviderName> get-all-product-types --name <serviceProviderName> get-all-products --name <serviceProviderName> get-products-from-contract --address <nftContract> get-nft-contracts --name <serviceProviderName> create-product-type --name <serviceProviderName> --type <productType> --info <jsonOrPath> create-product --ens <ensName> --type <productType> --info <jsonOrPath> batch-create-product --ens <ensName> --info <jsonArrayOrPath> --types <jsonArrayOrComma> create-provision --name <serviceProviderName> --identifier <id> --key <key> --info <jsonOrPath> create-record --name <serviceProviderName> --key <key> --value <value> get-data --ens <ensName> get-provision --name <serviceProviderName> --identifier <id> get-expiry --name <ensName> get-resolver --ens <ensName> get-ttl --ens <ensName> get-registered-names --address <address> get-all-addresses get-address --contract <contractName> Examples: arnacon -k 0xabc -r https://rpc.test deploy-contracts --tld global arnacon -k 0xabc -r https://rpc.test register-sp --name cellact arnacon -k 0xabc -r https://rpc.test create-provision --name cellact --identifier sbc --key sbc_v_1 --info provision.json `); } async function main() { const argv = process.argv.slice(2); if (argv.length === 0 || flagExists('-h', argv) || flagExists('--help', argv)) { usage(); process.exit(0); } const privateKey = getFlagValue(['--private-key', '-k'], argv) || process.env.SECNUM_PRIVATE_KEY; const rpcUrl = getFlagValue(['--rpc-url', '-r'], argv) || process.env.RPC_URL; if (!privateKey) exitWithError('Missing private key. Use --private-key or set SECNUM_PRIVATE_KEY'); if (!rpcUrl) exitWithError('Missing RPC URL. Use --rpc-url or set RPC_URL'); const sdk = new ArnaconSDK(privateKey, rpcUrl); // Find command as the first token that is not a flag or a flag's value function findCommandIndex(tokens) { let i = 0; while (i < tokens.length) { const t = tokens[i]; if (t.startsWith('-')) { // --flag=value style if (t.includes('=')) { i += 1; continue; } // Skip the next token if it's a value (doesn't start with '-') if (i + 1 < tokens.length && !tokens[i + 1].startsWith('-')) { i += 2; } else { i += 1; } continue; } return i; } return -1; } const commandIdx = findCommandIndex(argv); if (commandIdx === -1) { usage(); process.exit(1); } const command = argv[commandIdx]; const args = argv.slice(commandIdx + 1); try { switch (command) { case 'deploy-contracts': { const tld = getFlagValue(['--tld'], args) || 'global'; print(`Deploying core contracts for TLD: ${tld} ...`); const res = await sdk.deployContracts(tld); const count = res && typeof res === 'object' ? Object.keys(res).length : 0; print(`✅ Core contracts deployed (${count}) for .${tld}`); print(JSON.stringify(res, null, 2)); break; } case 'deploy-gsm': { print('Deploying GSM contracts ...'); const res = await sdk.deployGSM(); const count = res && typeof res === 'object' ? Object.keys(res).length : 0; print(`✅ GSM contracts deployed (${count})`); print(JSON.stringify(res, null, 2)); break; } case 'register-sp': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.registerAsServiceProvider(name); print(`✅ Service provider registered: ${name}`); print(JSON.stringify(res, null, 2)); break; } case 'purchase-name': { const name = getFlagValue(['--name'], args); const daysStr = getFlagValue(['--days'], args); const days = daysStr ? Number(daysStr) : 365; if (!name) exitWithError('Missing --name'); print(`Purchasing name ${name} for ${days} days ...`); const res = await sdk.purchaseName(name, days); print(`✅ Purchased name: ${name} for ${days} days`); print(JSON.stringify(res, null, 2)); break; } case 'get-owner': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.getOwner(name); print(`Owner of ${name}: ${res}`); break; } case 'get-ens-owner': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.getEnsOwner(name); print(`ENS owner of ${name}: ${res}`); break; } case 'get-wrapper-owner': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.getWrapperOwner(name); print(`Wrapper owner of ${name}: ${res}`); break; } case 'register-subdomain': { const owner = getFlagValue(['--owner'], args); const label = getFlagValue(['--label'], args); const name = getFlagValue(['--name'], args); if (!owner || !label || !name) exitWithError('Missing --owner, --label, or --name'); const res = await sdk.registerSubdomain(owner, label, name); print(`✅ Subdomain registered: ${label}.${name}${owner}`); print(JSON.stringify(res, null, 2)); break; } case 'get-second-level-controller': { const res = await sdk.getSecondLevelController(); print(`Second level controller: ${res}`); break; } case 'get-second-level-interactor': { const res = await sdk.getSecondLevelInteractor(); print(`Second level interactor: ${res}`); break; } case 'get-product-registry': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.getProductRegistry(name); print(`Product registry for ${name}:`); print(JSON.stringify(res, null, 2)); break; } case 'get-product-types': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.getProductTypes(name); const count = Array.isArray(res) ? res.length : 0; print(`Product types for ${name}: ${count}`); print(JSON.stringify(res, null, 2)); break; } case 'get-all-products': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.getAllProducts(name); const count = Array.isArray(res) ? res.length : (res && typeof res === 'object' ? Object.keys(res).length : 0); print(`All products for ${name}: ${count}`); print(JSON.stringify(res, null, 2)); break; } case 'get-all-product-types': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.getAllProductTypesWithMetadata(name); const count = Array.isArray(res) ? res.length : (res && typeof res === 'object' ? Object.keys(res).length : 0); print(`All product types with metadata for ${name}: ${count}`); print(JSON.stringify(res, null, 2)); break; } case 'get-products-from-contract': { const address = getFlagValue(['--address'], args); if (!address) exitWithError('Missing --address'); const res = await sdk.getProductsFromContract(address); const count = Array.isArray(res) ? res.length : 0; print(`Products from contract ${address}: ${count}`); print(JSON.stringify(res, null, 2)); break; } case 'get-nft-contracts': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.getNFTContracts(name); const count = Array.isArray(res) ? res.length : 0; print(`NFT contracts for ${name}: ${count}`); print(JSON.stringify(res, null, 2)); break; } case 'create-product-type': { const name = getFlagValue(['--name'], args); const type = getFlagValue(['--type'], args); const infoRaw = getFlagValue(['--info'], args); if (!name || !type || !infoRaw) exitWithError('Missing --name, --type, or --info'); const info = parseJsonInput(infoRaw); const res = await sdk.createProductType(name, type, info); print(`✅ Created product type '${type}' for ${name}`); print(JSON.stringify(res, null, 2)); break; } case 'create-product': { const ens = getFlagValue(['--ens'], args); const type = getFlagValue(['--type'], args); const infoRaw = getFlagValue(['--info'], args); if (!ens || !type || !infoRaw) exitWithError('Missing --ens, --type, or --info'); const info = parseJsonInput(infoRaw); const res = await sdk.createProduct(ens, info, type); print(`✅ Created product on ${ens} (type: ${type})`); print(JSON.stringify(res, null, 2)); break; } case 'batch-create-product': { const ens = getFlagValue(['--ens'], args); const infoRaw = getFlagValue(['--info'], args); const typesRaw = getFlagValue(['--types'], args); if (!ens || !infoRaw || !typesRaw) exitWithError('Missing --ens, --info, or --types'); const productInfos = parseJsonInput(infoRaw); let productTypes; try { // Try JSON first productTypes = parseJsonInput(typesRaw); } catch (_) { // Fallback comma-separated productTypes = String(typesRaw).split(',').map(s => s.trim()).filter(Boolean); } if (!Array.isArray(productInfos) || !Array.isArray(productTypes)) { exitWithError('--info and --types must be arrays'); } const res = await sdk.batchCreateProduct(ens, productInfos, productTypes); print(`✅ Batch created ${productInfos.length} products on ${ens}`); print(JSON.stringify(res, null, 2)); break; } case 'create-provision': { const name = getFlagValue(['--name'], args); const identifier = getFlagValue(['--identifier'], args); const key = getFlagValue(['--key'], args); const infoRaw = getFlagValue(['--info'], args); if (!name || !identifier || !key || !infoRaw) { exitWithError('Missing --name, --identifier, --key, or --info'); } const info = parseJsonInput(infoRaw); const res = await sdk.createProvisionAndUpdateResolver(key, info, name, identifier); print(`✅ Provision created for ${name} [${identifier}] (key: ${key})`); print(JSON.stringify(res, null, 2)); break; } case 'create-record': { const name = getFlagValue(['--name'], args); const key = getFlagValue(['--key'], args); const value = getFlagValue(['--value'], args); if (!name || !key || typeof value === 'undefined') exitWithError('Missing --name, --key, or --value'); const res = await sdk.createRecord(key, value, name); print(`✅ Record created for ${name}: ${key}=${value}`); print(JSON.stringify(res, null, 2)); break; } case 'get-data': { const ens = getFlagValue(['--ens'], args); if (!ens) exitWithError('Missing --ens'); const res = await sdk.getData(ens); print(`Data for ${ens}:`); print(JSON.stringify(res, null, 2)); break; } case 'get-provision': { const name = getFlagValue(['--name'], args); const identifier = getFlagValue(['--identifier'], args); if (!name || !identifier) exitWithError('Missing --name or --identifier'); const res = await sdk.getProvision(name, identifier); print(`Provision for ${name} [${identifier}]:`); print(JSON.stringify(res, null, 2)); break; } case 'get-expiry': { const name = getFlagValue(['--name'], args); if (!name) exitWithError('Missing --name'); const res = await sdk.getExpiry(name); const date = new Date(res * 1000); print("Expiry date: " + date.toLocaleString()); break; } case 'get-resolver': { const ens = getFlagValue(['--ens'], args); if (!ens) exitWithError('Missing --ens'); const res = await sdk.getResolver(ens); if (typeof res === 'string') { print(`Resolver for ${ens}: ${res}`); } else { print(`Resolver for ${ens}:`); } print(JSON.stringify(res, null, 2)); break; } case 'get-ttl': { const ens = getFlagValue(['--ens'], args); if (!ens) exitWithError('Missing --ens'); const res = await sdk.getTTL(ens); print(`TTL for ${ens}: ${String(res)}`); break; } case 'get-registered-names': { const address = getFlagValue(['--address'], args); if (!address) exitWithError('Missing --address'); const res = await sdk.getRegisteredNames(address); const count = Array.isArray(res) ? res.length : 0; print(`Registered names for ${address}: ${count}`); print(JSON.stringify(res, null, 2)); break; } case 'get-all-addresses': { const res = sdk.getAllContractAddresses(); const count = res && typeof res === 'object' ? Object.keys(res).length : 0; print(`All deployed addresses (${count}):`); print(JSON.stringify(res, null, 2)); break; } case 'get-address': { const contract = getFlagValue(['--contract'], args); if (!contract) exitWithError('Missing --contract'); const res = sdk.getContractAddress(contract); print(`Address for ${contract}: ${String(res || 'not found')}`); break; } default: usage(); process.exit(1); } } catch (err) { exitWithError(err && err.message ? err.message : String(err)); } } main();