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.
Files changed (606) hide show
  1. package/.eslintrc.cjs +24 -0
  2. package/.gnoman/contracts.json +4094 -0
  3. package/.gnoman/exec_package/runtime-debug.jsonl +45 -0
  4. package/.gnoman/holds.sqlite +0 -0
  5. package/.gnoman/license.json +7 -0
  6. package/.gnoman/safes.json +37 -0
  7. package/.gnoman/vanity-jobs.json +67 -0
  8. package/.gnoman/wallets.db +0 -0
  9. package/.prettierrc.json +6 -0
  10. package/CODex_TASKS.md +124 -0
  11. package/LICENSE.md +164 -0
  12. package/README.md +95 -0
  13. package/assets/GnoLogo.jpg +0 -0
  14. package/assets/self.png +0 -0
  15. package/backend/controllers/contractController.ts +49 -0
  16. package/backend/controllers/devToolsController.ts +76 -0
  17. package/backend/controllers/etherscanController.ts +59 -0
  18. package/backend/controllers/historyController.ts +7 -0
  19. package/backend/controllers/keyringController.ts +134 -0
  20. package/backend/controllers/robinhoodController.ts +80 -0
  21. package/backend/controllers/safeController.ts +167 -0
  22. package/backend/controllers/sandboxController.ts +63 -0
  23. package/backend/controllers/settingsController.ts +38 -0
  24. package/backend/controllers/walletController.ts +151 -0
  25. package/backend/index.ts +133 -0
  26. package/backend/licenses/license_public.pem +3 -0
  27. package/backend/licenses/verify_license.py +43 -0
  28. package/backend/routes/contractRoutes.ts +11 -0
  29. package/backend/routes/devToolsRoutes.ts +11 -0
  30. package/backend/routes/etherscanRoutes.ts +11 -0
  31. package/backend/routes/historyRoutes.ts +8 -0
  32. package/backend/routes/keyringRoutes.ts +25 -0
  33. package/backend/routes/licenseRoutes.ts +35 -0
  34. package/backend/routes/robinhoodRoutes.ts +22 -0
  35. package/backend/routes/runtimeRoutes.ts +29 -0
  36. package/backend/routes/safeRoutes.ts +28 -0
  37. package/backend/routes/sandboxRoutes.ts +17 -0
  38. package/backend/routes/settingsRoutes.ts +14 -0
  39. package/backend/routes/walletRoutes.ts +21 -0
  40. package/backend/services/chainlinkService.ts +65 -0
  41. package/backend/services/contractRegistryService.ts +205 -0
  42. package/backend/services/devToolsService.ts +251 -0
  43. package/backend/services/diagnosticsService.ts +350 -0
  44. package/backend/services/etherscanService.ts +152 -0
  45. package/backend/services/historyService.ts +89 -0
  46. package/backend/services/keyringAccessor.ts +4 -0
  47. package/backend/services/licenseService.ts +163 -0
  48. package/backend/services/onchain/abiRegistry.ts +57 -0
  49. package/backend/services/onchain/chainlinkClient.ts +56 -0
  50. package/backend/services/onchain/errors.ts +16 -0
  51. package/backend/services/onchain/etherscanClient.ts +94 -0
  52. package/backend/services/onchain/index.ts +76 -0
  53. package/backend/services/onchain/tenderlyRpcClient.ts +74 -0
  54. package/backend/services/onchain/types.ts +33 -0
  55. package/backend/services/onchainAutomationService.ts +424 -0
  56. package/backend/services/robinhood/auth.ts +42 -0
  57. package/backend/services/robinhood/client.ts +123 -0
  58. package/backend/services/robinhood/integrationService.ts +140 -0
  59. package/backend/services/robinhood/provider.ts +22 -0
  60. package/backend/services/robinhood/unofficialClient.ts +66 -0
  61. package/backend/services/rpcService.ts +44 -0
  62. package/backend/services/runtimeTelemetryService.ts +158 -0
  63. package/backend/services/safeConfigRepository.ts +205 -0
  64. package/backend/services/safeService.ts +588 -0
  65. package/backend/services/sandboxService.ts +157 -0
  66. package/backend/services/secureSettingsService.ts +45 -0
  67. package/backend/services/transactionHoldService.ts +223 -0
  68. package/backend/services/vanityService.ts +293 -0
  69. package/backend/services/walletService.ts +290 -0
  70. package/backend/services/walletStore.ts +179 -0
  71. package/backend/types/express-async-handler.d.ts +13 -0
  72. package/backend/types/keyring.d.ts +19 -0
  73. package/backend/utils/abiResolver.ts +208 -0
  74. package/backend/utils/http.ts +6 -0
  75. package/backend/utils/secretsResolver.ts +150 -0
  76. package/backend/utils/signer.ts +11 -0
  77. package/backend/workers/vanityWorker.ts +76 -0
  78. package/capacitor.config.ts +13 -0
  79. package/cli/gnoman.ts +424 -0
  80. package/contracts/OracleConsumer.sol +20 -0
  81. package/contracts/PriceFeedConsumer.sol +22 -0
  82. package/dist/backend/backend/controllers/contractController.js +41 -0
  83. package/dist/backend/backend/controllers/contractController.js.map +1 -0
  84. package/dist/backend/backend/controllers/devToolsController.js +63 -0
  85. package/dist/backend/backend/controllers/devToolsController.js.map +1 -0
  86. package/dist/backend/backend/controllers/etherscanController.js +53 -0
  87. package/dist/backend/backend/controllers/etherscanController.js.map +1 -0
  88. package/dist/backend/backend/controllers/historyController.js +12 -0
  89. package/dist/backend/backend/controllers/historyController.js.map +1 -0
  90. package/dist/backend/backend/controllers/keyringController.js +126 -0
  91. package/dist/backend/backend/controllers/keyringController.js.map +1 -0
  92. package/dist/backend/backend/controllers/robinhoodController.js +69 -0
  93. package/dist/backend/backend/controllers/robinhoodController.js.map +1 -0
  94. package/dist/backend/backend/controllers/safeController.js +137 -0
  95. package/dist/backend/backend/controllers/safeController.js.map +1 -0
  96. package/dist/backend/backend/controllers/sandboxController.js +48 -0
  97. package/dist/backend/backend/controllers/sandboxController.js.map +1 -0
  98. package/dist/backend/backend/controllers/settingsController.js +34 -0
  99. package/dist/backend/backend/controllers/settingsController.js.map +1 -0
  100. package/dist/backend/backend/controllers/walletController.js +140 -0
  101. package/dist/backend/backend/controllers/walletController.js.map +1 -0
  102. package/dist/backend/backend/index.js +119 -0
  103. package/dist/backend/backend/index.js.map +1 -0
  104. package/dist/backend/backend/routes/contractRoutes.js +44 -0
  105. package/dist/backend/backend/routes/contractRoutes.js.map +1 -0
  106. package/dist/backend/backend/routes/devToolsRoutes.js +44 -0
  107. package/dist/backend/backend/routes/devToolsRoutes.js.map +1 -0
  108. package/dist/backend/backend/routes/etherscanRoutes.js +44 -0
  109. package/dist/backend/backend/routes/etherscanRoutes.js.map +1 -0
  110. package/dist/backend/backend/routes/historyRoutes.js +41 -0
  111. package/dist/backend/backend/routes/historyRoutes.js.map +1 -0
  112. package/dist/backend/backend/routes/keyringRoutes.js +18 -0
  113. package/dist/backend/backend/routes/keyringRoutes.js.map +1 -0
  114. package/dist/backend/backend/routes/licenseRoutes.js +30 -0
  115. package/dist/backend/backend/routes/licenseRoutes.js.map +1 -0
  116. package/dist/backend/backend/routes/robinhoodRoutes.js +14 -0
  117. package/dist/backend/backend/routes/robinhoodRoutes.js.map +1 -0
  118. package/dist/backend/backend/routes/runtimeRoutes.js +26 -0
  119. package/dist/backend/backend/routes/runtimeRoutes.js.map +1 -0
  120. package/dist/backend/backend/routes/safeRoutes.js +61 -0
  121. package/dist/backend/backend/routes/safeRoutes.js.map +1 -0
  122. package/dist/backend/backend/routes/sandboxRoutes.js +50 -0
  123. package/dist/backend/backend/routes/sandboxRoutes.js.map +1 -0
  124. package/dist/backend/backend/routes/settingsRoutes.js +10 -0
  125. package/dist/backend/backend/routes/settingsRoutes.js.map +1 -0
  126. package/dist/backend/backend/routes/walletRoutes.js +54 -0
  127. package/dist/backend/backend/routes/walletRoutes.js.map +1 -0
  128. package/dist/backend/backend/services/chainlinkService.js +48 -0
  129. package/dist/backend/backend/services/chainlinkService.js.map +1 -0
  130. package/dist/backend/backend/services/contractRegistryService.js +138 -0
  131. package/dist/backend/backend/services/contractRegistryService.js.map +1 -0
  132. package/dist/backend/backend/services/devToolsService.js +213 -0
  133. package/dist/backend/backend/services/devToolsService.js.map +1 -0
  134. package/dist/backend/backend/services/diagnosticsService.js +286 -0
  135. package/dist/backend/backend/services/diagnosticsService.js.map +1 -0
  136. package/dist/backend/backend/services/etherscanService.js +125 -0
  137. package/dist/backend/backend/services/etherscanService.js.map +1 -0
  138. package/dist/backend/backend/services/historyService.js +75 -0
  139. package/dist/backend/backend/services/historyService.js.map +1 -0
  140. package/dist/backend/backend/services/keyringAccessor.js +40 -0
  141. package/dist/backend/backend/services/keyringAccessor.js.map +1 -0
  142. package/dist/backend/backend/services/licenseService.js +130 -0
  143. package/dist/backend/backend/services/licenseService.js.map +1 -0
  144. package/dist/backend/backend/services/onchain/abiRegistry.js +47 -0
  145. package/dist/backend/backend/services/onchain/abiRegistry.js.map +1 -0
  146. package/dist/backend/backend/services/onchain/chainlinkClient.js +43 -0
  147. package/dist/backend/backend/services/onchain/chainlinkClient.js.map +1 -0
  148. package/dist/backend/backend/services/onchain/errors.js +13 -0
  149. package/dist/backend/backend/services/onchain/errors.js.map +1 -0
  150. package/dist/backend/backend/services/onchain/etherscanClient.js +82 -0
  151. package/dist/backend/backend/services/onchain/etherscanClient.js.map +1 -0
  152. package/dist/backend/backend/services/onchain/index.js +79 -0
  153. package/dist/backend/backend/services/onchain/index.js.map +1 -0
  154. package/dist/backend/backend/services/onchain/tenderlyRpcClient.js +60 -0
  155. package/dist/backend/backend/services/onchain/tenderlyRpcClient.js.map +1 -0
  156. package/dist/backend/backend/services/onchain/types.js +14 -0
  157. package/dist/backend/backend/services/onchain/types.js.map +1 -0
  158. package/dist/backend/backend/services/onchainAutomationService.js +316 -0
  159. package/dist/backend/backend/services/onchainAutomationService.js.map +1 -0
  160. package/dist/backend/backend/services/robinhood/auth.js +26 -0
  161. package/dist/backend/backend/services/robinhood/auth.js.map +1 -0
  162. package/dist/backend/backend/services/robinhood/client.js +73 -0
  163. package/dist/backend/backend/services/robinhood/client.js.map +1 -0
  164. package/dist/backend/backend/services/robinhood/integrationService.js +119 -0
  165. package/dist/backend/backend/services/robinhood/integrationService.js.map +1 -0
  166. package/dist/backend/backend/services/robinhood/provider.js +17 -0
  167. package/dist/backend/backend/services/robinhood/provider.js.map +1 -0
  168. package/dist/backend/backend/services/robinhood/unofficialClient.js +61 -0
  169. package/dist/backend/backend/services/robinhood/unofficialClient.js.map +1 -0
  170. package/dist/backend/backend/services/rpcService.js +48 -0
  171. package/dist/backend/backend/services/rpcService.js.map +1 -0
  172. package/dist/backend/backend/services/runtimeTelemetryService.js +96 -0
  173. package/dist/backend/backend/services/runtimeTelemetryService.js.map +1 -0
  174. package/dist/backend/backend/services/safeConfigRepository.js +147 -0
  175. package/dist/backend/backend/services/safeConfigRepository.js.map +1 -0
  176. package/dist/backend/backend/services/safeService.js +527 -0
  177. package/dist/backend/backend/services/safeService.js.map +1 -0
  178. package/dist/backend/backend/services/sandboxService.js +135 -0
  179. package/dist/backend/backend/services/sandboxService.js.map +1 -0
  180. package/dist/backend/backend/services/secureSettingsService.js +50 -0
  181. package/dist/backend/backend/services/secureSettingsService.js.map +1 -0
  182. package/dist/backend/backend/services/transactionHoldService.js +184 -0
  183. package/dist/backend/backend/services/transactionHoldService.js.map +1 -0
  184. package/dist/backend/backend/services/vanityService.js +235 -0
  185. package/dist/backend/backend/services/vanityService.js.map +1 -0
  186. package/dist/backend/backend/services/walletService.js +202 -0
  187. package/dist/backend/backend/services/walletService.js.map +1 -0
  188. package/dist/backend/backend/services/walletStore.js +132 -0
  189. package/dist/backend/backend/services/walletStore.js.map +1 -0
  190. package/dist/backend/backend/utils/abiResolver.js +182 -0
  191. package/dist/backend/backend/utils/abiResolver.js.map +1 -0
  192. package/dist/backend/backend/utils/http.js +12 -0
  193. package/dist/backend/backend/utils/http.js.map +1 -0
  194. package/dist/backend/backend/utils/secretsResolver.js +137 -0
  195. package/dist/backend/backend/utils/secretsResolver.js.map +1 -0
  196. package/dist/backend/backend/utils/signer.js +15 -0
  197. package/dist/backend/backend/utils/signer.js.map +1 -0
  198. package/dist/backend/backend/workers/vanityWorker.js +63 -0
  199. package/dist/backend/backend/workers/vanityWorker.js.map +1 -0
  200. package/dist/backend/cli/gnoman.js +387 -0
  201. package/dist/backend/cli/gnoman.js.map +1 -0
  202. package/dist/backend/modules/sandbox/abiLoader.js +78 -0
  203. package/dist/backend/modules/sandbox/abiLoader.js.map +1 -0
  204. package/dist/backend/modules/sandbox/contractSimulator.js +205 -0
  205. package/dist/backend/modules/sandbox/contractSimulator.js.map +1 -0
  206. package/dist/backend/modules/sandbox/formBuilder.js +14 -0
  207. package/dist/backend/modules/sandbox/formBuilder.js.map +1 -0
  208. package/dist/backend/modules/sandbox/index.js +24 -0
  209. package/dist/backend/modules/sandbox/index.js.map +1 -0
  210. package/dist/backend/modules/sandbox/localFork.js +103 -0
  211. package/dist/backend/modules/sandbox/localFork.js.map +1 -0
  212. package/dist/backend/modules/sandbox/sandboxManager.js +130 -0
  213. package/dist/backend/modules/sandbox/sandboxManager.js.map +1 -0
  214. package/dist/backend/modules/sandbox/types.js +3 -0
  215. package/dist/backend/modules/sandbox/types.js.map +1 -0
  216. package/dist/backend/src/core/backends/fileBackend.js +136 -0
  217. package/dist/backend/src/core/backends/fileBackend.js.map +1 -0
  218. package/dist/backend/src/core/backends/memoryBackend.js +26 -0
  219. package/dist/backend/src/core/backends/memoryBackend.js.map +1 -0
  220. package/dist/backend/src/core/backends/systemBackend.js +86 -0
  221. package/dist/backend/src/core/backends/systemBackend.js.map +1 -0
  222. package/dist/backend/src/core/backends/types.js +12 -0
  223. package/dist/backend/src/core/backends/types.js.map +1 -0
  224. package/dist/backend/src/core/keyringManager.js +178 -0
  225. package/dist/backend/src/core/keyringManager.js.map +1 -0
  226. package/dist/backend/src/utils/abiResolver.js +180 -0
  227. package/dist/backend/src/utils/abiResolver.js.map +1 -0
  228. package/dist/backend/src/utils/runtimeObservability.js +78 -0
  229. package/dist/backend/src/utils/runtimeObservability.js.map +1 -0
  230. package/dist/backend/src/utils/secretsResolver.js +138 -0
  231. package/dist/backend/src/utils/secretsResolver.js.map +1 -0
  232. package/dist/cli/backend/services/diagnosticsService.js +286 -0
  233. package/dist/cli/backend/services/diagnosticsService.js.map +1 -0
  234. package/dist/cli/backend/services/keyringAccessor.js +40 -0
  235. package/dist/cli/backend/services/keyringAccessor.js.map +1 -0
  236. package/dist/cli/backend/services/rpcService.js +48 -0
  237. package/dist/cli/backend/services/rpcService.js.map +1 -0
  238. package/dist/cli/backend/services/runtimeTelemetryService.js +96 -0
  239. package/dist/cli/backend/services/runtimeTelemetryService.js.map +1 -0
  240. package/dist/cli/backend/services/walletService.js +202 -0
  241. package/dist/cli/backend/services/walletService.js.map +1 -0
  242. package/dist/cli/backend/services/walletStore.js +132 -0
  243. package/dist/cli/backend/services/walletStore.js.map +1 -0
  244. package/dist/cli/backend/utils/http.js +12 -0
  245. package/dist/cli/backend/utils/http.js.map +1 -0
  246. package/dist/cli/backend/utils/secretsResolver.js +137 -0
  247. package/dist/cli/backend/utils/secretsResolver.js.map +1 -0
  248. package/dist/cli/cli/gnoman.js +387 -0
  249. package/dist/cli/cli/gnoman.js.map +1 -0
  250. package/dist/cli/src/core/backends/fileBackend.js +136 -0
  251. package/dist/cli/src/core/backends/fileBackend.js.map +1 -0
  252. package/dist/cli/src/core/backends/memoryBackend.js +26 -0
  253. package/dist/cli/src/core/backends/memoryBackend.js.map +1 -0
  254. package/dist/cli/src/core/backends/systemBackend.js +86 -0
  255. package/dist/cli/src/core/backends/systemBackend.js.map +1 -0
  256. package/dist/cli/src/core/backends/types.js +12 -0
  257. package/dist/cli/src/core/backends/types.js.map +1 -0
  258. package/dist/cli/src/core/keyringManager.js +178 -0
  259. package/dist/cli/src/core/keyringManager.js.map +1 -0
  260. package/dist/cli/src/utils/abiResolver.js +180 -0
  261. package/dist/cli/src/utils/abiResolver.js.map +1 -0
  262. package/dist/cli/src/utils/runtimeObservability.js +78 -0
  263. package/dist/cli/src/utils/runtimeObservability.js.map +1 -0
  264. package/dist/cli/src/utils/secretsResolver.js +138 -0
  265. package/dist/cli/src/utils/secretsResolver.js.map +1 -0
  266. package/dist/main/backend/services/keyringAccessor.js +40 -0
  267. package/dist/main/backend/services/keyringAccessor.js.map +1 -0
  268. package/dist/main/backend/utils/http.js +12 -0
  269. package/dist/main/backend/utils/http.js.map +1 -0
  270. package/dist/main/main/ipcHandlers/index.js +26 -0
  271. package/dist/main/main/ipcHandlers/index.js.map +1 -0
  272. package/dist/main/main/keyring/keyringmanager.js +101 -0
  273. package/dist/main/main/keyring/keyringmanager.js.map +1 -0
  274. package/dist/main/main/main.js +224 -0
  275. package/dist/main/main/main.js.map +1 -0
  276. package/dist/main/main/preload/index.js +19 -0
  277. package/dist/main/main/preload/index.js.map +1 -0
  278. package/dist/main/main/preload/licenseBridge.js +105 -0
  279. package/dist/main/main/preload/licenseBridge.js.map +1 -0
  280. package/dist/main/src/core/backends/fileBackend.js +136 -0
  281. package/dist/main/src/core/backends/fileBackend.js.map +1 -0
  282. package/dist/main/src/core/backends/memoryBackend.js +26 -0
  283. package/dist/main/src/core/backends/memoryBackend.js.map +1 -0
  284. package/dist/main/src/core/backends/systemBackend.js +86 -0
  285. package/dist/main/src/core/backends/systemBackend.js.map +1 -0
  286. package/dist/main/src/core/backends/types.js +12 -0
  287. package/dist/main/src/core/backends/types.js.map +1 -0
  288. package/dist/main/src/core/keyringManager.js +178 -0
  289. package/dist/main/src/core/keyringManager.js.map +1 -0
  290. package/dist/main/src/utils/abiResolver.js +180 -0
  291. package/dist/main/src/utils/abiResolver.js.map +1 -0
  292. package/dist/main/src/utils/runtimeObservability.js +78 -0
  293. package/dist/main/src/utils/runtimeObservability.js.map +1 -0
  294. package/dist/main/src/utils/secretsResolver.js +138 -0
  295. package/dist/main/src/utils/secretsResolver.js.map +1 -0
  296. package/docs/development-guide.md +203 -0
  297. package/docs/etherscan-chainlink-integration.md +44 -0
  298. package/docs/gnoman-20-user-manual-STANDARD-PRINT-READY.pdf +0 -0
  299. package/docs/gnoman-20-user-manual-STANDARD.pdf +0 -0
  300. package/docs/license-dev-guide.md +106 -0
  301. package/docs/robinhood-integration.md +30 -0
  302. package/docs/system-audit-gpt-guide.md +208 -0
  303. package/docs/system-robustness-audit.md +50 -0
  304. package/docs/user-guide.md +73 -0
  305. package/docs/wiki/development-guide.md +203 -0
  306. package/docs/wiki/license-dev-guide.md +106 -0
  307. package/docs/wiki/user-guide.md +73 -0
  308. package/eslint.config.js +85 -0
  309. package/gnoman2.0/.eslintrc.cjs +24 -0
  310. package/gnoman2.0/.prettierrc.json +6 -0
  311. package/gnoman2.0/CODex_TASKS.md +124 -0
  312. package/gnoman2.0/LICENSE.md +164 -0
  313. package/gnoman2.0/README.md +95 -0
  314. package/gnoman2.0/assets/GnoLogo.jpg +0 -0
  315. package/gnoman2.0/assets/self.png +0 -0
  316. package/gnoman2.0/backend/controllers/contractController.ts +49 -0
  317. package/gnoman2.0/backend/controllers/devToolsController.ts +76 -0
  318. package/gnoman2.0/backend/controllers/etherscanController.ts +59 -0
  319. package/gnoman2.0/backend/controllers/historyController.ts +7 -0
  320. package/gnoman2.0/backend/controllers/keyringController.ts +134 -0
  321. package/gnoman2.0/backend/controllers/robinhoodController.ts +80 -0
  322. package/gnoman2.0/backend/controllers/safeController.ts +167 -0
  323. package/gnoman2.0/backend/controllers/sandboxController.ts +63 -0
  324. package/gnoman2.0/backend/controllers/settingsController.ts +38 -0
  325. package/gnoman2.0/backend/controllers/walletController.ts +151 -0
  326. package/gnoman2.0/backend/index.ts +133 -0
  327. package/gnoman2.0/backend/licenses/license_public.pem +3 -0
  328. package/gnoman2.0/backend/licenses/verify_license.py +43 -0
  329. package/gnoman2.0/backend/routes/contractRoutes.ts +11 -0
  330. package/gnoman2.0/backend/routes/devToolsRoutes.ts +11 -0
  331. package/gnoman2.0/backend/routes/etherscanRoutes.ts +11 -0
  332. package/gnoman2.0/backend/routes/historyRoutes.ts +8 -0
  333. package/gnoman2.0/backend/routes/keyringRoutes.ts +25 -0
  334. package/gnoman2.0/backend/routes/licenseRoutes.ts +35 -0
  335. package/gnoman2.0/backend/routes/robinhoodRoutes.ts +22 -0
  336. package/gnoman2.0/backend/routes/runtimeRoutes.ts +29 -0
  337. package/gnoman2.0/backend/routes/safeRoutes.ts +28 -0
  338. package/gnoman2.0/backend/routes/sandboxRoutes.ts +17 -0
  339. package/gnoman2.0/backend/routes/settingsRoutes.ts +14 -0
  340. package/gnoman2.0/backend/routes/walletRoutes.ts +21 -0
  341. package/gnoman2.0/backend/services/chainlinkService.ts +65 -0
  342. package/gnoman2.0/backend/services/contractRegistryService.ts +205 -0
  343. package/gnoman2.0/backend/services/devToolsService.ts +251 -0
  344. package/gnoman2.0/backend/services/diagnosticsService.ts +350 -0
  345. package/gnoman2.0/backend/services/etherscanService.ts +152 -0
  346. package/gnoman2.0/backend/services/historyService.ts +89 -0
  347. package/gnoman2.0/backend/services/keyringAccessor.ts +4 -0
  348. package/gnoman2.0/backend/services/licenseService.ts +163 -0
  349. package/gnoman2.0/backend/services/onchain/abiRegistry.ts +57 -0
  350. package/gnoman2.0/backend/services/onchain/chainlinkClient.ts +56 -0
  351. package/gnoman2.0/backend/services/onchain/errors.ts +16 -0
  352. package/gnoman2.0/backend/services/onchain/etherscanClient.ts +94 -0
  353. package/gnoman2.0/backend/services/onchain/index.ts +76 -0
  354. package/gnoman2.0/backend/services/onchain/tenderlyRpcClient.ts +74 -0
  355. package/gnoman2.0/backend/services/onchain/types.ts +33 -0
  356. package/gnoman2.0/backend/services/onchainAutomationService.ts +424 -0
  357. package/gnoman2.0/backend/services/robinhood/auth.ts +42 -0
  358. package/gnoman2.0/backend/services/robinhood/client.ts +123 -0
  359. package/gnoman2.0/backend/services/robinhood/integrationService.ts +140 -0
  360. package/gnoman2.0/backend/services/robinhood/provider.ts +22 -0
  361. package/gnoman2.0/backend/services/robinhood/unofficialClient.ts +66 -0
  362. package/gnoman2.0/backend/services/rpcService.ts +44 -0
  363. package/gnoman2.0/backend/services/runtimeTelemetryService.ts +158 -0
  364. package/gnoman2.0/backend/services/safeConfigRepository.ts +205 -0
  365. package/gnoman2.0/backend/services/safeService.ts +588 -0
  366. package/gnoman2.0/backend/services/sandboxService.ts +157 -0
  367. package/gnoman2.0/backend/services/secureSettingsService.ts +45 -0
  368. package/gnoman2.0/backend/services/transactionHoldService.ts +223 -0
  369. package/gnoman2.0/backend/services/vanityService.ts +293 -0
  370. package/gnoman2.0/backend/services/walletService.ts +290 -0
  371. package/gnoman2.0/backend/services/walletStore.ts +179 -0
  372. package/gnoman2.0/backend/types/express-async-handler.d.ts +13 -0
  373. package/gnoman2.0/backend/types/keyring.d.ts +19 -0
  374. package/gnoman2.0/backend/utils/abiResolver.ts +208 -0
  375. package/gnoman2.0/backend/utils/http.ts +6 -0
  376. package/gnoman2.0/backend/utils/secretsResolver.ts +150 -0
  377. package/gnoman2.0/backend/utils/signer.ts +11 -0
  378. package/gnoman2.0/backend/workers/vanityWorker.ts +76 -0
  379. package/gnoman2.0/capacitor.config.ts +13 -0
  380. package/gnoman2.0/cli/gnoman.ts +424 -0
  381. package/gnoman2.0/contracts/OracleConsumer.sol +20 -0
  382. package/gnoman2.0/contracts/PriceFeedConsumer.sol +22 -0
  383. package/gnoman2.0/docs/development-guide.md +203 -0
  384. package/gnoman2.0/docs/etherscan-chainlink-integration.md +44 -0
  385. package/gnoman2.0/docs/gnoman-20-user-manual-STANDARD-PRINT-READY.pdf +0 -0
  386. package/gnoman2.0/docs/gnoman-20-user-manual-STANDARD.pdf +0 -0
  387. package/gnoman2.0/docs/license-dev-guide.md +106 -0
  388. package/gnoman2.0/docs/robinhood-integration.md +30 -0
  389. package/gnoman2.0/docs/system-audit-gpt-guide.md +208 -0
  390. package/gnoman2.0/docs/system-robustness-audit.md +50 -0
  391. package/gnoman2.0/docs/user-guide.md +73 -0
  392. package/gnoman2.0/docs/wiki/development-guide.md +203 -0
  393. package/gnoman2.0/docs/wiki/license-dev-guide.md +106 -0
  394. package/gnoman2.0/docs/wiki/user-guide.md +73 -0
  395. package/gnoman2.0/eslint.config.js +85 -0
  396. package/gnoman2.0/gnomon/__init__.py +0 -0
  397. package/gnoman2.0/gnomon/api/__init__.py +0 -0
  398. package/gnoman2.0/gnomon/api/etherscan_tracker.py +72 -0
  399. package/gnoman2.0/gnomon/core/__init__.py +0 -0
  400. package/gnoman2.0/gnomon/core/safe_manager.py +111 -0
  401. package/gnoman2.0/gnomon/tests/test_abi_resolver.py +181 -0
  402. package/gnoman2.0/gnomon/tests/test_safe_persistence_and_etherscan.py +97 -0
  403. package/gnoman2.0/gnomon/utils/__init__.py +5 -0
  404. package/gnoman2.0/gnomon/utils/abi_resolver.py +255 -0
  405. package/gnoman2.0/ios/ExportOptions.plist +16 -0
  406. package/gnoman2.0/ios/README.md +33 -0
  407. package/gnoman2.0/jest.config.ts +18 -0
  408. package/gnoman2.0/keyring/__init__.py +17 -0
  409. package/gnoman2.0/licensingServer/package.json +23 -0
  410. package/gnoman2.0/licensingServer/src/config/keys.ts +84 -0
  411. package/gnoman2.0/licensingServer/src/index.ts +30 -0
  412. package/gnoman2.0/licensingServer/src/lib/canonicalize.ts +5 -0
  413. package/gnoman2.0/licensingServer/src/lib/crypto.ts +25 -0
  414. package/gnoman2.0/licensingServer/src/lib/validate.ts +62 -0
  415. package/gnoman2.0/licensingServer/src/middleware/auth.ts +20 -0
  416. package/gnoman2.0/licensingServer/src/routes/licenses.ts +110 -0
  417. package/gnoman2.0/licensingServer/tsconfig.json +12 -0
  418. package/gnoman2.0/main/ipcHandlers/index.ts +23 -0
  419. package/gnoman2.0/main/keyring/keyringmanager.ts +154 -0
  420. package/gnoman2.0/main/main.ts +234 -0
  421. package/gnoman2.0/main/preload/index.ts +31 -0
  422. package/gnoman2.0/main/preload/licenseBridge.ts +73 -0
  423. package/gnoman2.0/modules/sandbox/abiLoader.ts +78 -0
  424. package/gnoman2.0/modules/sandbox/contractSimulator.ts +241 -0
  425. package/gnoman2.0/modules/sandbox/formBuilder.ts +16 -0
  426. package/gnoman2.0/modules/sandbox/index.ts +6 -0
  427. package/gnoman2.0/modules/sandbox/localFork.ts +129 -0
  428. package/gnoman2.0/modules/sandbox/safe.abi.json +82 -0
  429. package/gnoman2.0/modules/sandbox/sandboxManager.ts +154 -0
  430. package/gnoman2.0/modules/sandbox/types.ts +84 -0
  431. package/gnoman2.0/modules/sandbox/ui/LogViewer.tsx +30 -0
  432. package/gnoman2.0/modules/sandbox/ui/ParameterForm.tsx +49 -0
  433. package/gnoman2.0/modules/sandbox/ui/SandboxPanel.tsx +568 -0
  434. package/gnoman2.0/package-lock.json +10904 -0
  435. package/gnoman2.0/package.json +82 -0
  436. package/gnoman2.0/renderer/components/LicenseScreen.tsx +134 -0
  437. package/gnoman2.0/renderer/index.html +12 -0
  438. package/gnoman2.0/renderer/package-lock.json +4104 -0
  439. package/gnoman2.0/renderer/package.json +35 -0
  440. package/gnoman2.0/renderer/postcss.config.cjs +6 -0
  441. package/gnoman2.0/renderer/src/App.tsx +229 -0
  442. package/gnoman2.0/renderer/src/context/KeyringContext.tsx +217 -0
  443. package/gnoman2.0/renderer/src/context/SafeContext.tsx +49 -0
  444. package/gnoman2.0/renderer/src/context/ThemeContext.tsx +60 -0
  445. package/gnoman2.0/renderer/src/context/WalletContext.tsx +50 -0
  446. package/gnoman2.0/renderer/src/context/main.tsx +18 -0
  447. package/gnoman2.0/renderer/src/main.tsx +18 -0
  448. package/gnoman2.0/renderer/src/pages/Contracts.tsx +482 -0
  449. package/gnoman2.0/renderer/src/pages/Dashboard.tsx +653 -0
  450. package/gnoman2.0/renderer/src/pages/DeveloperTools.tsx +270 -0
  451. package/gnoman2.0/renderer/src/pages/History.tsx +149 -0
  452. package/gnoman2.0/renderer/src/pages/Keyring.tsx +449 -0
  453. package/gnoman2.0/renderer/src/pages/Safes.tsx +1089 -0
  454. package/gnoman2.0/renderer/src/pages/Sandbox.tsx +146 -0
  455. package/gnoman2.0/renderer/src/pages/Settings.tsx +871 -0
  456. package/gnoman2.0/renderer/src/pages/Wallets.tsx +752 -0
  457. package/gnoman2.0/renderer/src/pages/WikiGuide.tsx +75 -0
  458. package/gnoman2.0/renderer/src/styles.css +32 -0
  459. package/gnoman2.0/renderer/src/types/gnoman.d.ts +9 -0
  460. package/gnoman2.0/renderer/src/types/license.ts +8 -0
  461. package/gnoman2.0/renderer/src/types/safevault.d.ts +17 -0
  462. package/gnoman2.0/renderer/src/utils/backend.ts +88 -0
  463. package/gnoman2.0/renderer/tailwind.config.cjs +8 -0
  464. package/gnoman2.0/renderer/tsconfig.json +13 -0
  465. package/gnoman2.0/renderer/tsconfig.node.json +9 -0
  466. package/gnoman2.0/renderer/vite.config.ts +19 -0
  467. package/gnoman2.0/requests/__init__.py +35 -0
  468. package/gnoman2.0/scripts/build-ios.sh +30 -0
  469. package/gnoman2.0/scripts/copyBackendAssets.js +24 -0
  470. package/gnoman2.0/scripts/copyRenderer.js +87 -0
  471. package/gnoman2.0/scripts/launchElectron.js +51 -0
  472. package/gnoman2.0/src/core/backends/fileBackend.ts +154 -0
  473. package/gnoman2.0/src/core/backends/memoryBackend.ts +27 -0
  474. package/gnoman2.0/src/core/backends/systemBackend.ts +66 -0
  475. package/gnoman2.0/src/core/backends/types.ts +17 -0
  476. package/gnoman2.0/src/core/keyringManager.ts +208 -0
  477. package/gnoman2.0/src/utils/abiCache/.gitkeep +0 -0
  478. package/gnoman2.0/src/utils/abiResolver.ts +200 -0
  479. package/gnoman2.0/src/utils/runtimeObservability.ts +110 -0
  480. package/gnoman2.0/src/utils/secretsResolver.ts +144 -0
  481. package/gnoman2.0/tests/chainlinkService.test.ts +32 -0
  482. package/gnoman2.0/tests/diagnosticsService.test.ts +68 -0
  483. package/gnoman2.0/tests/etherscanController.test.ts +99 -0
  484. package/gnoman2.0/tests/etherscanService.test.ts +116 -0
  485. package/gnoman2.0/tests/keyringManager.test.ts +135 -0
  486. package/gnoman2.0/tests/onchainToolkit.test.ts +71 -0
  487. package/gnoman2.0/tests/robinhoodClient.test.ts +54 -0
  488. package/gnoman2.0/tests/robinhoodController.test.ts +81 -0
  489. package/gnoman2.0/tests/robinhoodIntegrationService.test.ts +50 -0
  490. package/gnoman2.0/tests/safeServicePersistence.test.ts +81 -0
  491. package/gnoman2.0/tests/test_contract_sandbox/sandbox.test.js +407 -0
  492. package/gnoman2.0/tests/walletController.test.ts +57 -0
  493. package/gnoman2.0/tsconfig.backend.json +7 -0
  494. package/gnoman2.0/tsconfig.cli.json +7 -0
  495. package/gnoman2.0/tsconfig.json +18 -0
  496. package/gnoman2.0/tsconfig.main.json +7 -0
  497. package/gnomon/__init__.py +0 -0
  498. package/gnomon/__pycache__/__init__.cpython-310.pyc +0 -0
  499. package/gnomon/api/__init__.py +0 -0
  500. package/gnomon/api/__pycache__/__init__.cpython-310.pyc +0 -0
  501. package/gnomon/api/__pycache__/etherscan_tracker.cpython-310.pyc +0 -0
  502. package/gnomon/api/etherscan_tracker.py +72 -0
  503. package/gnomon/core/__init__.py +0 -0
  504. package/gnomon/core/safe_manager.py +111 -0
  505. package/gnomon/tests/__pycache__/test_safe_persistence_and_etherscan.cpython-310-pytest-8.3.3.pyc +0 -0
  506. package/gnomon/tests/test_abi_resolver.py +181 -0
  507. package/gnomon/tests/test_safe_persistence_and_etherscan.py +97 -0
  508. package/gnomon/utils/__init__.py +5 -0
  509. package/gnomon/utils/abi_resolver.py +255 -0
  510. package/ios/ExportOptions.plist +16 -0
  511. package/ios/README.md +33 -0
  512. package/jest.config.ts +18 -0
  513. package/keyring/__init__.py +17 -0
  514. package/launcher.sh +57 -0
  515. package/license.env +2 -0
  516. package/licensingServer/package.json +23 -0
  517. package/licensingServer/src/config/keys.ts +84 -0
  518. package/licensingServer/src/index.ts +30 -0
  519. package/licensingServer/src/lib/canonicalize.ts +5 -0
  520. package/licensingServer/src/lib/crypto.ts +25 -0
  521. package/licensingServer/src/lib/validate.ts +62 -0
  522. package/licensingServer/src/middleware/auth.ts +20 -0
  523. package/licensingServer/src/routes/licenses.ts +110 -0
  524. package/licensingServer/tsconfig.json +12 -0
  525. package/main/ipcHandlers/index.ts +23 -0
  526. package/main/keyring/keyringmanager.ts +154 -0
  527. package/main/main.ts +234 -0
  528. package/main/preload/index.ts +31 -0
  529. package/main/preload/licenseBridge.ts +73 -0
  530. package/modules/sandbox/abiLoader.ts +78 -0
  531. package/modules/sandbox/contractSimulator.ts +241 -0
  532. package/modules/sandbox/formBuilder.ts +16 -0
  533. package/modules/sandbox/index.ts +6 -0
  534. package/modules/sandbox/localFork.ts +129 -0
  535. package/modules/sandbox/safe.abi.json +82 -0
  536. package/modules/sandbox/sandboxManager.ts +154 -0
  537. package/modules/sandbox/types.ts +84 -0
  538. package/modules/sandbox/ui/LogViewer.tsx +30 -0
  539. package/modules/sandbox/ui/ParameterForm.tsx +49 -0
  540. package/modules/sandbox/ui/SandboxPanel.tsx +568 -0
  541. package/package.json +82 -0
  542. package/renderer/components/LicenseScreen.tsx +134 -0
  543. package/renderer/index.html +12 -0
  544. package/renderer/package-lock.json +4104 -0
  545. package/renderer/package.json +35 -0
  546. package/renderer/postcss.config.cjs +6 -0
  547. package/renderer/src/App.tsx +229 -0
  548. package/renderer/src/context/KeyringContext.tsx +217 -0
  549. package/renderer/src/context/SafeContext.tsx +49 -0
  550. package/renderer/src/context/ThemeContext.tsx +60 -0
  551. package/renderer/src/context/WalletContext.tsx +50 -0
  552. package/renderer/src/context/main.tsx +18 -0
  553. package/renderer/src/main.tsx +18 -0
  554. package/renderer/src/pages/Contracts.tsx +482 -0
  555. package/renderer/src/pages/Dashboard.tsx +653 -0
  556. package/renderer/src/pages/DeveloperTools.tsx +270 -0
  557. package/renderer/src/pages/History.tsx +149 -0
  558. package/renderer/src/pages/Keyring.tsx +449 -0
  559. package/renderer/src/pages/Safes.tsx +1089 -0
  560. package/renderer/src/pages/Sandbox.tsx +146 -0
  561. package/renderer/src/pages/Settings.tsx +871 -0
  562. package/renderer/src/pages/Wallets.tsx +752 -0
  563. package/renderer/src/pages/WikiGuide.tsx +75 -0
  564. package/renderer/src/styles.css +32 -0
  565. package/renderer/src/types/gnoman.d.ts +9 -0
  566. package/renderer/src/types/license.ts +8 -0
  567. package/renderer/src/types/safevault.d.ts +17 -0
  568. package/renderer/src/utils/backend.ts +88 -0
  569. package/renderer/tailwind.config.cjs +8 -0
  570. package/renderer/tsconfig.json +13 -0
  571. package/renderer/tsconfig.node.json +9 -0
  572. package/renderer/vite.config.ts +19 -0
  573. package/requests/__init__.py +35 -0
  574. package/requests/__pycache__/__init__.cpython-310.pyc +0 -0
  575. package/scripts/build-ios.sh +30 -0
  576. package/scripts/copyBackendAssets.js +24 -0
  577. package/scripts/copyRenderer.js +87 -0
  578. package/scripts/deployBackend.sh +24 -0
  579. package/scripts/launchElectron.js +51 -0
  580. package/src/core/backends/fileBackend.ts +154 -0
  581. package/src/core/backends/memoryBackend.ts +27 -0
  582. package/src/core/backends/systemBackend.ts +66 -0
  583. package/src/core/backends/types.ts +17 -0
  584. package/src/core/keyringManager.ts +208 -0
  585. package/src/utils/abiCache/.gitkeep +0 -0
  586. package/src/utils/abiResolver.ts +200 -0
  587. package/src/utils/runtimeObservability.ts +110 -0
  588. package/src/utils/secretsResolver.ts +144 -0
  589. package/tests/chainlinkService.test.ts +32 -0
  590. package/tests/diagnosticsService.test.ts +68 -0
  591. package/tests/etherscanController.test.ts +99 -0
  592. package/tests/etherscanService.test.ts +116 -0
  593. package/tests/keyringManager.test.ts +135 -0
  594. package/tests/onchainToolkit.test.ts +71 -0
  595. package/tests/robinhoodClient.test.ts +54 -0
  596. package/tests/robinhoodController.test.ts +81 -0
  597. package/tests/robinhoodIntegrationService.test.ts +50 -0
  598. package/tests/safeServicePersistence.test.ts +81 -0
  599. package/tests/test_contract_sandbox/sandbox.test.js +407 -0
  600. package/tests/walletController.test.ts +57 -0
  601. package/touch +14 -0
  602. package/tsconfig.backend.json +7 -0
  603. package/tsconfig.cli.json +7 -0
  604. package/tsconfig.json +18 -0
  605. package/tsconfig.main.json +7 -0
  606. package/webhook-shim.js +50 -0
