gnoman 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +24 -0
- package/.gnoman/contracts.json +4094 -0
- package/.gnoman/exec_package/runtime-debug.jsonl +45 -0
- package/.gnoman/holds.sqlite +0 -0
- package/.gnoman/license.json +7 -0
- package/.gnoman/safes.json +37 -0
- package/.gnoman/vanity-jobs.json +67 -0
- package/.gnoman/wallets.db +0 -0
- package/.prettierrc.json +6 -0
- package/CODex_TASKS.md +124 -0
- package/LICENSE.md +164 -0
- package/README.md +95 -0
- package/assets/GnoLogo.jpg +0 -0
- package/assets/self.png +0 -0
- package/backend/controllers/contractController.ts +49 -0
- package/backend/controllers/devToolsController.ts +76 -0
- package/backend/controllers/etherscanController.ts +59 -0
- package/backend/controllers/historyController.ts +7 -0
- package/backend/controllers/keyringController.ts +134 -0
- package/backend/controllers/robinhoodController.ts +80 -0
- package/backend/controllers/safeController.ts +167 -0
- package/backend/controllers/sandboxController.ts +63 -0
- package/backend/controllers/settingsController.ts +38 -0
- package/backend/controllers/walletController.ts +151 -0
- package/backend/index.ts +133 -0
- package/backend/licenses/license_public.pem +3 -0
- package/backend/licenses/verify_license.py +43 -0
- package/backend/routes/contractRoutes.ts +11 -0
- package/backend/routes/devToolsRoutes.ts +11 -0
- package/backend/routes/etherscanRoutes.ts +11 -0
- package/backend/routes/historyRoutes.ts +8 -0
- package/backend/routes/keyringRoutes.ts +25 -0
- package/backend/routes/licenseRoutes.ts +35 -0
- package/backend/routes/robinhoodRoutes.ts +22 -0
- package/backend/routes/runtimeRoutes.ts +29 -0
- package/backend/routes/safeRoutes.ts +28 -0
- package/backend/routes/sandboxRoutes.ts +17 -0
- package/backend/routes/settingsRoutes.ts +14 -0
- package/backend/routes/walletRoutes.ts +21 -0
- package/backend/services/chainlinkService.ts +65 -0
- package/backend/services/contractRegistryService.ts +205 -0
- package/backend/services/devToolsService.ts +251 -0
- package/backend/services/diagnosticsService.ts +350 -0
- package/backend/services/etherscanService.ts +152 -0
- package/backend/services/historyService.ts +89 -0
- package/backend/services/keyringAccessor.ts +4 -0
- package/backend/services/licenseService.ts +163 -0
- package/backend/services/onchain/abiRegistry.ts +57 -0
- package/backend/services/onchain/chainlinkClient.ts +56 -0
- package/backend/services/onchain/errors.ts +16 -0
- package/backend/services/onchain/etherscanClient.ts +94 -0
- package/backend/services/onchain/index.ts +76 -0
- package/backend/services/onchain/tenderlyRpcClient.ts +74 -0
- package/backend/services/onchain/types.ts +33 -0
- package/backend/services/onchainAutomationService.ts +424 -0
- package/backend/services/robinhood/auth.ts +42 -0
- package/backend/services/robinhood/client.ts +123 -0
- package/backend/services/robinhood/integrationService.ts +140 -0
- package/backend/services/robinhood/provider.ts +22 -0
- package/backend/services/robinhood/unofficialClient.ts +66 -0
- package/backend/services/rpcService.ts +44 -0
- package/backend/services/runtimeTelemetryService.ts +158 -0
- package/backend/services/safeConfigRepository.ts +205 -0
- package/backend/services/safeService.ts +588 -0
- package/backend/services/sandboxService.ts +157 -0
- package/backend/services/secureSettingsService.ts +45 -0
- package/backend/services/transactionHoldService.ts +223 -0
- package/backend/services/vanityService.ts +293 -0
- package/backend/services/walletService.ts +290 -0
- package/backend/services/walletStore.ts +179 -0
- package/backend/types/express-async-handler.d.ts +13 -0
- package/backend/types/keyring.d.ts +19 -0
- package/backend/utils/abiResolver.ts +208 -0
- package/backend/utils/http.ts +6 -0
- package/backend/utils/secretsResolver.ts +150 -0
- package/backend/utils/signer.ts +11 -0
- package/backend/workers/vanityWorker.ts +76 -0
- package/capacitor.config.ts +13 -0
- package/cli/gnoman.ts +424 -0
- package/contracts/OracleConsumer.sol +20 -0
- package/contracts/PriceFeedConsumer.sol +22 -0
- package/dist/backend/backend/controllers/contractController.js +41 -0
- package/dist/backend/backend/controllers/contractController.js.map +1 -0
- package/dist/backend/backend/controllers/devToolsController.js +63 -0
- package/dist/backend/backend/controllers/devToolsController.js.map +1 -0
- package/dist/backend/backend/controllers/etherscanController.js +53 -0
- package/dist/backend/backend/controllers/etherscanController.js.map +1 -0
- package/dist/backend/backend/controllers/historyController.js +12 -0
- package/dist/backend/backend/controllers/historyController.js.map +1 -0
- package/dist/backend/backend/controllers/keyringController.js +126 -0
- package/dist/backend/backend/controllers/keyringController.js.map +1 -0
- package/dist/backend/backend/controllers/robinhoodController.js +69 -0
- package/dist/backend/backend/controllers/robinhoodController.js.map +1 -0
- package/dist/backend/backend/controllers/safeController.js +137 -0
- package/dist/backend/backend/controllers/safeController.js.map +1 -0
- package/dist/backend/backend/controllers/sandboxController.js +48 -0
- package/dist/backend/backend/controllers/sandboxController.js.map +1 -0
- package/dist/backend/backend/controllers/settingsController.js +34 -0
- package/dist/backend/backend/controllers/settingsController.js.map +1 -0
- package/dist/backend/backend/controllers/walletController.js +140 -0
- package/dist/backend/backend/controllers/walletController.js.map +1 -0
- package/dist/backend/backend/index.js +119 -0
- package/dist/backend/backend/index.js.map +1 -0
- package/dist/backend/backend/routes/contractRoutes.js +44 -0
- package/dist/backend/backend/routes/contractRoutes.js.map +1 -0
- package/dist/backend/backend/routes/devToolsRoutes.js +44 -0
- package/dist/backend/backend/routes/devToolsRoutes.js.map +1 -0
- package/dist/backend/backend/routes/etherscanRoutes.js +44 -0
- package/dist/backend/backend/routes/etherscanRoutes.js.map +1 -0
- package/dist/backend/backend/routes/historyRoutes.js +41 -0
- package/dist/backend/backend/routes/historyRoutes.js.map +1 -0
- package/dist/backend/backend/routes/keyringRoutes.js +18 -0
- package/dist/backend/backend/routes/keyringRoutes.js.map +1 -0
- package/dist/backend/backend/routes/licenseRoutes.js +30 -0
- package/dist/backend/backend/routes/licenseRoutes.js.map +1 -0
- package/dist/backend/backend/routes/robinhoodRoutes.js +14 -0
- package/dist/backend/backend/routes/robinhoodRoutes.js.map +1 -0
- package/dist/backend/backend/routes/runtimeRoutes.js +26 -0
- package/dist/backend/backend/routes/runtimeRoutes.js.map +1 -0
- package/dist/backend/backend/routes/safeRoutes.js +61 -0
- package/dist/backend/backend/routes/safeRoutes.js.map +1 -0
- package/dist/backend/backend/routes/sandboxRoutes.js +50 -0
- package/dist/backend/backend/routes/sandboxRoutes.js.map +1 -0
- package/dist/backend/backend/routes/settingsRoutes.js +10 -0
- package/dist/backend/backend/routes/settingsRoutes.js.map +1 -0
- package/dist/backend/backend/routes/walletRoutes.js +54 -0
- package/dist/backend/backend/routes/walletRoutes.js.map +1 -0
- package/dist/backend/backend/services/chainlinkService.js +48 -0
- package/dist/backend/backend/services/chainlinkService.js.map +1 -0
- package/dist/backend/backend/services/contractRegistryService.js +138 -0
- package/dist/backend/backend/services/contractRegistryService.js.map +1 -0
- package/dist/backend/backend/services/devToolsService.js +213 -0
- package/dist/backend/backend/services/devToolsService.js.map +1 -0
- package/dist/backend/backend/services/diagnosticsService.js +286 -0
- package/dist/backend/backend/services/diagnosticsService.js.map +1 -0
- package/dist/backend/backend/services/etherscanService.js +125 -0
- package/dist/backend/backend/services/etherscanService.js.map +1 -0
- package/dist/backend/backend/services/historyService.js +75 -0
- package/dist/backend/backend/services/historyService.js.map +1 -0
- package/dist/backend/backend/services/keyringAccessor.js +40 -0
- package/dist/backend/backend/services/keyringAccessor.js.map +1 -0
- package/dist/backend/backend/services/licenseService.js +130 -0
- package/dist/backend/backend/services/licenseService.js.map +1 -0
- package/dist/backend/backend/services/onchain/abiRegistry.js +47 -0
- package/dist/backend/backend/services/onchain/abiRegistry.js.map +1 -0
- package/dist/backend/backend/services/onchain/chainlinkClient.js +43 -0
- package/dist/backend/backend/services/onchain/chainlinkClient.js.map +1 -0
- package/dist/backend/backend/services/onchain/errors.js +13 -0
- package/dist/backend/backend/services/onchain/errors.js.map +1 -0
- package/dist/backend/backend/services/onchain/etherscanClient.js +82 -0
- package/dist/backend/backend/services/onchain/etherscanClient.js.map +1 -0
- package/dist/backend/backend/services/onchain/index.js +79 -0
- package/dist/backend/backend/services/onchain/index.js.map +1 -0
- package/dist/backend/backend/services/onchain/tenderlyRpcClient.js +60 -0
- package/dist/backend/backend/services/onchain/tenderlyRpcClient.js.map +1 -0
- package/dist/backend/backend/services/onchain/types.js +14 -0
- package/dist/backend/backend/services/onchain/types.js.map +1 -0
- package/dist/backend/backend/services/onchainAutomationService.js +316 -0
- package/dist/backend/backend/services/onchainAutomationService.js.map +1 -0
- package/dist/backend/backend/services/robinhood/auth.js +26 -0
- package/dist/backend/backend/services/robinhood/auth.js.map +1 -0
- package/dist/backend/backend/services/robinhood/client.js +73 -0
- package/dist/backend/backend/services/robinhood/client.js.map +1 -0
- package/dist/backend/backend/services/robinhood/integrationService.js +119 -0
- package/dist/backend/backend/services/robinhood/integrationService.js.map +1 -0
- package/dist/backend/backend/services/robinhood/provider.js +17 -0
- package/dist/backend/backend/services/robinhood/provider.js.map +1 -0
- package/dist/backend/backend/services/robinhood/unofficialClient.js +61 -0
- package/dist/backend/backend/services/robinhood/unofficialClient.js.map +1 -0
- package/dist/backend/backend/services/rpcService.js +48 -0
- package/dist/backend/backend/services/rpcService.js.map +1 -0
- package/dist/backend/backend/services/runtimeTelemetryService.js +96 -0
- package/dist/backend/backend/services/runtimeTelemetryService.js.map +1 -0
- package/dist/backend/backend/services/safeConfigRepository.js +147 -0
- package/dist/backend/backend/services/safeConfigRepository.js.map +1 -0
- package/dist/backend/backend/services/safeService.js +527 -0
- package/dist/backend/backend/services/safeService.js.map +1 -0
- package/dist/backend/backend/services/sandboxService.js +135 -0
- package/dist/backend/backend/services/sandboxService.js.map +1 -0
- package/dist/backend/backend/services/secureSettingsService.js +50 -0
- package/dist/backend/backend/services/secureSettingsService.js.map +1 -0
- package/dist/backend/backend/services/transactionHoldService.js +184 -0
- package/dist/backend/backend/services/transactionHoldService.js.map +1 -0
- package/dist/backend/backend/services/vanityService.js +235 -0
- package/dist/backend/backend/services/vanityService.js.map +1 -0
- package/dist/backend/backend/services/walletService.js +202 -0
- package/dist/backend/backend/services/walletService.js.map +1 -0
- package/dist/backend/backend/services/walletStore.js +132 -0
- package/dist/backend/backend/services/walletStore.js.map +1 -0
- package/dist/backend/backend/utils/abiResolver.js +182 -0
- package/dist/backend/backend/utils/abiResolver.js.map +1 -0
- package/dist/backend/backend/utils/http.js +12 -0
- package/dist/backend/backend/utils/http.js.map +1 -0
- package/dist/backend/backend/utils/secretsResolver.js +137 -0
- package/dist/backend/backend/utils/secretsResolver.js.map +1 -0
- package/dist/backend/backend/utils/signer.js +15 -0
- package/dist/backend/backend/utils/signer.js.map +1 -0
- package/dist/backend/backend/workers/vanityWorker.js +63 -0
- package/dist/backend/backend/workers/vanityWorker.js.map +1 -0
- package/dist/backend/cli/gnoman.js +387 -0
- package/dist/backend/cli/gnoman.js.map +1 -0
- package/dist/backend/modules/sandbox/abiLoader.js +78 -0
- package/dist/backend/modules/sandbox/abiLoader.js.map +1 -0
- package/dist/backend/modules/sandbox/contractSimulator.js +205 -0
- package/dist/backend/modules/sandbox/contractSimulator.js.map +1 -0
- package/dist/backend/modules/sandbox/formBuilder.js +14 -0
- package/dist/backend/modules/sandbox/formBuilder.js.map +1 -0
- package/dist/backend/modules/sandbox/index.js +24 -0
- package/dist/backend/modules/sandbox/index.js.map +1 -0
- package/dist/backend/modules/sandbox/localFork.js +103 -0
- package/dist/backend/modules/sandbox/localFork.js.map +1 -0
- package/dist/backend/modules/sandbox/sandboxManager.js +130 -0
- package/dist/backend/modules/sandbox/sandboxManager.js.map +1 -0
- package/dist/backend/modules/sandbox/types.js +3 -0
- package/dist/backend/modules/sandbox/types.js.map +1 -0
- package/dist/backend/src/core/backends/fileBackend.js +136 -0
- package/dist/backend/src/core/backends/fileBackend.js.map +1 -0
- package/dist/backend/src/core/backends/memoryBackend.js +26 -0
- package/dist/backend/src/core/backends/memoryBackend.js.map +1 -0
- package/dist/backend/src/core/backends/systemBackend.js +86 -0
- package/dist/backend/src/core/backends/systemBackend.js.map +1 -0
- package/dist/backend/src/core/backends/types.js +12 -0
- package/dist/backend/src/core/backends/types.js.map +1 -0
- package/dist/backend/src/core/keyringManager.js +178 -0
- package/dist/backend/src/core/keyringManager.js.map +1 -0
- package/dist/backend/src/utils/abiResolver.js +180 -0
- package/dist/backend/src/utils/abiResolver.js.map +1 -0
- package/dist/backend/src/utils/runtimeObservability.js +78 -0
- package/dist/backend/src/utils/runtimeObservability.js.map +1 -0
- package/dist/backend/src/utils/secretsResolver.js +138 -0
- package/dist/backend/src/utils/secretsResolver.js.map +1 -0
- package/dist/cli/backend/services/diagnosticsService.js +286 -0
- package/dist/cli/backend/services/diagnosticsService.js.map +1 -0
- package/dist/cli/backend/services/keyringAccessor.js +40 -0
- package/dist/cli/backend/services/keyringAccessor.js.map +1 -0
- package/dist/cli/backend/services/rpcService.js +48 -0
- package/dist/cli/backend/services/rpcService.js.map +1 -0
- package/dist/cli/backend/services/runtimeTelemetryService.js +96 -0
- package/dist/cli/backend/services/runtimeTelemetryService.js.map +1 -0
- package/dist/cli/backend/services/walletService.js +202 -0
- package/dist/cli/backend/services/walletService.js.map +1 -0
- package/dist/cli/backend/services/walletStore.js +132 -0
- package/dist/cli/backend/services/walletStore.js.map +1 -0
- package/dist/cli/backend/utils/http.js +12 -0
- package/dist/cli/backend/utils/http.js.map +1 -0
- package/dist/cli/backend/utils/secretsResolver.js +137 -0
- package/dist/cli/backend/utils/secretsResolver.js.map +1 -0
- package/dist/cli/cli/gnoman.js +387 -0
- package/dist/cli/cli/gnoman.js.map +1 -0
- package/dist/cli/src/core/backends/fileBackend.js +136 -0
- package/dist/cli/src/core/backends/fileBackend.js.map +1 -0
- package/dist/cli/src/core/backends/memoryBackend.js +26 -0
- package/dist/cli/src/core/backends/memoryBackend.js.map +1 -0
- package/dist/cli/src/core/backends/systemBackend.js +86 -0
- package/dist/cli/src/core/backends/systemBackend.js.map +1 -0
- package/dist/cli/src/core/backends/types.js +12 -0
- package/dist/cli/src/core/backends/types.js.map +1 -0
- package/dist/cli/src/core/keyringManager.js +178 -0
- package/dist/cli/src/core/keyringManager.js.map +1 -0
- package/dist/cli/src/utils/abiResolver.js +180 -0
- package/dist/cli/src/utils/abiResolver.js.map +1 -0
- package/dist/cli/src/utils/runtimeObservability.js +78 -0
- package/dist/cli/src/utils/runtimeObservability.js.map +1 -0
- package/dist/cli/src/utils/secretsResolver.js +138 -0
- package/dist/cli/src/utils/secretsResolver.js.map +1 -0
- package/dist/main/backend/services/keyringAccessor.js +40 -0
- package/dist/main/backend/services/keyringAccessor.js.map +1 -0
- package/dist/main/backend/utils/http.js +12 -0
- package/dist/main/backend/utils/http.js.map +1 -0
- package/dist/main/main/ipcHandlers/index.js +26 -0
- package/dist/main/main/ipcHandlers/index.js.map +1 -0
- package/dist/main/main/keyring/keyringmanager.js +101 -0
- package/dist/main/main/keyring/keyringmanager.js.map +1 -0
- package/dist/main/main/main.js +224 -0
- package/dist/main/main/main.js.map +1 -0
- package/dist/main/main/preload/index.js +19 -0
- package/dist/main/main/preload/index.js.map +1 -0
- package/dist/main/main/preload/licenseBridge.js +105 -0
- package/dist/main/main/preload/licenseBridge.js.map +1 -0
- package/dist/main/src/core/backends/fileBackend.js +136 -0
- package/dist/main/src/core/backends/fileBackend.js.map +1 -0
- package/dist/main/src/core/backends/memoryBackend.js +26 -0
- package/dist/main/src/core/backends/memoryBackend.js.map +1 -0
- package/dist/main/src/core/backends/systemBackend.js +86 -0
- package/dist/main/src/core/backends/systemBackend.js.map +1 -0
- package/dist/main/src/core/backends/types.js +12 -0
- package/dist/main/src/core/backends/types.js.map +1 -0
- package/dist/main/src/core/keyringManager.js +178 -0
- package/dist/main/src/core/keyringManager.js.map +1 -0
- package/dist/main/src/utils/abiResolver.js +180 -0
- package/dist/main/src/utils/abiResolver.js.map +1 -0
- package/dist/main/src/utils/runtimeObservability.js +78 -0
- package/dist/main/src/utils/runtimeObservability.js.map +1 -0
- package/dist/main/src/utils/secretsResolver.js +138 -0
- package/dist/main/src/utils/secretsResolver.js.map +1 -0
- package/docs/development-guide.md +203 -0
- package/docs/etherscan-chainlink-integration.md +44 -0
- package/docs/gnoman-20-user-manual-STANDARD-PRINT-READY.pdf +0 -0
- package/docs/gnoman-20-user-manual-STANDARD.pdf +0 -0
- package/docs/license-dev-guide.md +106 -0
- package/docs/robinhood-integration.md +30 -0
- package/docs/system-audit-gpt-guide.md +208 -0
- package/docs/system-robustness-audit.md +50 -0
- package/docs/user-guide.md +73 -0
- package/docs/wiki/development-guide.md +203 -0
- package/docs/wiki/license-dev-guide.md +106 -0
- package/docs/wiki/user-guide.md +73 -0
- package/eslint.config.js +85 -0
- package/gnoman2.0/.eslintrc.cjs +24 -0
- package/gnoman2.0/.prettierrc.json +6 -0
- package/gnoman2.0/CODex_TASKS.md +124 -0
- package/gnoman2.0/LICENSE.md +164 -0
- package/gnoman2.0/README.md +95 -0
- package/gnoman2.0/assets/GnoLogo.jpg +0 -0
- package/gnoman2.0/assets/self.png +0 -0
- package/gnoman2.0/backend/controllers/contractController.ts +49 -0
- package/gnoman2.0/backend/controllers/devToolsController.ts +76 -0
- package/gnoman2.0/backend/controllers/etherscanController.ts +59 -0
- package/gnoman2.0/backend/controllers/historyController.ts +7 -0
- package/gnoman2.0/backend/controllers/keyringController.ts +134 -0
- package/gnoman2.0/backend/controllers/robinhoodController.ts +80 -0
- package/gnoman2.0/backend/controllers/safeController.ts +167 -0
- package/gnoman2.0/backend/controllers/sandboxController.ts +63 -0
- package/gnoman2.0/backend/controllers/settingsController.ts +38 -0
- package/gnoman2.0/backend/controllers/walletController.ts +151 -0
- package/gnoman2.0/backend/index.ts +133 -0
- package/gnoman2.0/backend/licenses/license_public.pem +3 -0
- package/gnoman2.0/backend/licenses/verify_license.py +43 -0
- package/gnoman2.0/backend/routes/contractRoutes.ts +11 -0
- package/gnoman2.0/backend/routes/devToolsRoutes.ts +11 -0
- package/gnoman2.0/backend/routes/etherscanRoutes.ts +11 -0
- package/gnoman2.0/backend/routes/historyRoutes.ts +8 -0
- package/gnoman2.0/backend/routes/keyringRoutes.ts +25 -0
- package/gnoman2.0/backend/routes/licenseRoutes.ts +35 -0
- package/gnoman2.0/backend/routes/robinhoodRoutes.ts +22 -0
- package/gnoman2.0/backend/routes/runtimeRoutes.ts +29 -0
- package/gnoman2.0/backend/routes/safeRoutes.ts +28 -0
- package/gnoman2.0/backend/routes/sandboxRoutes.ts +17 -0
- package/gnoman2.0/backend/routes/settingsRoutes.ts +14 -0
- package/gnoman2.0/backend/routes/walletRoutes.ts +21 -0
- package/gnoman2.0/backend/services/chainlinkService.ts +65 -0
- package/gnoman2.0/backend/services/contractRegistryService.ts +205 -0
- package/gnoman2.0/backend/services/devToolsService.ts +251 -0
- package/gnoman2.0/backend/services/diagnosticsService.ts +350 -0
- package/gnoman2.0/backend/services/etherscanService.ts +152 -0
- package/gnoman2.0/backend/services/historyService.ts +89 -0
- package/gnoman2.0/backend/services/keyringAccessor.ts +4 -0
- package/gnoman2.0/backend/services/licenseService.ts +163 -0
- package/gnoman2.0/backend/services/onchain/abiRegistry.ts +57 -0
- package/gnoman2.0/backend/services/onchain/chainlinkClient.ts +56 -0
- package/gnoman2.0/backend/services/onchain/errors.ts +16 -0
- package/gnoman2.0/backend/services/onchain/etherscanClient.ts +94 -0
- package/gnoman2.0/backend/services/onchain/index.ts +76 -0
- package/gnoman2.0/backend/services/onchain/tenderlyRpcClient.ts +74 -0
- package/gnoman2.0/backend/services/onchain/types.ts +33 -0
- package/gnoman2.0/backend/services/onchainAutomationService.ts +424 -0
- package/gnoman2.0/backend/services/robinhood/auth.ts +42 -0
- package/gnoman2.0/backend/services/robinhood/client.ts +123 -0
- package/gnoman2.0/backend/services/robinhood/integrationService.ts +140 -0
- package/gnoman2.0/backend/services/robinhood/provider.ts +22 -0
- package/gnoman2.0/backend/services/robinhood/unofficialClient.ts +66 -0
- package/gnoman2.0/backend/services/rpcService.ts +44 -0
- package/gnoman2.0/backend/services/runtimeTelemetryService.ts +158 -0
- package/gnoman2.0/backend/services/safeConfigRepository.ts +205 -0
- package/gnoman2.0/backend/services/safeService.ts +588 -0
- package/gnoman2.0/backend/services/sandboxService.ts +157 -0
- package/gnoman2.0/backend/services/secureSettingsService.ts +45 -0
- package/gnoman2.0/backend/services/transactionHoldService.ts +223 -0
- package/gnoman2.0/backend/services/vanityService.ts +293 -0
- package/gnoman2.0/backend/services/walletService.ts +290 -0
- package/gnoman2.0/backend/services/walletStore.ts +179 -0
- package/gnoman2.0/backend/types/express-async-handler.d.ts +13 -0
- package/gnoman2.0/backend/types/keyring.d.ts +19 -0
- package/gnoman2.0/backend/utils/abiResolver.ts +208 -0
- package/gnoman2.0/backend/utils/http.ts +6 -0
- package/gnoman2.0/backend/utils/secretsResolver.ts +150 -0
- package/gnoman2.0/backend/utils/signer.ts +11 -0
- package/gnoman2.0/backend/workers/vanityWorker.ts +76 -0
- package/gnoman2.0/capacitor.config.ts +13 -0
- package/gnoman2.0/cli/gnoman.ts +424 -0
- package/gnoman2.0/contracts/OracleConsumer.sol +20 -0
- package/gnoman2.0/contracts/PriceFeedConsumer.sol +22 -0
- package/gnoman2.0/docs/development-guide.md +203 -0
- package/gnoman2.0/docs/etherscan-chainlink-integration.md +44 -0
- package/gnoman2.0/docs/gnoman-20-user-manual-STANDARD-PRINT-READY.pdf +0 -0
- package/gnoman2.0/docs/gnoman-20-user-manual-STANDARD.pdf +0 -0
- package/gnoman2.0/docs/license-dev-guide.md +106 -0
- package/gnoman2.0/docs/robinhood-integration.md +30 -0
- package/gnoman2.0/docs/system-audit-gpt-guide.md +208 -0
- package/gnoman2.0/docs/system-robustness-audit.md +50 -0
- package/gnoman2.0/docs/user-guide.md +73 -0
- package/gnoman2.0/docs/wiki/development-guide.md +203 -0
- package/gnoman2.0/docs/wiki/license-dev-guide.md +106 -0
- package/gnoman2.0/docs/wiki/user-guide.md +73 -0
- package/gnoman2.0/eslint.config.js +85 -0
- package/gnoman2.0/gnomon/__init__.py +0 -0
- package/gnoman2.0/gnomon/api/__init__.py +0 -0
- package/gnoman2.0/gnomon/api/etherscan_tracker.py +72 -0
- package/gnoman2.0/gnomon/core/__init__.py +0 -0
- package/gnoman2.0/gnomon/core/safe_manager.py +111 -0
- package/gnoman2.0/gnomon/tests/test_abi_resolver.py +181 -0
- package/gnoman2.0/gnomon/tests/test_safe_persistence_and_etherscan.py +97 -0
- package/gnoman2.0/gnomon/utils/__init__.py +5 -0
- package/gnoman2.0/gnomon/utils/abi_resolver.py +255 -0
- package/gnoman2.0/ios/ExportOptions.plist +16 -0
- package/gnoman2.0/ios/README.md +33 -0
- package/gnoman2.0/jest.config.ts +18 -0
- package/gnoman2.0/keyring/__init__.py +17 -0
- package/gnoman2.0/licensingServer/package.json +23 -0
- package/gnoman2.0/licensingServer/src/config/keys.ts +84 -0
- package/gnoman2.0/licensingServer/src/index.ts +30 -0
- package/gnoman2.0/licensingServer/src/lib/canonicalize.ts +5 -0
- package/gnoman2.0/licensingServer/src/lib/crypto.ts +25 -0
- package/gnoman2.0/licensingServer/src/lib/validate.ts +62 -0
- package/gnoman2.0/licensingServer/src/middleware/auth.ts +20 -0
- package/gnoman2.0/licensingServer/src/routes/licenses.ts +110 -0
- package/gnoman2.0/licensingServer/tsconfig.json +12 -0
- package/gnoman2.0/main/ipcHandlers/index.ts +23 -0
- package/gnoman2.0/main/keyring/keyringmanager.ts +154 -0
- package/gnoman2.0/main/main.ts +234 -0
- package/gnoman2.0/main/preload/index.ts +31 -0
- package/gnoman2.0/main/preload/licenseBridge.ts +73 -0
- package/gnoman2.0/modules/sandbox/abiLoader.ts +78 -0
- package/gnoman2.0/modules/sandbox/contractSimulator.ts +241 -0
- package/gnoman2.0/modules/sandbox/formBuilder.ts +16 -0
- package/gnoman2.0/modules/sandbox/index.ts +6 -0
- package/gnoman2.0/modules/sandbox/localFork.ts +129 -0
- package/gnoman2.0/modules/sandbox/safe.abi.json +82 -0
- package/gnoman2.0/modules/sandbox/sandboxManager.ts +154 -0
- package/gnoman2.0/modules/sandbox/types.ts +84 -0
- package/gnoman2.0/modules/sandbox/ui/LogViewer.tsx +30 -0
- package/gnoman2.0/modules/sandbox/ui/ParameterForm.tsx +49 -0
- package/gnoman2.0/modules/sandbox/ui/SandboxPanel.tsx +568 -0
- package/gnoman2.0/package-lock.json +10904 -0
- package/gnoman2.0/package.json +82 -0
- package/gnoman2.0/renderer/components/LicenseScreen.tsx +134 -0
- package/gnoman2.0/renderer/index.html +12 -0
- package/gnoman2.0/renderer/package-lock.json +4104 -0
- package/gnoman2.0/renderer/package.json +35 -0
- package/gnoman2.0/renderer/postcss.config.cjs +6 -0
- package/gnoman2.0/renderer/src/App.tsx +229 -0
- package/gnoman2.0/renderer/src/context/KeyringContext.tsx +217 -0
- package/gnoman2.0/renderer/src/context/SafeContext.tsx +49 -0
- package/gnoman2.0/renderer/src/context/ThemeContext.tsx +60 -0
- package/gnoman2.0/renderer/src/context/WalletContext.tsx +50 -0
- package/gnoman2.0/renderer/src/context/main.tsx +18 -0
- package/gnoman2.0/renderer/src/main.tsx +18 -0
- package/gnoman2.0/renderer/src/pages/Contracts.tsx +482 -0
- package/gnoman2.0/renderer/src/pages/Dashboard.tsx +653 -0
- package/gnoman2.0/renderer/src/pages/DeveloperTools.tsx +270 -0
- package/gnoman2.0/renderer/src/pages/History.tsx +149 -0
- package/gnoman2.0/renderer/src/pages/Keyring.tsx +449 -0
- package/gnoman2.0/renderer/src/pages/Safes.tsx +1089 -0
- package/gnoman2.0/renderer/src/pages/Sandbox.tsx +146 -0
- package/gnoman2.0/renderer/src/pages/Settings.tsx +871 -0
- package/gnoman2.0/renderer/src/pages/Wallets.tsx +752 -0
- package/gnoman2.0/renderer/src/pages/WikiGuide.tsx +75 -0
- package/gnoman2.0/renderer/src/styles.css +32 -0
- package/gnoman2.0/renderer/src/types/gnoman.d.ts +9 -0
- package/gnoman2.0/renderer/src/types/license.ts +8 -0
- package/gnoman2.0/renderer/src/types/safevault.d.ts +17 -0
- package/gnoman2.0/renderer/src/utils/backend.ts +88 -0
- package/gnoman2.0/renderer/tailwind.config.cjs +8 -0
- package/gnoman2.0/renderer/tsconfig.json +13 -0
- package/gnoman2.0/renderer/tsconfig.node.json +9 -0
- package/gnoman2.0/renderer/vite.config.ts +19 -0
- package/gnoman2.0/requests/__init__.py +35 -0
- package/gnoman2.0/scripts/build-ios.sh +30 -0
- package/gnoman2.0/scripts/copyBackendAssets.js +24 -0
- package/gnoman2.0/scripts/copyRenderer.js +87 -0
- package/gnoman2.0/scripts/launchElectron.js +51 -0
- package/gnoman2.0/src/core/backends/fileBackend.ts +154 -0
- package/gnoman2.0/src/core/backends/memoryBackend.ts +27 -0
- package/gnoman2.0/src/core/backends/systemBackend.ts +66 -0
- package/gnoman2.0/src/core/backends/types.ts +17 -0
- package/gnoman2.0/src/core/keyringManager.ts +208 -0
- package/gnoman2.0/src/utils/abiCache/.gitkeep +0 -0
- package/gnoman2.0/src/utils/abiResolver.ts +200 -0
- package/gnoman2.0/src/utils/runtimeObservability.ts +110 -0
- package/gnoman2.0/src/utils/secretsResolver.ts +144 -0
- package/gnoman2.0/tests/chainlinkService.test.ts +32 -0
- package/gnoman2.0/tests/diagnosticsService.test.ts +68 -0
- package/gnoman2.0/tests/etherscanController.test.ts +99 -0
- package/gnoman2.0/tests/etherscanService.test.ts +116 -0
- package/gnoman2.0/tests/keyringManager.test.ts +135 -0
- package/gnoman2.0/tests/onchainToolkit.test.ts +71 -0
- package/gnoman2.0/tests/robinhoodClient.test.ts +54 -0
- package/gnoman2.0/tests/robinhoodController.test.ts +81 -0
- package/gnoman2.0/tests/robinhoodIntegrationService.test.ts +50 -0
- package/gnoman2.0/tests/safeServicePersistence.test.ts +81 -0
- package/gnoman2.0/tests/test_contract_sandbox/sandbox.test.js +407 -0
- package/gnoman2.0/tests/walletController.test.ts +57 -0
- package/gnoman2.0/tsconfig.backend.json +7 -0
- package/gnoman2.0/tsconfig.cli.json +7 -0
- package/gnoman2.0/tsconfig.json +18 -0
- package/gnoman2.0/tsconfig.main.json +7 -0
- package/gnomon/__init__.py +0 -0
- package/gnomon/__pycache__/__init__.cpython-310.pyc +0 -0
- package/gnomon/api/__init__.py +0 -0
- package/gnomon/api/__pycache__/__init__.cpython-310.pyc +0 -0
- package/gnomon/api/__pycache__/etherscan_tracker.cpython-310.pyc +0 -0
- package/gnomon/api/etherscan_tracker.py +72 -0
- package/gnomon/core/__init__.py +0 -0
- package/gnomon/core/safe_manager.py +111 -0
- package/gnomon/tests/__pycache__/test_safe_persistence_and_etherscan.cpython-310-pytest-8.3.3.pyc +0 -0
- package/gnomon/tests/test_abi_resolver.py +181 -0
- package/gnomon/tests/test_safe_persistence_and_etherscan.py +97 -0
- package/gnomon/utils/__init__.py +5 -0
- package/gnomon/utils/abi_resolver.py +255 -0
- package/ios/ExportOptions.plist +16 -0
- package/ios/README.md +33 -0
- package/jest.config.ts +18 -0
- package/keyring/__init__.py +17 -0
- package/launcher.sh +57 -0
- package/license.env +2 -0
- package/licensingServer/package.json +23 -0
- package/licensingServer/src/config/keys.ts +84 -0
- package/licensingServer/src/index.ts +30 -0
- package/licensingServer/src/lib/canonicalize.ts +5 -0
- package/licensingServer/src/lib/crypto.ts +25 -0
- package/licensingServer/src/lib/validate.ts +62 -0
- package/licensingServer/src/middleware/auth.ts +20 -0
- package/licensingServer/src/routes/licenses.ts +110 -0
- package/licensingServer/tsconfig.json +12 -0
- package/main/ipcHandlers/index.ts +23 -0
- package/main/keyring/keyringmanager.ts +154 -0
- package/main/main.ts +234 -0
- package/main/preload/index.ts +31 -0
- package/main/preload/licenseBridge.ts +73 -0
- package/modules/sandbox/abiLoader.ts +78 -0
- package/modules/sandbox/contractSimulator.ts +241 -0
- package/modules/sandbox/formBuilder.ts +16 -0
- package/modules/sandbox/index.ts +6 -0
- package/modules/sandbox/localFork.ts +129 -0
- package/modules/sandbox/safe.abi.json +82 -0
- package/modules/sandbox/sandboxManager.ts +154 -0
- package/modules/sandbox/types.ts +84 -0
- package/modules/sandbox/ui/LogViewer.tsx +30 -0
- package/modules/sandbox/ui/ParameterForm.tsx +49 -0
- package/modules/sandbox/ui/SandboxPanel.tsx +568 -0
- package/package.json +82 -0
- package/renderer/components/LicenseScreen.tsx +134 -0
- package/renderer/index.html +12 -0
- package/renderer/package-lock.json +4104 -0
- package/renderer/package.json +35 -0
- package/renderer/postcss.config.cjs +6 -0
- package/renderer/src/App.tsx +229 -0
- package/renderer/src/context/KeyringContext.tsx +217 -0
- package/renderer/src/context/SafeContext.tsx +49 -0
- package/renderer/src/context/ThemeContext.tsx +60 -0
- package/renderer/src/context/WalletContext.tsx +50 -0
- package/renderer/src/context/main.tsx +18 -0
- package/renderer/src/main.tsx +18 -0
- package/renderer/src/pages/Contracts.tsx +482 -0
- package/renderer/src/pages/Dashboard.tsx +653 -0
- package/renderer/src/pages/DeveloperTools.tsx +270 -0
- package/renderer/src/pages/History.tsx +149 -0
- package/renderer/src/pages/Keyring.tsx +449 -0
- package/renderer/src/pages/Safes.tsx +1089 -0
- package/renderer/src/pages/Sandbox.tsx +146 -0
- package/renderer/src/pages/Settings.tsx +871 -0
- package/renderer/src/pages/Wallets.tsx +752 -0
- package/renderer/src/pages/WikiGuide.tsx +75 -0
- package/renderer/src/styles.css +32 -0
- package/renderer/src/types/gnoman.d.ts +9 -0
- package/renderer/src/types/license.ts +8 -0
- package/renderer/src/types/safevault.d.ts +17 -0
- package/renderer/src/utils/backend.ts +88 -0
- package/renderer/tailwind.config.cjs +8 -0
- package/renderer/tsconfig.json +13 -0
- package/renderer/tsconfig.node.json +9 -0
- package/renderer/vite.config.ts +19 -0
- package/requests/__init__.py +35 -0
- package/requests/__pycache__/__init__.cpython-310.pyc +0 -0
- package/scripts/build-ios.sh +30 -0
- package/scripts/copyBackendAssets.js +24 -0
- package/scripts/copyRenderer.js +87 -0
- package/scripts/deployBackend.sh +24 -0
- package/scripts/launchElectron.js +51 -0
- package/src/core/backends/fileBackend.ts +154 -0
- package/src/core/backends/memoryBackend.ts +27 -0
- package/src/core/backends/systemBackend.ts +66 -0
- package/src/core/backends/types.ts +17 -0
- package/src/core/keyringManager.ts +208 -0
- package/src/utils/abiCache/.gitkeep +0 -0
- package/src/utils/abiResolver.ts +200 -0
- package/src/utils/runtimeObservability.ts +110 -0
- package/src/utils/secretsResolver.ts +144 -0
- package/tests/chainlinkService.test.ts +32 -0
- package/tests/diagnosticsService.test.ts +68 -0
- package/tests/etherscanController.test.ts +99 -0
- package/tests/etherscanService.test.ts +116 -0
- package/tests/keyringManager.test.ts +135 -0
- package/tests/onchainToolkit.test.ts +71 -0
- package/tests/robinhoodClient.test.ts +54 -0
- package/tests/robinhoodController.test.ts +81 -0
- package/tests/robinhoodIntegrationService.test.ts +50 -0
- package/tests/safeServicePersistence.test.ts +81 -0
- package/tests/test_contract_sandbox/sandbox.test.js +407 -0
- package/tests/walletController.test.ts +57 -0
- package/touch +14 -0
- package/tsconfig.backend.json +7 -0
- package/tsconfig.cli.json +7 -0
- package/tsconfig.json +18 -0
- package/tsconfig.main.json +7 -0
- package/webhook-shim.js +50 -0
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
import { FormEvent, useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import { useWallets } from '../context/WalletContext';
|
|
3
|
+
import { buildBackendUrl } from '../utils/backend';
|
|
4
|
+
|
|
5
|
+
interface WalletDetails {
|
|
6
|
+
address: string;
|
|
7
|
+
alias?: string;
|
|
8
|
+
hidden: boolean;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
source?: string;
|
|
11
|
+
network?: string;
|
|
12
|
+
balance?: string;
|
|
13
|
+
publicKey?: string;
|
|
14
|
+
mnemonic?: string;
|
|
15
|
+
derivationPath?: string;
|
|
16
|
+
privateKey: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Wallets = () => {
|
|
20
|
+
const { wallets, refresh } = useWallets();
|
|
21
|
+
const [loading, setLoading] = useState(false);
|
|
22
|
+
const [error, setError] = useState<string | undefined>();
|
|
23
|
+
const [propertiesOpen, setPropertiesOpen] = useState(false);
|
|
24
|
+
const [propertiesLoading, setPropertiesLoading] = useState(false);
|
|
25
|
+
const [propertiesError, setPropertiesError] = useState<string>();
|
|
26
|
+
const [properties, setProperties] = useState<WalletDetails | undefined>();
|
|
27
|
+
const [propertiesAddress, setPropertiesAddress] = useState<string>();
|
|
28
|
+
const [importType, setImportType] = useState<'mnemonic' | 'privateKey'>('mnemonic');
|
|
29
|
+
const [importLoading, setImportLoading] = useState(false);
|
|
30
|
+
const [importError, setImportError] = useState<string>();
|
|
31
|
+
const [importMessage, setImportMessage] = useState<string>();
|
|
32
|
+
const [txForm, setTxForm] = useState({ to: '', value: '', data: '', password: '' });
|
|
33
|
+
const [txLoading, setTxLoading] = useState(false);
|
|
34
|
+
const [txError, setTxError] = useState<string>();
|
|
35
|
+
const [txMessage, setTxMessage] = useState<string>();
|
|
36
|
+
const [removeMessage, setRemoveMessage] = useState<string>();
|
|
37
|
+
const [removeError, setRemoveError] = useState<string>();
|
|
38
|
+
const [removingAddress, setRemovingAddress] = useState<string | null>(null);
|
|
39
|
+
|
|
40
|
+
const formatRelativeTime = useCallback((value: string) => {
|
|
41
|
+
const timestamp = new Date(value).getTime();
|
|
42
|
+
if (Number.isNaN(timestamp)) {
|
|
43
|
+
return 'Unknown';
|
|
44
|
+
}
|
|
45
|
+
const diff = Date.now() - timestamp;
|
|
46
|
+
if (diff < 60 * 1000) {
|
|
47
|
+
return 'just now';
|
|
48
|
+
}
|
|
49
|
+
const minutes = Math.floor(diff / (60 * 1000));
|
|
50
|
+
if (minutes < 60) {
|
|
51
|
+
return `${minutes}m ago`;
|
|
52
|
+
}
|
|
53
|
+
const hours = Math.floor(minutes / 60);
|
|
54
|
+
if (hours < 24) {
|
|
55
|
+
return `${hours}h ago`;
|
|
56
|
+
}
|
|
57
|
+
const days = Math.floor(hours / 24);
|
|
58
|
+
if (days < 30) {
|
|
59
|
+
return `${days}d ago`;
|
|
60
|
+
}
|
|
61
|
+
const months = Math.floor(days / 30);
|
|
62
|
+
if (months < 12) {
|
|
63
|
+
return `${months}mo ago`;
|
|
64
|
+
}
|
|
65
|
+
const years = Math.floor(months / 12);
|
|
66
|
+
return `${years}y ago`;
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
const inventoryMetrics = useMemo(() => {
|
|
70
|
+
let hidden = 0;
|
|
71
|
+
let aliasCount = 0;
|
|
72
|
+
let imported = 0;
|
|
73
|
+
let newest: (typeof wallets)[number] | undefined;
|
|
74
|
+
let oldest: (typeof wallets)[number] | undefined;
|
|
75
|
+
const networkCounts = new Map<string, number>();
|
|
76
|
+
for (const wallet of wallets) {
|
|
77
|
+
if (wallet.hidden) {
|
|
78
|
+
hidden += 1;
|
|
79
|
+
}
|
|
80
|
+
if (wallet.alias && wallet.alias.trim()) {
|
|
81
|
+
aliasCount += 1;
|
|
82
|
+
}
|
|
83
|
+
if (wallet.source && wallet.source !== 'generated') {
|
|
84
|
+
imported += 1;
|
|
85
|
+
}
|
|
86
|
+
const createdAt = new Date(wallet.createdAt).getTime();
|
|
87
|
+
if (!Number.isNaN(createdAt)) {
|
|
88
|
+
if (!newest || createdAt > new Date(newest.createdAt).getTime()) {
|
|
89
|
+
newest = wallet;
|
|
90
|
+
}
|
|
91
|
+
if (!oldest || createdAt < new Date(oldest.createdAt).getTime()) {
|
|
92
|
+
oldest = wallet;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const network = (wallet.network ?? 'mainnet').toLowerCase();
|
|
96
|
+
networkCounts.set(network, (networkCounts.get(network) ?? 0) + 1);
|
|
97
|
+
}
|
|
98
|
+
const total = wallets.length;
|
|
99
|
+
const distribution = Array.from(networkCounts.entries())
|
|
100
|
+
.map(([network, count]) => ({
|
|
101
|
+
network,
|
|
102
|
+
count,
|
|
103
|
+
percentage: total ? Math.round((count / total) * 100) : 0
|
|
104
|
+
}))
|
|
105
|
+
.sort((a, b) => b.count - a.count)
|
|
106
|
+
.slice(0, 5);
|
|
107
|
+
return { total, hidden, aliasCount, imported, newest, oldest, distribution };
|
|
108
|
+
}, [wallets]);
|
|
109
|
+
|
|
110
|
+
const hiddenPercentage = inventoryMetrics.total
|
|
111
|
+
? Math.round((inventoryMetrics.hidden / inventoryMetrics.total) * 100)
|
|
112
|
+
: 0;
|
|
113
|
+
const aliasCoverage = inventoryMetrics.total
|
|
114
|
+
? Math.round((inventoryMetrics.aliasCount / inventoryMetrics.total) * 100)
|
|
115
|
+
: 0;
|
|
116
|
+
const importedShare = inventoryMetrics.total
|
|
117
|
+
? Math.round((inventoryMetrics.imported / inventoryMetrics.total) * 100)
|
|
118
|
+
: 0;
|
|
119
|
+
const newestRelative = useMemo(
|
|
120
|
+
() => (inventoryMetrics.newest ? formatRelativeTime(inventoryMetrics.newest.createdAt) : '—'),
|
|
121
|
+
[formatRelativeTime, inventoryMetrics.newest?.createdAt]
|
|
122
|
+
);
|
|
123
|
+
const oldestRelative = useMemo(
|
|
124
|
+
() => (inventoryMetrics.oldest ? formatRelativeTime(inventoryMetrics.oldest.createdAt) : '—'),
|
|
125
|
+
[formatRelativeTime, inventoryMetrics.oldest?.createdAt]
|
|
126
|
+
);
|
|
127
|
+
const walletInsights = useMemo(() => {
|
|
128
|
+
const insights: string[] = [];
|
|
129
|
+
if (!inventoryMetrics.total) {
|
|
130
|
+
insights.push('No wallets stored yet. Generate a wallet to populate telemetry.');
|
|
131
|
+
return insights;
|
|
132
|
+
}
|
|
133
|
+
insights.push(
|
|
134
|
+
`${hiddenPercentage}% hidden via keyring isolation (${inventoryMetrics.hidden}/${inventoryMetrics.total}).`
|
|
135
|
+
);
|
|
136
|
+
insights.push(
|
|
137
|
+
aliasCoverage
|
|
138
|
+
? `${inventoryMetrics.aliasCount} wallet aliases assigned for quick operator recognition.`
|
|
139
|
+
: 'Add wallet aliases to improve operator visibility during incident response.'
|
|
140
|
+
);
|
|
141
|
+
insights.push(
|
|
142
|
+
importedShare
|
|
143
|
+
? `${inventoryMetrics.imported} imported wallets (${importedShare}%).`
|
|
144
|
+
: 'All managed wallets were generated locally inside GNOMAN.'
|
|
145
|
+
);
|
|
146
|
+
if (inventoryMetrics.newest) {
|
|
147
|
+
insights.push(`Most recent rotation happened ${newestRelative}.`);
|
|
148
|
+
}
|
|
149
|
+
if (inventoryMetrics.oldest) {
|
|
150
|
+
insights.push(`Longest-lived wallet observed ${oldestRelative}.`);
|
|
151
|
+
}
|
|
152
|
+
return insights;
|
|
153
|
+
}, [aliasCoverage, hiddenPercentage, importedShare, inventoryMetrics, newestRelative, oldestRelative]);
|
|
154
|
+
|
|
155
|
+
const handleGenerate = async (event: FormEvent<HTMLFormElement>) => {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
const formData = new FormData(event.currentTarget);
|
|
158
|
+
setLoading(true);
|
|
159
|
+
setError(undefined);
|
|
160
|
+
try {
|
|
161
|
+
const response = await fetch(buildBackendUrl('/api/wallets/generate'), {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
headers: { 'Content-Type': 'application/json' },
|
|
164
|
+
body: JSON.stringify({
|
|
165
|
+
alias: formData.get('alias') || undefined,
|
|
166
|
+
password: formData.get('password') || undefined,
|
|
167
|
+
hidden: formData.get('hidden') === 'on'
|
|
168
|
+
})
|
|
169
|
+
});
|
|
170
|
+
if (!response.ok) {
|
|
171
|
+
throw new Error('Wallet generation failed');
|
|
172
|
+
}
|
|
173
|
+
await refresh();
|
|
174
|
+
event.currentTarget.reset();
|
|
175
|
+
} catch (err) {
|
|
176
|
+
setError(err instanceof Error ? err.message : 'Failed to create wallet');
|
|
177
|
+
} finally {
|
|
178
|
+
setLoading(false);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const handleImport = async (event: FormEvent<HTMLFormElement>) => {
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
const formData = new FormData(event.currentTarget);
|
|
185
|
+
setImportLoading(true);
|
|
186
|
+
setImportError(undefined);
|
|
187
|
+
setImportMessage(undefined);
|
|
188
|
+
const endpoint =
|
|
189
|
+
importType === 'mnemonic' ? '/api/wallets/import/mnemonic' : '/api/wallets/import/private-key';
|
|
190
|
+
const payload =
|
|
191
|
+
importType === 'mnemonic'
|
|
192
|
+
? {
|
|
193
|
+
mnemonic: formData.get('mnemonic'),
|
|
194
|
+
path: formData.get('derivationPath') || undefined,
|
|
195
|
+
alias: formData.get('importAlias') || undefined,
|
|
196
|
+
password: formData.get('importPassword') || undefined,
|
|
197
|
+
hidden: formData.get('importHidden') === 'on'
|
|
198
|
+
}
|
|
199
|
+
: {
|
|
200
|
+
privateKey: formData.get('privateKey'),
|
|
201
|
+
alias: formData.get('importAlias') || undefined,
|
|
202
|
+
password: formData.get('importPassword') || undefined,
|
|
203
|
+
hidden: formData.get('importHidden') === 'on'
|
|
204
|
+
};
|
|
205
|
+
try {
|
|
206
|
+
const response = await fetch(buildBackendUrl(endpoint), {
|
|
207
|
+
method: 'POST',
|
|
208
|
+
headers: { 'Content-Type': 'application/json' },
|
|
209
|
+
body: JSON.stringify(payload)
|
|
210
|
+
});
|
|
211
|
+
if (!response.ok) {
|
|
212
|
+
throw new Error('Wallet import failed');
|
|
213
|
+
}
|
|
214
|
+
await refresh();
|
|
215
|
+
setImportMessage('Wallet imported');
|
|
216
|
+
event.currentTarget.reset();
|
|
217
|
+
} catch (err) {
|
|
218
|
+
setImportError(err instanceof Error ? err.message : 'Failed to import wallet');
|
|
219
|
+
} finally {
|
|
220
|
+
setImportLoading(false);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const safeRefresh = async () => {
|
|
225
|
+
try {
|
|
226
|
+
await refresh();
|
|
227
|
+
} catch (err) {
|
|
228
|
+
setError(err instanceof Error ? err.message : 'Failed to refresh wallets');
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const openProperties = async (address: string) => {
|
|
233
|
+
setPropertiesAddress(address);
|
|
234
|
+
setPropertiesOpen(true);
|
|
235
|
+
setPropertiesLoading(true);
|
|
236
|
+
setPropertiesError(undefined);
|
|
237
|
+
setProperties(undefined);
|
|
238
|
+
setTxForm({ to: '', value: '', data: '', password: '' });
|
|
239
|
+
setTxError(undefined);
|
|
240
|
+
setTxMessage(undefined);
|
|
241
|
+
try {
|
|
242
|
+
const response = await fetch(buildBackendUrl(`/api/wallets/${address}/details`));
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
throw new Error('Unable to load wallet details');
|
|
245
|
+
}
|
|
246
|
+
const payload = (await response.json()) as WalletDetails;
|
|
247
|
+
setProperties(payload);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
setPropertiesError(err instanceof Error ? err.message : 'Failed to fetch wallet details');
|
|
250
|
+
} finally {
|
|
251
|
+
setPropertiesLoading(false);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const closeProperties = () => {
|
|
256
|
+
setPropertiesOpen(false);
|
|
257
|
+
setProperties(undefined);
|
|
258
|
+
setPropertiesAddress(undefined);
|
|
259
|
+
setPropertiesError(undefined);
|
|
260
|
+
setTxError(undefined);
|
|
261
|
+
setTxMessage(undefined);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const derivedBalance = useMemo(() => {
|
|
265
|
+
if (!properties?.balance) {
|
|
266
|
+
return 'Not yet synced';
|
|
267
|
+
}
|
|
268
|
+
return properties.balance.includes('ETH') ? properties.balance : `${properties.balance} ETH`;
|
|
269
|
+
}, [properties?.balance]);
|
|
270
|
+
|
|
271
|
+
const mnemonicLabel = useMemo(() => {
|
|
272
|
+
if (!properties) {
|
|
273
|
+
return '—';
|
|
274
|
+
}
|
|
275
|
+
if (properties.source === 'privateKey') {
|
|
276
|
+
return 'Not available (imported via private key)';
|
|
277
|
+
}
|
|
278
|
+
return properties.mnemonic ?? 'Unavailable';
|
|
279
|
+
}, [properties]);
|
|
280
|
+
|
|
281
|
+
const derivationPathLabel = useMemo(() => {
|
|
282
|
+
if (!properties) {
|
|
283
|
+
return '—';
|
|
284
|
+
}
|
|
285
|
+
if (properties.source === 'privateKey') {
|
|
286
|
+
return 'Not applicable for private key imports';
|
|
287
|
+
}
|
|
288
|
+
return properties.derivationPath ?? '—';
|
|
289
|
+
}, [properties]);
|
|
290
|
+
|
|
291
|
+
const handleSendTransaction = async (event: FormEvent<HTMLFormElement>) => {
|
|
292
|
+
event.preventDefault();
|
|
293
|
+
if (!propertiesAddress) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
setTxLoading(true);
|
|
297
|
+
setTxError(undefined);
|
|
298
|
+
setTxMessage(undefined);
|
|
299
|
+
try {
|
|
300
|
+
const response = await fetch(buildBackendUrl(`/api/wallets/${propertiesAddress}/transactions`), {
|
|
301
|
+
method: 'POST',
|
|
302
|
+
headers: { 'Content-Type': 'application/json' },
|
|
303
|
+
body: JSON.stringify({
|
|
304
|
+
to: txForm.to,
|
|
305
|
+
value: txForm.value || undefined,
|
|
306
|
+
data: txForm.data || undefined,
|
|
307
|
+
password: txForm.password
|
|
308
|
+
})
|
|
309
|
+
});
|
|
310
|
+
if (!response.ok) {
|
|
311
|
+
throw new Error('Failed to send transaction');
|
|
312
|
+
}
|
|
313
|
+
const payload = (await response.json()) as { hash: string };
|
|
314
|
+
setTxMessage(`Transaction submitted: ${payload.hash}`);
|
|
315
|
+
setTxForm({ to: '', value: '', data: '', password: '' });
|
|
316
|
+
} catch (err) {
|
|
317
|
+
setTxError(err instanceof Error ? err.message : 'Unable to send transaction');
|
|
318
|
+
} finally {
|
|
319
|
+
setTxLoading(false);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const handleRemoveWallet = async (address: string, alias?: string) => {
|
|
324
|
+
const label = alias?.trim() ? `${alias} (${address})` : address;
|
|
325
|
+
const confirmed = window.confirm(`Remove wallet ${label}? This cannot be undone.`);
|
|
326
|
+
if (!confirmed) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
setRemoveMessage(undefined);
|
|
330
|
+
setRemoveError(undefined);
|
|
331
|
+
setRemovingAddress(address);
|
|
332
|
+
try {
|
|
333
|
+
const response = await fetch(buildBackendUrl(`/api/wallets/${address}`), { method: 'DELETE' });
|
|
334
|
+
if (!response.ok) {
|
|
335
|
+
throw new Error('Failed to remove wallet');
|
|
336
|
+
}
|
|
337
|
+
await refresh();
|
|
338
|
+
setRemoveMessage('Wallet removed.');
|
|
339
|
+
if (propertiesAddress === address) {
|
|
340
|
+
closeProperties();
|
|
341
|
+
}
|
|
342
|
+
} catch (err) {
|
|
343
|
+
setRemoveError(err instanceof Error ? err.message : 'Unable to remove wallet');
|
|
344
|
+
} finally {
|
|
345
|
+
setRemovingAddress(null);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<div className="space-y-6">
|
|
351
|
+
<section className="rounded-2xl border border-slate-800 bg-slate-900/60 p-6">
|
|
352
|
+
<div className="flex items-center justify-between">
|
|
353
|
+
<h2 className="text-sm font-semibold text-slate-200">Inventory overview</h2>
|
|
354
|
+
<span className="text-xs uppercase tracking-widest text-slate-500">
|
|
355
|
+
{inventoryMetrics.total ? 'Keyring sync active' : 'Awaiting wallets'}
|
|
356
|
+
</span>
|
|
357
|
+
</div>
|
|
358
|
+
<div className="mt-4 grid gap-4 sm:grid-cols-3">
|
|
359
|
+
<div>
|
|
360
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Managed wallets</p>
|
|
361
|
+
<p className="mt-2 text-2xl font-semibold text-white">{inventoryMetrics.total}</p>
|
|
362
|
+
<p className="mt-1 text-xs text-slate-400">
|
|
363
|
+
{inventoryMetrics.total
|
|
364
|
+
? `${inventoryMetrics.hidden} hidden • ${inventoryMetrics.total - inventoryMetrics.hidden} visible`
|
|
365
|
+
: 'Generate or import to begin'}
|
|
366
|
+
</p>
|
|
367
|
+
</div>
|
|
368
|
+
<div>
|
|
369
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Hidden coverage</p>
|
|
370
|
+
<p className="mt-2 text-2xl font-semibold text-white">{hiddenPercentage}%</p>
|
|
371
|
+
<p className="mt-1 text-xs text-slate-400">
|
|
372
|
+
{inventoryMetrics.hidden
|
|
373
|
+
? 'Keyring isolation enforced'
|
|
374
|
+
: 'No hidden wallets stored'}
|
|
375
|
+
</p>
|
|
376
|
+
</div>
|
|
377
|
+
<div>
|
|
378
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Rotation cadence</p>
|
|
379
|
+
<p className="mt-2 text-2xl font-semibold text-white">{newestRelative}</p>
|
|
380
|
+
<p className="mt-1 text-xs text-slate-400">Longest lived: {oldestRelative}</p>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
<div className="mt-6 grid gap-4 md:grid-cols-2">
|
|
384
|
+
<div className="rounded-2xl border border-slate-800/80 bg-slate-950/60 p-4">
|
|
385
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Network distribution</p>
|
|
386
|
+
<ul className="mt-3 space-y-2 text-xs text-slate-300">
|
|
387
|
+
{inventoryMetrics.distribution.length ? (
|
|
388
|
+
inventoryMetrics.distribution.map((entry) => (
|
|
389
|
+
<li
|
|
390
|
+
key={entry.network}
|
|
391
|
+
className="flex items-center justify-between rounded-lg border border-slate-800/80 bg-slate-900/60 px-3 py-2"
|
|
392
|
+
>
|
|
393
|
+
<span className="font-medium capitalize text-white">{entry.network}</span>
|
|
394
|
+
<span className="text-[11px] text-slate-400">
|
|
395
|
+
{entry.count} • {entry.percentage}%
|
|
396
|
+
</span>
|
|
397
|
+
</li>
|
|
398
|
+
))
|
|
399
|
+
) : (
|
|
400
|
+
<li className="rounded-lg border border-dashed border-slate-700 bg-slate-950/40 px-3 py-2 text-slate-500">
|
|
401
|
+
Distribution will appear once wallets are generated.
|
|
402
|
+
</li>
|
|
403
|
+
)}
|
|
404
|
+
</ul>
|
|
405
|
+
</div>
|
|
406
|
+
<div className="rounded-2xl border border-slate-800/80 bg-slate-950/60 p-4">
|
|
407
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Operational insights</p>
|
|
408
|
+
<ul className="mt-3 space-y-2 text-xs text-slate-300">
|
|
409
|
+
{walletInsights.map((insight) => (
|
|
410
|
+
<li key={insight} className="rounded-lg border border-slate-800/80 bg-slate-900/60 px-3 py-2">
|
|
411
|
+
{insight}
|
|
412
|
+
</li>
|
|
413
|
+
))}
|
|
414
|
+
</ul>
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
</section>
|
|
418
|
+
|
|
419
|
+
<div className="grid gap-6 md:grid-cols-[2fr,3fr]">
|
|
420
|
+
<div className="space-y-6">
|
|
421
|
+
<section className="rounded-lg border border-slate-800 bg-slate-900/60 p-4">
|
|
422
|
+
<h2 className="text-lg font-semibold">Generate Wallet</h2>
|
|
423
|
+
<p className="mt-1 text-sm text-slate-500">
|
|
424
|
+
Create a new wallet secured with encryption. Hidden wallets are stored only in the active keyring service.
|
|
425
|
+
</p>
|
|
426
|
+
<form className="mt-4 space-y-3" onSubmit={handleGenerate}>
|
|
427
|
+
<label className="block text-sm">
|
|
428
|
+
<span className="text-slate-300">Alias</span>
|
|
429
|
+
<input name="alias" className="mt-1 w-full rounded border border-slate-700 bg-slate-900 p-2" />
|
|
430
|
+
</label>
|
|
431
|
+
<label className="block text-sm">
|
|
432
|
+
<span className="text-slate-300">Encryption Password</span>
|
|
433
|
+
<input
|
|
434
|
+
name="password"
|
|
435
|
+
type="password"
|
|
436
|
+
placeholder="Auto-generate when empty"
|
|
437
|
+
className="mt-1 w-full rounded border border-slate-700 bg-slate-900 p-2"
|
|
438
|
+
/>
|
|
439
|
+
</label>
|
|
440
|
+
<label className="flex items-center gap-2 text-sm text-slate-300">
|
|
441
|
+
<input type="checkbox" name="hidden" className="h-4 w-4 rounded border-slate-700" />
|
|
442
|
+
Hidden wallet (keyring storage)
|
|
443
|
+
</label>
|
|
444
|
+
<button
|
|
445
|
+
type="submit"
|
|
446
|
+
disabled={loading}
|
|
447
|
+
className="w-full rounded bg-emerald-500 px-4 py-2 text-sm font-semibold text-emerald-950 transition hover:bg-emerald-400 disabled:opacity-50"
|
|
448
|
+
>
|
|
449
|
+
{loading ? 'Generating...' : 'Generate Wallet'}
|
|
450
|
+
</button>
|
|
451
|
+
{error && <p className="text-sm text-red-400">{error}</p>}
|
|
452
|
+
</form>
|
|
453
|
+
</section>
|
|
454
|
+
<section className="rounded-lg border border-slate-800 bg-slate-900/60 p-4">
|
|
455
|
+
<h2 className="text-lg font-semibold">Import Wallet</h2>
|
|
456
|
+
<p className="mt-1 text-sm text-slate-500">
|
|
457
|
+
Bring an existing wallet into GNOMAN using a mnemonic phrase or private key.
|
|
458
|
+
</p>
|
|
459
|
+
<div className="mt-4 flex gap-2 rounded-lg border border-slate-800 bg-slate-950/60 p-1 text-xs">
|
|
460
|
+
<button
|
|
461
|
+
type="button"
|
|
462
|
+
onClick={() => setImportType('mnemonic')}
|
|
463
|
+
className={`flex-1 rounded px-3 py-2 font-semibold transition ${
|
|
464
|
+
importType === 'mnemonic'
|
|
465
|
+
? 'bg-emerald-500 text-emerald-950'
|
|
466
|
+
: 'text-slate-300 hover:text-white'
|
|
467
|
+
}`}
|
|
468
|
+
>
|
|
469
|
+
Mnemonic
|
|
470
|
+
</button>
|
|
471
|
+
<button
|
|
472
|
+
type="button"
|
|
473
|
+
onClick={() => setImportType('privateKey')}
|
|
474
|
+
className={`flex-1 rounded px-3 py-2 font-semibold transition ${
|
|
475
|
+
importType === 'privateKey'
|
|
476
|
+
? 'bg-purple-500 text-purple-950'
|
|
477
|
+
: 'text-slate-300 hover:text-white'
|
|
478
|
+
}`}
|
|
479
|
+
>
|
|
480
|
+
Private key
|
|
481
|
+
</button>
|
|
482
|
+
</div>
|
|
483
|
+
<form className="mt-4 space-y-3" onSubmit={handleImport}>
|
|
484
|
+
{importType === 'mnemonic' ? (
|
|
485
|
+
<>
|
|
486
|
+
<label className="block text-sm">
|
|
487
|
+
<span className="text-slate-300">Mnemonic phrase</span>
|
|
488
|
+
<textarea
|
|
489
|
+
name="mnemonic"
|
|
490
|
+
rows={3}
|
|
491
|
+
required
|
|
492
|
+
className="mt-1 w-full rounded border border-slate-700 bg-slate-900 p-2 text-xs"
|
|
493
|
+
placeholder="word word word ..."
|
|
494
|
+
/>
|
|
495
|
+
</label>
|
|
496
|
+
<label className="block text-sm">
|
|
497
|
+
<span className="text-slate-300">Derivation path</span>
|
|
498
|
+
<input
|
|
499
|
+
name="derivationPath"
|
|
500
|
+
className="mt-1 w-full rounded border border-slate-700 bg-slate-900 p-2 text-xs"
|
|
501
|
+
placeholder="m/44'/60'/0'/0/0"
|
|
502
|
+
/>
|
|
503
|
+
</label>
|
|
504
|
+
</>
|
|
505
|
+
) : (
|
|
506
|
+
<label className="block text-sm">
|
|
507
|
+
<span className="text-slate-300">Private key</span>
|
|
508
|
+
<input
|
|
509
|
+
name="privateKey"
|
|
510
|
+
required
|
|
511
|
+
className="mt-1 w-full rounded border border-slate-700 bg-slate-900 p-2 text-xs"
|
|
512
|
+
placeholder="0x..."
|
|
513
|
+
/>
|
|
514
|
+
</label>
|
|
515
|
+
)}
|
|
516
|
+
<label className="block text-sm">
|
|
517
|
+
<span className="text-slate-300">Alias</span>
|
|
518
|
+
<input
|
|
519
|
+
name="importAlias"
|
|
520
|
+
className="mt-1 w-full rounded border border-slate-700 bg-slate-900 p-2 text-xs"
|
|
521
|
+
/>
|
|
522
|
+
</label>
|
|
523
|
+
<label className="block text-sm">
|
|
524
|
+
<span className="text-slate-300">Encryption Password</span>
|
|
525
|
+
<input
|
|
526
|
+
name="importPassword"
|
|
527
|
+
type="password"
|
|
528
|
+
placeholder="Auto-generate when empty"
|
|
529
|
+
className="mt-1 w-full rounded border border-slate-700 bg-slate-900 p-2 text-xs"
|
|
530
|
+
/>
|
|
531
|
+
</label>
|
|
532
|
+
<label className="flex items-center gap-2 text-sm text-slate-300">
|
|
533
|
+
<input type="checkbox" name="importHidden" className="h-4 w-4 rounded border-slate-700" />
|
|
534
|
+
Hidden wallet (keyring storage)
|
|
535
|
+
</label>
|
|
536
|
+
<button
|
|
537
|
+
type="submit"
|
|
538
|
+
disabled={importLoading}
|
|
539
|
+
className="w-full rounded bg-emerald-500 px-4 py-2 text-sm font-semibold text-emerald-950 transition hover:bg-emerald-400 disabled:opacity-50"
|
|
540
|
+
>
|
|
541
|
+
{importLoading ? 'Importing...' : 'Import wallet'}
|
|
542
|
+
</button>
|
|
543
|
+
{importError && <p className="text-sm text-red-400">{importError}</p>}
|
|
544
|
+
{importMessage && <p className="text-sm text-emerald-300">{importMessage}</p>}
|
|
545
|
+
</form>
|
|
546
|
+
</section>
|
|
547
|
+
</div>
|
|
548
|
+
<section className="rounded-lg border border-slate-800 bg-slate-900/60 p-4">
|
|
549
|
+
<div className="flex items-center justify-between">
|
|
550
|
+
<h2 className="text-lg font-semibold">Stored Wallets</h2>
|
|
551
|
+
<button
|
|
552
|
+
onClick={() => safeRefresh()}
|
|
553
|
+
className="rounded border border-slate-700 px-3 py-1 text-xs text-slate-300 transition hover:bg-slate-800"
|
|
554
|
+
>
|
|
555
|
+
Refresh
|
|
556
|
+
</button>
|
|
557
|
+
</div>
|
|
558
|
+
{(removeMessage || removeError) && (
|
|
559
|
+
<p className={`mt-2 text-xs ${removeError ? 'text-red-400' : 'text-emerald-300'}`}>
|
|
560
|
+
{removeError ?? removeMessage}
|
|
561
|
+
</p>
|
|
562
|
+
)}
|
|
563
|
+
<ul className="mt-4 space-y-3">
|
|
564
|
+
{wallets.map((wallet) => (
|
|
565
|
+
<li key={wallet.address} className="rounded border border-slate-800 p-3">
|
|
566
|
+
<p className="font-mono text-sm text-emerald-300">{wallet.address}</p>
|
|
567
|
+
<p className="text-xs text-slate-500">
|
|
568
|
+
Alias: {wallet.alias ?? '—'} • Created {new Date(wallet.createdAt).toLocaleString()} • Source: {wallet.source ?? 'generated'} • Network: {wallet.network ?? 'mainnet'} • Balance:{' '}
|
|
569
|
+
{wallet.balance
|
|
570
|
+
? wallet.balance.includes('ETH')
|
|
571
|
+
? wallet.balance
|
|
572
|
+
: `${wallet.balance} ETH`
|
|
573
|
+
: 'Not yet synced'}
|
|
574
|
+
</p>
|
|
575
|
+
<div className="mt-2 flex flex-wrap gap-2">
|
|
576
|
+
<span
|
|
577
|
+
className={`rounded-full px-2 py-0.5 text-[11px] ${
|
|
578
|
+
wallet.hidden
|
|
579
|
+
? 'bg-slate-800 text-slate-300'
|
|
580
|
+
: 'bg-emerald-500/10 text-emerald-300'
|
|
581
|
+
}`}
|
|
582
|
+
>
|
|
583
|
+
{wallet.hidden ? 'Hidden' : 'Visible'}
|
|
584
|
+
</span>
|
|
585
|
+
<button
|
|
586
|
+
onClick={() => openProperties(wallet.address)}
|
|
587
|
+
className="rounded border border-slate-700 px-2 py-0.5 text-[11px] font-semibold text-slate-200 transition hover:bg-slate-800"
|
|
588
|
+
>
|
|
589
|
+
View properties
|
|
590
|
+
</button>
|
|
591
|
+
<button
|
|
592
|
+
onClick={() => handleRemoveWallet(wallet.address, wallet.alias)}
|
|
593
|
+
className="rounded border border-red-500/40 px-2 py-0.5 text-[11px] font-semibold text-red-300 transition hover:bg-red-500/10 disabled:opacity-50"
|
|
594
|
+
disabled={removingAddress === wallet.address}
|
|
595
|
+
>
|
|
596
|
+
{removingAddress === wallet.address ? 'Removing…' : 'Remove'}
|
|
597
|
+
</button>
|
|
598
|
+
</div>
|
|
599
|
+
</li>
|
|
600
|
+
))}
|
|
601
|
+
{wallets.length === 0 && (
|
|
602
|
+
<li className="rounded border border-dashed border-slate-700 p-4 text-sm text-slate-500">
|
|
603
|
+
No wallets stored yet.
|
|
604
|
+
</li>
|
|
605
|
+
)}
|
|
606
|
+
</ul>
|
|
607
|
+
</section>
|
|
608
|
+
</div>
|
|
609
|
+
|
|
610
|
+
{propertiesOpen && (
|
|
611
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-950/80 p-4">
|
|
612
|
+
<div className="relative w-full max-w-2xl rounded-xl border border-slate-800 bg-slate-900 p-6 shadow-xl">
|
|
613
|
+
<button
|
|
614
|
+
onClick={closeProperties}
|
|
615
|
+
className="absolute right-4 top-4 rounded-full border border-slate-700 px-2 py-0.5 text-xs text-slate-300 transition hover:bg-slate-800"
|
|
616
|
+
>
|
|
617
|
+
Close
|
|
618
|
+
</button>
|
|
619
|
+
<h3 className="text-lg font-semibold text-white">Wallet properties</h3>
|
|
620
|
+
<p className="mt-1 text-xs text-slate-400">{propertiesAddress}</p>
|
|
621
|
+
<div className="mt-4 space-y-3 text-sm text-slate-300">
|
|
622
|
+
{propertiesLoading && <p className="text-slate-400">Loading properties…</p>}
|
|
623
|
+
{propertiesError && <p className="text-red-400">{propertiesError}</p>}
|
|
624
|
+
{!propertiesLoading && !propertiesError && properties && (
|
|
625
|
+
<div className="space-y-4">
|
|
626
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
627
|
+
<div>
|
|
628
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Alias</p>
|
|
629
|
+
<p className="font-medium text-white">{properties.alias ?? '—'}</p>
|
|
630
|
+
</div>
|
|
631
|
+
<div>
|
|
632
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Created</p>
|
|
633
|
+
<p>{new Date(properties.createdAt).toLocaleString()}</p>
|
|
634
|
+
</div>
|
|
635
|
+
<div>
|
|
636
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Network</p>
|
|
637
|
+
<p>{properties.network ?? 'mainnet'}</p>
|
|
638
|
+
</div>
|
|
639
|
+
<div>
|
|
640
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Balance</p>
|
|
641
|
+
<p>{derivedBalance}</p>
|
|
642
|
+
</div>
|
|
643
|
+
<div>
|
|
644
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Visibility</p>
|
|
645
|
+
<p>{properties.hidden ? 'Hidden (keyring storage)' : 'Visible'}</p>
|
|
646
|
+
</div>
|
|
647
|
+
<div>
|
|
648
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Source</p>
|
|
649
|
+
<p>{properties.source ?? 'generated'}</p>
|
|
650
|
+
</div>
|
|
651
|
+
</div>
|
|
652
|
+
<div className="space-y-3 rounded-lg border border-slate-800 bg-slate-950/60 p-4">
|
|
653
|
+
<div>
|
|
654
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Public key</p>
|
|
655
|
+
<p className="mt-1 break-all font-mono text-xs text-emerald-300">
|
|
656
|
+
{properties.publicKey ?? 'Unavailable'}
|
|
657
|
+
</p>
|
|
658
|
+
</div>
|
|
659
|
+
<div>
|
|
660
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Private key</p>
|
|
661
|
+
<p className="mt-1 break-all font-mono text-xs text-amber-300">
|
|
662
|
+
{properties.privateKey}
|
|
663
|
+
</p>
|
|
664
|
+
</div>
|
|
665
|
+
<div>
|
|
666
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Mnemonic</p>
|
|
667
|
+
<p className="mt-1 break-words font-mono text-xs text-slate-200">
|
|
668
|
+
{mnemonicLabel}
|
|
669
|
+
</p>
|
|
670
|
+
</div>
|
|
671
|
+
<div>
|
|
672
|
+
<p className="text-xs uppercase tracking-widest text-slate-500">Derivation path</p>
|
|
673
|
+
<p className="mt-1 font-mono text-xs text-slate-200">
|
|
674
|
+
{derivationPathLabel}
|
|
675
|
+
</p>
|
|
676
|
+
</div>
|
|
677
|
+
</div>
|
|
678
|
+
<div className="space-y-3 rounded-lg border border-slate-800 bg-slate-950/60 p-4">
|
|
679
|
+
<h4 className="text-sm font-semibold text-white">Send transaction</h4>
|
|
680
|
+
<form className="space-y-3" onSubmit={handleSendTransaction}>
|
|
681
|
+
<label className="block text-xs uppercase tracking-widest text-slate-500">To address</label>
|
|
682
|
+
<input
|
|
683
|
+
value={txForm.to}
|
|
684
|
+
onChange={(event) => setTxForm((prev) => ({ ...prev, to: event.target.value }))}
|
|
685
|
+
className="w-full rounded border border-slate-700 bg-slate-900 p-2 text-xs"
|
|
686
|
+
placeholder="0x..."
|
|
687
|
+
required
|
|
688
|
+
/>
|
|
689
|
+
<label className="block text-xs uppercase tracking-widest text-slate-500">Value (ETH)</label>
|
|
690
|
+
<input
|
|
691
|
+
value={txForm.value}
|
|
692
|
+
onChange={(event) => setTxForm((prev) => ({ ...prev, value: event.target.value }))}
|
|
693
|
+
className="w-full rounded border border-slate-700 bg-slate-900 p-2 text-xs"
|
|
694
|
+
placeholder="0.0"
|
|
695
|
+
/>
|
|
696
|
+
<label className="block text-xs uppercase tracking-widest text-slate-500">Data (optional)</label>
|
|
697
|
+
<textarea
|
|
698
|
+
value={txForm.data}
|
|
699
|
+
onChange={(event) => setTxForm((prev) => ({ ...prev, data: event.target.value }))}
|
|
700
|
+
className="w-full rounded border border-slate-700 bg-slate-900 p-2 text-xs"
|
|
701
|
+
rows={3}
|
|
702
|
+
placeholder="0x"
|
|
703
|
+
/>
|
|
704
|
+
<label className="block text-xs uppercase tracking-widest text-slate-500">Wallet password</label>
|
|
705
|
+
<input
|
|
706
|
+
type="password"
|
|
707
|
+
value={txForm.password}
|
|
708
|
+
onChange={(event) => setTxForm((prev) => ({ ...prev, password: event.target.value }))}
|
|
709
|
+
className="w-full rounded border border-slate-700 bg-slate-900 p-2 text-xs"
|
|
710
|
+
placeholder="Password used to encrypt the wallet"
|
|
711
|
+
required
|
|
712
|
+
/>
|
|
713
|
+
<button
|
|
714
|
+
type="submit"
|
|
715
|
+
disabled={txLoading}
|
|
716
|
+
className="w-full rounded bg-blue-500 px-3 py-2 text-xs font-semibold text-blue-950 transition hover:bg-blue-400 disabled:opacity-50"
|
|
717
|
+
>
|
|
718
|
+
{txLoading ? 'Sending...' : 'Send transaction'}
|
|
719
|
+
</button>
|
|
720
|
+
{(txError || txMessage) && (
|
|
721
|
+
<p className={`text-xs ${txError ? 'text-red-400' : 'text-emerald-400'}`}>
|
|
722
|
+
{txError ?? txMessage}
|
|
723
|
+
</p>
|
|
724
|
+
)}
|
|
725
|
+
</form>
|
|
726
|
+
</div>
|
|
727
|
+
<div className="space-y-2 rounded-lg border border-red-500/30 bg-red-500/5 p-4 text-xs text-red-200">
|
|
728
|
+
<p className="text-sm font-semibold text-red-100">Remove wallet</p>
|
|
729
|
+
<p>
|
|
730
|
+
This permanently deletes the wallet record from local storage. Export the wallet first if you
|
|
731
|
+
still need the private key.
|
|
732
|
+
</p>
|
|
733
|
+
<button
|
|
734
|
+
type="button"
|
|
735
|
+
onClick={() => handleRemoveWallet(properties.address, properties.alias)}
|
|
736
|
+
className="inline-flex items-center gap-2 rounded border border-red-500/50 px-3 py-1 text-xs font-semibold text-red-200 transition hover:bg-red-500/10 disabled:opacity-50"
|
|
737
|
+
disabled={removingAddress === properties.address}
|
|
738
|
+
>
|
|
739
|
+
{removingAddress === properties.address ? 'Removing…' : 'Remove wallet'}
|
|
740
|
+
</button>
|
|
741
|
+
</div>
|
|
742
|
+
</div>
|
|
743
|
+
)}
|
|
744
|
+
</div>
|
|
745
|
+
</div>
|
|
746
|
+
</div>
|
|
747
|
+
)}
|
|
748
|
+
</div>
|
|
749
|
+
);
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
export default Wallets;
|