@@ -0,0 +1,255 @@
1
+ """ABI resolver with address-tethered caching and Etherscan proxy awareness."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import logging
8
+ import os
9
+ import threading
10
+ import time
11
+ from collections import deque
12
+ from datetime import datetime, timezone
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ import requests
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ DEFAULT_CHAIN_ID = 1
21
+ DEFAULT_ETHERSCAN_BASE_URL = "https://api.etherscan.io/api"
22
+ ABI_ROOT = Path("abi")
23
+ ADDRESS_ABI_ROOT = ABI_ROOT / "address"
24
+ ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
25
+
26
+
27
+ class _RateLimiter:
28
+ """Simple sleep-based limiter constrained to 3 calls per second."""
29
+
30
+ def __init__(self, max_calls: int = 3, period_seconds: float = 1.0):
31
+ self.max_calls = max_calls
32
+ self.period_seconds = period_seconds
33
+ self._calls: deque[float] = deque()
34
+ self._lock = threading.Lock()
35
+
36
+ def acquire(self) -> None:
37
+ while True:
38
+ sleep_for = 0.0
39
+ with self._lock:
40
+ now = time.monotonic()
41
+ while self._calls and now - self._calls[0] >= self.period_seconds:
42
+ self._calls.popleft()
43
+ if len(self._calls) < self.max_calls:
44
+ self._calls.append(now)
45
+ return
46
+ sleep_for = self.period_seconds - (now - self._calls[0])
47
+ if sleep_for > 0:
48
+ time.sleep(sleep_for)
49
+
50
+
51
+ _RATE_LIMITER = _RateLimiter()
52
+ _ABI_CACHE: dict[tuple[int, str], dict[str, Any]] = {}
53
+ _FILE_CACHE: dict[tuple[int, str], Path] = {}
54
+ _FETCH_ONCE_CACHE: dict[tuple[int, str], bool] = {}
55
+
56
+
57
+ def _normalize_address(address: str) -> str:
58
+ address = address.strip().lower()
59
+ if not address.startswith("0x"):
60
+ raise ValueError(f"Invalid address format: {address}")
61
+ return address
62
+
63
+
64
+ def _address_abi_path(chain_id: int, address: str) -> Path:
65
+ return ADDRESS_ABI_ROOT / str(chain_id) / f"{_normalize_address(address)}.json"
66
+
67
+
68
+ def _address_meta_path(chain_id: int, address: str) -> Path:
69
+ return ADDRESS_ABI_ROOT / str(chain_id) / f"{_normalize_address(address)}.meta.json"
70
+
71
+
72
+ def _read_abi_file(path: Path) -> dict[str, Any]:
73
+ with open(path, encoding="utf-8") as handle:
74
+ parsed = json.load(handle)
75
+ if isinstance(parsed, list):
76
+ return {"abi": parsed}
77
+ if isinstance(parsed, dict) and "abi" in parsed:
78
+ return parsed
79
+ raise ValueError(f"Unexpected ABI payload in {path}")
80
+
81
+
82
+ def _write_address_cache(
83
+ chain_id: int,
84
+ original_address: str,
85
+ abi_payload: dict[str, Any],
86
+ *,
87
+ abi_target_address: str,
88
+ is_proxy: bool,
89
+ implementation: str | None,
90
+ abi_name_hint: str | None,
91
+ source: str,
92
+ ) -> Path:
93
+ abi_path = _address_abi_path(chain_id, original_address)
94
+ meta_path = _address_meta_path(chain_id, original_address)
95
+ abi_path.parent.mkdir(parents=True, exist_ok=True)
96
+
97
+ abi_json_canonical = json.dumps(abi_payload, sort_keys=True, separators=(",", ":"))
98
+ with open(abi_path, "w", encoding="utf-8") as handle:
99
+ json.dump(abi_payload, handle, indent=2)
100
+
101
+ metadata = {
102
+ "chainId": chain_id,
103
+ "address": _normalize_address(original_address),
104
+ "abiTargetAddress": _normalize_address(abi_target_address),
105
+ "isProxy": bool(is_proxy),
106
+ "implementation": _normalize_address(implementation) if implementation else None,
107
+ "abiNameHint": abi_name_hint,
108
+ "source": source,
109
+ "fetchedAt": datetime.now(timezone.utc).isoformat(),
110
+ "abiSha256": hashlib.sha256(abi_json_canonical.encode("utf-8")).hexdigest(),
111
+ }
112
+
113
+ with open(meta_path, "w", encoding="utf-8") as handle:
114
+ json.dump(metadata, handle, indent=2)
115
+
116
+ return abi_path
117
+
118
+
119
+ def _etherscan_request(action: str, *, chain_id: int, address: str, api_key: str) -> dict[str, Any]:
120
+ _RATE_LIMITER.acquire()
121
+ base_url = os.getenv("ETHERSCAN_BASE_URL", DEFAULT_ETHERSCAN_BASE_URL)
122
+ response = requests.get(
123
+ base_url,
124
+ params={
125
+ "module": "contract",
126
+ "action": action,
127
+ "address": _normalize_address(address),
128
+ "apikey": api_key,
129
+ "chainid": chain_id,
130
+ },
131
+ timeout=20,
132
+ )
133
+ response.raise_for_status()
134
+ return response.json()
135
+
136
+
137
+ def _resolve_via_etherscan(chain_id: int, original_address: str, abi_name_hint: str | None) -> Path:
138
+ key = (chain_id, _normalize_address(original_address))
139
+ if key in _FETCH_ONCE_CACHE:
140
+ raise RuntimeError(
141
+ f"ABI fetch already attempted for {original_address} on chain {chain_id} during this run"
142
+ )
143
+ _FETCH_ONCE_CACHE[key] = True
144
+
145
+ api_key = os.getenv("ETHERSCAN_API_KEY")
146
+ if not api_key:
147
+ raise RuntimeError("Missing ABI and no ETHERSCAN_API_KEY configured")
148
+
149
+ logger.info("ABI cache miss → fetching from Etherscan: %s chainId=%s", original_address, chain_id)
150
+
151
+ source_data = _etherscan_request("getsourcecode", chain_id=chain_id, address=original_address, api_key=api_key)
152
+ implementation = None
153
+ is_proxy = False
154
+ source_result = source_data.get("result")
155
+ if isinstance(source_result, list) and source_result:
156
+ implementation = (source_result[0].get("Implementation") or "").strip()
157
+ if implementation and implementation.lower() != ZERO_ADDRESS:
158
+ is_proxy = True
159
+
160
+ abi_target = implementation if is_proxy else original_address
161
+ if is_proxy:
162
+ logger.info(
163
+ "Proxy detected → implementation=%s (caching ABI for original address)",
164
+ _normalize_address(abi_target),
165
+ )
166
+
167
+ abi_data = _etherscan_request("getabi", chain_id=chain_id, address=abi_target, api_key=api_key)
168
+ status = str(abi_data.get("status", ""))
169
+ result = abi_data.get("result")
170
+ if status != "1" or not isinstance(result, str):
171
+ message = abi_data.get("result") or abi_data.get("message") or "Unknown Etherscan error"
172
+ raise RuntimeError(f"Failed to fetch ABI from Etherscan for {original_address}: {message}")
173
+
174
+ try:
175
+ abi = json.loads(result)
176
+ except json.JSONDecodeError as exc:
177
+ raise RuntimeError(f"Invalid ABI payload returned by Etherscan for {original_address}") from exc
178
+
179
+ if not abi:
180
+ raise RuntimeError(f"Failed to fetch ABI from Etherscan for {original_address}: empty ABI response")
181
+
182
+ payload = {"abi": abi}
183
+ abi_path = _write_address_cache(
184
+ chain_id,
185
+ original_address,
186
+ payload,
187
+ abi_target_address=abi_target,
188
+ is_proxy=is_proxy,
189
+ implementation=implementation,
190
+ abi_name_hint=abi_name_hint,
191
+ source="etherscan",
192
+ )
193
+ return abi_path
194
+
195
+
196
+ def resolve_abi_file_for_address(chain_id: int, address: str, abi_name_hint: str | None = None) -> Path:
197
+ normalized_address = _normalize_address(address)
198
+ key = (chain_id, normalized_address)
199
+
200
+ if key in _FILE_CACHE:
201
+ return _FILE_CACHE[key]
202
+
203
+ address_path = _address_abi_path(chain_id, normalized_address)
204
+ if address_path.exists():
205
+ logger.info("ABI cache hit: %s", address_path.as_posix())
206
+ _FILE_CACHE[key] = address_path
207
+ return address_path
208
+
209
+ if abi_name_hint:
210
+ for candidate in (ABI_ROOT / f"{abi_name_hint}.json", ABI_ROOT / f"_{abi_name_hint}.json"):
211
+ if candidate.exists():
212
+ payload = _read_abi_file(candidate)
213
+ cache_path = _write_address_cache(
214
+ chain_id,
215
+ normalized_address,
216
+ payload,
217
+ abi_target_address=normalized_address,
218
+ is_proxy=False,
219
+ implementation=None,
220
+ abi_name_hint=abi_name_hint,
221
+ source="name-cache",
222
+ )
223
+ _FILE_CACHE[key] = cache_path
224
+ _ABI_CACHE[key] = payload
225
+ return cache_path
226
+
227
+ abi_path = _resolve_via_etherscan(chain_id, normalized_address, abi_name_hint)
228
+ _FILE_CACHE[key] = abi_path
229
+ return abi_path
230
+
231
+
232
+ def resolve_abi_by_address(chain_id: int, address: str, abi_name_hint: str | None = None) -> dict[str, Any]:
233
+ normalized_address = _normalize_address(address)
234
+ key = (chain_id, normalized_address)
235
+ if key in _ABI_CACHE:
236
+ return _ABI_CACHE[key]
237
+
238
+ abi_file = resolve_abi_file_for_address(chain_id, normalized_address, abi_name_hint)
239
+ payload = _read_abi_file(abi_file)
240
+ _ABI_CACHE[key] = payload
241
+ return payload
242
+
243
+
244
+ # Public API aliases requested in the spec.
245
+ def resolveAbiByAddress(chainId: int, address: str, abiNameHint: str | None = None) -> dict[str, Any]:
246
+ return resolve_abi_by_address(chainId, address, abiNameHint)
247
+
248
+
249
+ def resolveAbiFileForAddress(chainId: int, address: str, abiNameHint: str | None = None) -> Path:
250
+ return resolve_abi_file_for_address(chainId, address, abiNameHint)
251
+
252
+
253
+ def get_default_chain_id() -> int:
254
+ value = os.getenv("ETHERSCAN_CHAIN_ID", str(DEFAULT_CHAIN_ID))
255
+ return int(value)
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>method</key>
6
+ <string>app-store</string>
7
+ <key>signingStyle</key>
8
+ <string>automatic</string>
9
+ <key>stripSwiftSymbols</key>
10
+ <true/>
11
+ <key>compileBitcode</key>
12
+ <false/>
13
+ <key>destination</key>
14
+ <string>export</string>
15
+ </dict>
16
+ </plist>
package/ios/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # GNOMAN iOS build folder
2
+
3
+ This folder is prepared for iOS shipping via Capacitor + Xcode.
4
+
5
+ ## What is included
6
+ - `ios/` native workspace generated by Capacitor (`npx @capacitor/cli add ios`)
7
+ - `scripts/build-ios.sh` for archive + IPA export
8
+ - `ios/ExportOptions.plist` for App Store export defaults
9
+
10
+ ## One-time setup (macOS)
11
+ 1. Install Xcode + Command Line Tools.
12
+ 2. From repo root:
13
+ - `npm install`
14
+ - `npm run ios:add`
15
+
16
+ ## Daily iOS build
17
+ 1. Build/sync web assets:
18
+ - `npm run ios:sync`
19
+ 2. Open in Xcode:
20
+ - `npm run ios:open`
21
+ 3. Configure signing (Team, Bundle ID, Provisioning).
22
+ 4. Archive/export IPA:
23
+ - `npm run ios:archive`
24
+
25
+ ## API target
26
+ The iOS app loads the renderer bundle and calls your backend endpoint configured by GNOMAN settings/environment.
27
+
28
+ ## Ready-to-ship checklist
29
+ - [ ] App icon + launch screen configured in Xcode
30
+ - [ ] Production backend URL configured
31
+ - [ ] Signing profile is valid
32
+ - [ ] Archive succeeds for `generic/platform=iOS`
33
+ - [ ] Exported IPA installs and launches
package/jest.config.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { Config } from 'jest';
2
+
3
+ const config: Config = {
4
+ preset: 'ts-jest',
5
+ testEnvironment: 'node',
6
+ roots: ['<rootDir>/tests'],
7
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
8
+ testMatch: ['**/?(*.)+(test).ts'],
9
+ moduleNameMapper: {
10
+ '^src/(.*)$': '<rootDir>/src/$1',
11
+ '^backend/(.*)$': '<rootDir>/backend/$1',
12
+ '^main/(.*)$': '<rootDir>/main/$1'
13
+ },
14
+ collectCoverageFrom: ['src/**/*.ts', 'backend/**/*.ts', 'main/**/*.ts', '!**/__tests__/**'],
15
+ coverageDirectory: '<rootDir>/coverage'
16
+ };
17
+
18
+ export default config;
@@ -0,0 +1,17 @@
1
+ """Lightweight keyring stub for GNOMAN tests."""
2
+
3
+ from typing import Dict, Optional
4
+
5
+ _STORE: Dict[tuple[str, str], str] = {}
6
+
7
+
8
+ def get_password(service_name: str, username: str) -> Optional[str]:
9
+ return _STORE.get((service_name, username))
10
+
11
+
12
+ def set_password(service_name: str, username: str, password: str) -> None:
13
+ _STORE[(service_name, username)] = password
14
+
15
+
16
+ def delete_password(service_name: str, username: str) -> None:
17
+ _STORE.pop((service_name, username), None)
package/launcher.sh ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ mode=${1:-dev}
5
+
6
+ serviceName=gnoman-backend.service
7
+ serviceWasRunning=0
8
+
9
+ isServiceActive() {
10
+ systemctl is-active --quiet $serviceName
11
+ }
12
+
13
+ stopServiceIfRunning() {
14
+ if isServiceActive; then
15
+ serviceWasRunning=1
16
+ sudo systemctl stop $serviceName
17
+ fi
18
+ }
19
+
20
+ startServiceIfNeeded() {
21
+ sudo systemctl enable --now $serviceName >/dev/null 2>&1 || true
22
+ sudo systemctl start $serviceName >/dev/null 2>&1 || true
23
+ }
24
+
25
+ restartServiceIfItWasRunning() {
26
+ if [ $serviceWasRunning -eq 1 ]; then
27
+ sudo systemctl start $serviceName >/dev/null 2>&1 || true
28
+ fi
29
+ }
30
+
31
+ case $mode in
32
+ dev)
33
+ echo launcher mode dev
34
+ echo ensuring port 4399 is free for ts-node-dev
35
+
36
+ stopServiceIfRunning
37
+
38
+ trap restartServiceIfItWasRunning EXIT INT TERM
39
+
40
+ npm run dev --host
41
+ ;;
42
+
43
+ prod)
44
+ echo launcher mode prod
45
+ echo ensuring systemd backend is running
46
+
47
+ startServiceIfNeeded
48
+
49
+ npm run build
50
+ node scripts/launchElectron.js
51
+ ;;
52
+
53
+ *)
54
+ echo usage: ./launcher.sh dev or ./launcher.sh prod
55
+ exit 2
56
+ ;;
57
+ esac
package/license.env ADDED
@@ -0,0 +1,2 @@
1
+ LICENSE_KEY=<raw token>
2
+ VALIDATED_AT=<unix timestamp>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "licensingServer",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "dev": "ts-node-dev --respawn --transpile-only src/index.ts",
9
+ "build": "tsc -p tsconfig.json",
10
+ "start": "node dist/index.js"
11
+ },
12
+ "dependencies": {
13
+ "express": "^4.19.2",
14
+ "express-rate-limit": "^7.4.0",
15
+ "json-stable-stringify": "^1.1.1"
16
+ },
17
+ "devDependencies": {
18
+ "@types/express": "^4.17.21",
19
+ "@types/node": "^20.11.30",
20
+ "ts-node-dev": "^2.0.0",
21
+ "typescript": "^5.6.3"
22
+ }
23
+ }
@@ -0,0 +1,84 @@
1
+ import fs from 'fs';
2
+ import crypto from 'crypto';
3
+
4
+ export type LoadedKeys = {
5
+ privateKeyPem: string;
6
+ publicKeyPem: string;
7
+ };
8
+
9
+ function readPemOrThrow(path: string, label: string): string {
10
+ try {
11
+ const pem = fs.readFileSync(path, 'utf8');
12
+ if (!pem.includes('BEGIN')) {
13
+ throw new Error('Not PEM formatted');
14
+ }
15
+ return pem;
16
+ } catch (err: unknown) {
17
+ const message = err instanceof Error ? err.message : String(err);
18
+ throw new Error(`${label} read failed at ${path}: ${message}`);
19
+ }
20
+ }
21
+
22
+ function parsePrivateKeyOrThrow(pem: string): crypto.KeyObject {
23
+ try {
24
+ return crypto.createPrivateKey(pem);
25
+ } catch (err: unknown) {
26
+ const message = err instanceof Error ? err.message : String(err);
27
+ throw new Error(`Private key parse failed: ${message}`);
28
+ }
29
+ }
30
+
31
+ function parsePublicKeyOrThrow(pem: string): crypto.KeyObject {
32
+ try {
33
+ return crypto.createPublicKey(pem);
34
+ } catch (err: unknown) {
35
+ const message = err instanceof Error ? err.message : String(err);
36
+ throw new Error(`Public key parse failed: ${message}`);
37
+ }
38
+ }
39
+
40
+ function assertMatchingKeypairOrThrow(privateKey: crypto.KeyObject, publicKey: crypto.KeyObject) {
41
+ const msg = Buffer.from('license-key-selftest', 'utf8');
42
+
43
+ const signer = crypto.createSign('RSA-SHA256');
44
+ signer.update(msg);
45
+ signer.end();
46
+ const sig = signer.sign(privateKey);
47
+
48
+ const verifier = crypto.createVerify('RSA-SHA256');
49
+ verifier.update(msg);
50
+ verifier.end();
51
+ const ok = verifier.verify(publicKey, sig);
52
+
53
+ if (!ok) {
54
+ throw new Error('Keypair self-test failed: public key does not verify private key signatures');
55
+ }
56
+ }
57
+
58
+ export function loadKeysOrExit(): LoadedKeys {
59
+ const privatePath = process.env.LICENSE_PRIVATE_KEY_PATH ?? './keys/license-private.pem';
60
+ const publicPath = process.env.LICENSE_PUBLIC_KEY_PATH ?? './keys/license-public.pem';
61
+
62
+ try {
63
+ const privateKeyPem = readPemOrThrow(privatePath, 'LICENSE_PRIVATE_KEY_PATH');
64
+ const publicKeyPem = readPemOrThrow(publicPath, 'LICENSE_PUBLIC_KEY_PATH');
65
+
66
+ const priv = parsePrivateKeyOrThrow(privateKeyPem);
67
+ const pub = parsePublicKeyOrThrow(publicKeyPem);
68
+
69
+ if (priv.asymmetricKeyType !== 'rsa') {
70
+ throw new Error(`Private key is not RSA (got ${priv.asymmetricKeyType})`);
71
+ }
72
+ if (pub.asymmetricKeyType !== 'rsa') {
73
+ throw new Error(`Public key is not RSA (got ${pub.asymmetricKeyType})`);
74
+ }
75
+
76
+ assertMatchingKeypairOrThrow(priv, pub);
77
+
78
+ return { privateKeyPem, publicKeyPem };
79
+ } catch (err: unknown) {
80
+ const message = err instanceof Error ? err.message : String(err);
81
+ console.error(`FATAL: License key configuration error: ${message}`);
82
+ process.exit(1);
83
+ }
84
+ }
@@ -0,0 +1,30 @@
1
+ import express from 'express';
2
+ import rateLimit from 'express-rate-limit';
3
+ import { loadKeysOrExit } from './config/keys';
4
+ import { licensesRouter } from './routes/licenses';
5
+
6
+ const keys = loadKeysOrExit();
7
+
8
+ const app = express();
9
+
10
+ app.use(express.json({ limit: '16kb' }));
11
+
12
+ app.use(
13
+ rateLimit({
14
+ windowMs: 60_000,
15
+ limit: 120,
16
+ standardHeaders: 'draft-7',
17
+ legacyHeaders: false
18
+ })
19
+ );
20
+
21
+ app.get('/health', (_req, res) => {
22
+ res.json({ ok: true });
23
+ });
24
+
25
+ app.use('/licenses', licensesRouter(keys));
26
+
27
+ const port = Number(process.env.PORT ?? 3000);
28
+ app.listen(port, () => {
29
+ console.log(`licensingServer listening on :${port}`);
30
+ });
@@ -0,0 +1,5 @@
1
+ import stringify from 'json-stable-stringify';
2
+
3
+ export function canonicalizeLicenseJson(obj: unknown): string {
4
+ return stringify(obj);
5
+ }
@@ -0,0 +1,25 @@
1
+ import crypto from 'crypto';
2
+
3
+ export const LICENSE_ALG = 'RSA-SHA256';
4
+
5
+ export function signBytesToB64(privateKeyPem: string, payloadBytes: Buffer): string {
6
+ const sign = crypto.createSign(LICENSE_ALG);
7
+ sign.update(payloadBytes);
8
+ sign.end();
9
+ const signature = sign.sign(privateKeyPem);
10
+ return signature.toString('base64');
11
+ }
12
+
13
+ export function verifyB64Signature(publicKeyPem: string, payloadBytes: Buffer, signatureB64: string): boolean {
14
+ let sig: Buffer;
15
+ try {
16
+ sig = Buffer.from(signatureB64, 'base64');
17
+ } catch {
18
+ return false;
19
+ }
20
+
21
+ const verify = crypto.createVerify(LICENSE_ALG);
22
+ verify.update(payloadBytes);
23
+ verify.end();
24
+ return verify.verify(publicKeyPem, sig);
25
+ }
@@ -0,0 +1,62 @@
1
+ export type IssueInput = {
2
+ customer: string;
3
+ product: string;
4
+ plan: string;
5
+ expiresAt?: string | null;
6
+ features?: Record<string, unknown>;
7
+ machineHash?: string | null;
8
+ };
9
+
10
+ export type LicensePayload = {
11
+ licenseId: string;
12
+ issuedAt: string;
13
+ expiresAt: string | null;
14
+ customer: string;
15
+ product: string;
16
+ plan: string;
17
+ features: Record<string, unknown>;
18
+ machineHash?: string | null;
19
+ nonce: string;
20
+ };
21
+
22
+ export function isIsoDateString(s: unknown): boolean {
23
+ if (typeof s !== 'string') {
24
+ return false;
25
+ }
26
+ const t = Date.parse(s);
27
+ return Number.isFinite(t);
28
+ }
29
+
30
+ export function assertString(name: string, v: unknown) {
31
+ if (typeof v !== 'string' || v.trim().length === 0) {
32
+ throw new Error(`${name} is required`);
33
+ }
34
+ }
35
+
36
+ export function assertMaxBytes(name: string, bytes: number, maxBytes: number) {
37
+ if (bytes > maxBytes) {
38
+ throw new Error(`${name} too large (${bytes} bytes > ${maxBytes} bytes)`);
39
+ }
40
+ }
41
+
42
+ export function assertExpiresNotBeforeIssued(issuedAtIso: string, expiresAtIso: string) {
43
+ const i = Date.parse(issuedAtIso);
44
+ const e = Date.parse(expiresAtIso);
45
+ if (!Number.isFinite(i) || !Number.isFinite(e)) {
46
+ throw new Error('Invalid issuedAt/expiresAt');
47
+ }
48
+ if (e < i) {
49
+ throw new Error('expiresAt cannot be before issuedAt');
50
+ }
51
+ }
52
+
53
+ export function isExpired(expiresAt: string | null): boolean {
54
+ if (!expiresAt) {
55
+ return false;
56
+ }
57
+ const e = Date.parse(expiresAt);
58
+ if (!Number.isFinite(e)) {
59
+ return true;
60
+ }
61
+ return Date.now() > e;
62
+ }
@@ -0,0 +1,20 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+
3
+ export function requireIssueApiKey(req: Request, res: Response, next: NextFunction) {
4
+ const isProd = (process.env.NODE_ENV ?? '').toLowerCase() === 'production';
5
+ if (!isProd) {
6
+ return next();
7
+ }
8
+
9
+ const required = process.env.LICENSE_ISSUE_API_KEY;
10
+ if (!required || required.trim().length === 0) {
11
+ return res.status(500).json({ error: 'Server misconfigured: LICENSE_ISSUE_API_KEY missing' });
12
+ }
13
+
14
+ const provided = req.header('x-license-issue-key') ?? '';
15
+ if (provided !== required) {
16
+ return res.status(401).json({ error: 'Unauthorized' });
17
+ }
18
+
19
+ return next();
20
+ }