chia-blockchain 2.5.0rc2__py3-none-any.whl → 2.5.1rc2__py3-none-any.whl

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 (637) hide show
  1. chia/_tests/README.md +1 -1
  2. chia/_tests/blockchain/blockchain_test_utils.py +24 -26
  3. chia/_tests/blockchain/test_augmented_chain.py +6 -8
  4. chia/_tests/blockchain/test_blockchain.py +409 -307
  5. chia/_tests/blockchain/test_blockchain_transactions.py +56 -75
  6. chia/_tests/blockchain/test_build_chains.py +11 -13
  7. chia/_tests/blockchain/test_get_block_generator.py +8 -8
  8. chia/_tests/blockchain/test_lookup_fork_chain.py +3 -4
  9. chia/_tests/build-init-files.py +3 -4
  10. chia/_tests/build-job-matrix.py +9 -9
  11. chia/_tests/check_sql_statements.py +2 -3
  12. chia/_tests/clvm/benchmark_costs.py +1 -1
  13. chia/_tests/clvm/coin_store.py +7 -5
  14. chia/_tests/clvm/test_chialisp_deserialization.py +8 -8
  15. chia/_tests/clvm/test_condition_codes.py +2 -2
  16. chia/_tests/clvm/test_curry_and_treehash.py +2 -4
  17. chia/_tests/clvm/test_message_conditions.py +184 -0
  18. chia/_tests/clvm/test_puzzle_compression.py +1 -2
  19. chia/_tests/clvm/test_puzzle_drivers.py +3 -3
  20. chia/_tests/clvm/test_puzzles.py +13 -18
  21. chia/_tests/clvm/test_singletons.py +17 -17
  22. chia/_tests/clvm/test_spend_sim.py +7 -7
  23. chia/_tests/cmds/cmd_test_utils.py +42 -45
  24. chia/_tests/cmds/conftest.py +2 -2
  25. chia/_tests/cmds/test_click_types.py +21 -16
  26. chia/_tests/cmds/test_cmd_framework.py +255 -35
  27. chia/_tests/cmds/test_cmds_util.py +2 -2
  28. chia/_tests/cmds/test_daemon.py +3 -3
  29. chia/_tests/cmds/test_dev_gh.py +131 -0
  30. chia/_tests/cmds/test_farm_cmd.py +1 -2
  31. chia/_tests/cmds/test_show.py +6 -6
  32. chia/_tests/cmds/test_tx_config_args.py +2 -1
  33. chia/_tests/cmds/wallet/test_dao.py +23 -23
  34. chia/_tests/cmds/wallet/test_did.py +29 -29
  35. chia/_tests/cmds/wallet/test_nft.py +24 -23
  36. chia/_tests/cmds/wallet/test_notifications.py +8 -8
  37. chia/_tests/cmds/wallet/test_tx_decorators.py +3 -3
  38. chia/_tests/cmds/wallet/test_vcs.py +97 -73
  39. chia/_tests/cmds/wallet/test_wallet.py +74 -75
  40. chia/_tests/cmds/wallet/test_wallet_check.py +5 -7
  41. chia/_tests/conftest.py +153 -38
  42. chia/_tests/connection_utils.py +7 -6
  43. chia/_tests/core/cmds/test_beta.py +3 -3
  44. chia/_tests/core/cmds/test_keys.py +6 -6
  45. chia/_tests/core/cmds/test_wallet.py +3 -3
  46. chia/_tests/core/consensus/test_block_creation.py +3 -5
  47. chia/_tests/core/custom_types/test_coin.py +1 -3
  48. chia/_tests/core/custom_types/test_spend_bundle.py +3 -4
  49. chia/_tests/core/daemon/test_daemon.py +58 -58
  50. chia/_tests/core/daemon/test_keychain_proxy.py +2 -1
  51. chia/_tests/core/data_layer/conftest.py +4 -3
  52. chia/_tests/core/data_layer/test_data_cli.py +1 -2
  53. chia/_tests/core/data_layer/test_data_layer.py +5 -5
  54. chia/_tests/core/data_layer/test_data_layer_util.py +8 -9
  55. chia/_tests/core/data_layer/test_data_rpc.py +75 -93
  56. chia/_tests/core/data_layer/test_data_store.py +38 -37
  57. chia/_tests/core/data_layer/test_data_store_schema.py +11 -11
  58. chia/_tests/core/data_layer/util.py +11 -10
  59. chia/_tests/core/farmer/test_farmer_api.py +6 -4
  60. chia/_tests/core/full_node/full_sync/test_full_sync.py +5 -10
  61. chia/_tests/core/full_node/ram_db.py +2 -2
  62. chia/_tests/core/full_node/stores/test_block_store.py +113 -11
  63. chia/_tests/core/full_node/stores/test_coin_store.py +37 -28
  64. chia/_tests/core/full_node/stores/test_full_node_store.py +34 -30
  65. chia/_tests/core/full_node/stores/test_hint_store.py +3 -4
  66. chia/_tests/core/full_node/test_address_manager.py +2 -2
  67. chia/_tests/core/full_node/test_block_height_map.py +1 -1
  68. chia/_tests/core/full_node/test_conditions.py +10 -12
  69. chia/_tests/core/full_node/test_full_node.py +2077 -1822
  70. chia/_tests/core/full_node/test_generator_tools.py +4 -4
  71. chia/_tests/core/full_node/test_hint_management.py +2 -2
  72. chia/_tests/core/full_node/test_performance.py +2 -5
  73. chia/_tests/core/full_node/test_subscriptions.py +4 -4
  74. chia/_tests/core/full_node/test_tx_processing_queue.py +5 -4
  75. chia/_tests/core/make_block_generator.py +5 -7
  76. chia/_tests/core/mempool/test_mempool.py +205 -208
  77. chia/_tests/core/mempool/test_mempool_fee_protocol.py +5 -5
  78. chia/_tests/core/mempool/test_mempool_item_queries.py +2 -4
  79. chia/_tests/core/mempool/test_mempool_manager.py +109 -80
  80. chia/_tests/core/mempool/test_mempool_performance.py +3 -4
  81. chia/_tests/core/mempool/test_singleton_fast_forward.py +12 -12
  82. chia/_tests/core/server/flood.py +6 -4
  83. chia/_tests/core/server/serve.py +10 -7
  84. chia/_tests/core/server/test_api_protocol.py +21 -0
  85. chia/_tests/core/server/test_capabilities.py +3 -5
  86. chia/_tests/core/server/test_dos.py +15 -16
  87. chia/_tests/core/server/test_loop.py +14 -10
  88. chia/_tests/core/server/test_node_discovery.py +1 -2
  89. chia/_tests/core/server/test_rate_limits.py +156 -44
  90. chia/_tests/core/server/test_server.py +8 -7
  91. chia/_tests/core/services/test_services.py +59 -37
  92. chia/_tests/core/ssl/test_ssl.py +5 -3
  93. chia/_tests/core/test_cost_calculation.py +5 -6
  94. chia/_tests/core/test_crawler.py +2 -2
  95. chia/_tests/core/test_db_conversion.py +5 -4
  96. chia/_tests/core/test_db_validation.py +6 -5
  97. chia/_tests/core/test_farmer_harvester_rpc.py +8 -7
  98. chia/_tests/core/test_filter.py +3 -5
  99. chia/_tests/core/test_full_node_rpc.py +64 -90
  100. chia/_tests/core/test_merkle_set.py +10 -10
  101. chia/_tests/core/test_program.py +2 -4
  102. chia/_tests/core/test_rpc_util.py +1 -2
  103. chia/_tests/core/test_seeder.py +124 -12
  104. chia/_tests/core/util/test_block_cache.py +5 -5
  105. chia/_tests/core/util/test_cached_bls.py +3 -3
  106. chia/_tests/core/util/test_config.py +13 -13
  107. chia/_tests/core/util/test_files.py +2 -2
  108. chia/_tests/core/util/test_jsonify.py +9 -9
  109. chia/_tests/core/util/test_keychain.py +13 -5
  110. chia/_tests/core/util/test_keyring_wrapper.py +6 -5
  111. chia/_tests/core/util/test_log_exceptions.py +3 -3
  112. chia/_tests/core/util/test_streamable.py +38 -38
  113. chia/_tests/db/test_db_wrapper.py +13 -12
  114. chia/_tests/environments/common.py +2 -2
  115. chia/_tests/environments/full_node.py +2 -2
  116. chia/_tests/environments/wallet.py +109 -48
  117. chia/_tests/farmer_harvester/test_farmer.py +35 -35
  118. chia/_tests/farmer_harvester/test_farmer_harvester.py +17 -17
  119. chia/_tests/farmer_harvester/test_filter_prefix_bits.py +6 -5
  120. chia/_tests/farmer_harvester/test_third_party_harvesters.py +73 -46
  121. chia/_tests/fee_estimation/test_fee_estimation_integration.py +8 -8
  122. chia/_tests/fee_estimation/test_fee_estimation_rpc.py +47 -47
  123. chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +6 -7
  124. chia/_tests/fee_estimation/test_mempoolitem_height_added.py +11 -11
  125. chia/_tests/generator/test_compression.py +13 -30
  126. chia/_tests/generator/test_generator_types.py +3 -3
  127. chia/_tests/generator/test_rom.py +7 -9
  128. chia/_tests/plot_sync/test_delta.py +2 -3
  129. chia/_tests/plot_sync/test_plot_sync.py +25 -24
  130. chia/_tests/plot_sync/test_receiver.py +9 -9
  131. chia/_tests/plot_sync/test_sender.py +1 -1
  132. chia/_tests/plot_sync/test_sync_simulated.py +27 -26
  133. chia/_tests/plot_sync/util.py +2 -1
  134. chia/_tests/plotting/test_plot_manager.py +54 -11
  135. chia/_tests/plotting/util.py +2 -3
  136. chia/_tests/pools/test_pool_cli_parsing.py +128 -0
  137. chia/_tests/pools/test_pool_cmdline.py +993 -15
  138. chia/_tests/pools/test_pool_config.py +3 -5
  139. chia/_tests/pools/test_pool_puzzles_lifecycle.py +10 -11
  140. chia/_tests/pools/test_pool_rpc.py +203 -90
  141. chia/_tests/pools/test_pool_wallet.py +12 -8
  142. chia/_tests/pools/test_wallet_pool_store.py +3 -3
  143. chia/_tests/process_junit.py +16 -17
  144. chia/_tests/rpc/test_rpc_client.py +59 -2
  145. chia/_tests/rpc/test_rpc_server.py +183 -0
  146. chia/_tests/simulation/test_simulation.py +5 -5
  147. chia/_tests/simulation/test_simulator.py +8 -10
  148. chia/_tests/simulation/test_start_simulator.py +5 -4
  149. chia/_tests/timelord/test_new_peak.py +19 -19
  150. chia/_tests/tools/test_run_block.py +1 -2
  151. chia/_tests/tools/test_virtual_project.py +591 -0
  152. chia/_tests/util/benchmark_cost.py +9 -9
  153. chia/_tests/util/benchmarks.py +1 -2
  154. chia/_tests/util/blockchain.py +12 -11
  155. chia/_tests/util/blockchain_mock.py +15 -15
  156. chia/_tests/util/build_network_protocol_files.py +12 -12
  157. chia/_tests/util/db_connection.py +3 -2
  158. chia/_tests/util/full_sync.py +14 -6
  159. chia/_tests/util/gen_ssl_certs.py +4 -5
  160. chia/_tests/util/generator_tools_testing.py +5 -7
  161. chia/_tests/util/get_name_puzzle_conditions.py +52 -0
  162. chia/_tests/util/key_tool.py +2 -3
  163. chia/_tests/util/misc.py +59 -106
  164. chia/_tests/util/network_protocol_data.py +7 -9
  165. chia/_tests/util/protocol_messages_json.py +112 -111
  166. chia/_tests/util/rpc.py +3 -0
  167. chia/_tests/util/run_block.py +16 -16
  168. chia/_tests/util/setup_nodes.py +25 -23
  169. chia/{clvm → _tests/util}/spend_sim.py +59 -55
  170. chia/_tests/util/split_managers.py +12 -9
  171. chia/_tests/util/temp_file.py +1 -1
  172. chia/_tests/util/test_action_scope.py +2 -1
  173. chia/_tests/util/test_async_pool.py +8 -8
  174. chia/_tests/util/test_build_job_matrix.py +2 -3
  175. chia/_tests/util/test_condition_tools.py +4 -6
  176. chia/_tests/util/test_config.py +5 -5
  177. chia/_tests/util/test_dump_keyring.py +1 -1
  178. chia/_tests/util/test_full_block_utils.py +19 -11
  179. chia/_tests/util/test_limited_semaphore.py +4 -3
  180. chia/_tests/util/test_logging_filter.py +2 -3
  181. chia/_tests/util/test_misc.py +29 -28
  182. chia/_tests/util/test_network.py +32 -31
  183. chia/_tests/util/test_network_protocol_files.py +2 -3
  184. chia/_tests/util/test_network_protocol_json.py +1 -0
  185. chia/_tests/util/test_network_protocol_test.py +18 -19
  186. chia/_tests/util/test_paginator.py +3 -4
  187. chia/_tests/util/test_pprint.py +1 -1
  188. chia/_tests/util/test_priority_mutex.py +18 -17
  189. chia/_tests/util/test_recursive_replace.py +2 -2
  190. chia/_tests/util/test_testnet_overrides.py +3 -3
  191. chia/_tests/util/test_timing.py +1 -1
  192. chia/_tests/util/test_trusted_peer.py +2 -2
  193. chia/_tests/util/time_out_assert.py +43 -6
  194. chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +13 -13
  195. chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +1 -1
  196. chia/_tests/wallet/cat_wallet/test_cat_wallet.py +117 -29
  197. chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +15 -15
  198. chia/_tests/wallet/cat_wallet/test_trades.py +50 -28
  199. chia/_tests/wallet/clawback/test_clawback_decorator.py +3 -5
  200. chia/_tests/wallet/clawback/test_clawback_lifecycle.py +6 -6
  201. chia/_tests/wallet/clawback/test_clawback_metadata.py +1 -2
  202. chia/_tests/wallet/conftest.py +135 -74
  203. chia/_tests/wallet/dao_wallet/test_dao_clvm.py +25 -17
  204. chia/_tests/wallet/dao_wallet/test_dao_wallets.py +75 -75
  205. chia/_tests/wallet/db_wallet/test_db_graftroot.py +10 -12
  206. chia/_tests/wallet/db_wallet/test_dl_offers.py +6 -6
  207. chia/_tests/wallet/db_wallet/test_dl_wallet.py +18 -18
  208. chia/_tests/wallet/did_wallet/test_did.py +1277 -474
  209. chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +12 -11
  210. chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +115 -105
  211. chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +6 -7
  212. chia/_tests/wallet/nft_wallet/test_nft_offers.py +16 -16
  213. chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +3 -3
  214. chia/_tests/wallet/nft_wallet/test_nft_wallet.py +38 -12
  215. chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +1 -1
  216. chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +31 -33
  217. chia/_tests/wallet/rpc/test_wallet_rpc.py +218 -171
  218. chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +36 -37
  219. chia/_tests/wallet/sync/test_wallet_sync.py +241 -78
  220. chia/_tests/wallet/test_address_type.py +20 -20
  221. chia/_tests/wallet/test_clvm_streamable.py +5 -5
  222. chia/_tests/wallet/test_coin_management.py +354 -0
  223. chia/_tests/wallet/test_coin_selection.py +34 -35
  224. chia/_tests/wallet/test_conditions.py +28 -16
  225. chia/_tests/wallet/test_debug_spend_bundle.py +156 -14
  226. chia/_tests/wallet/test_new_wallet_protocol.py +29 -31
  227. chia/_tests/wallet/test_nft_store.py +1 -2
  228. chia/_tests/wallet/test_notifications.py +2 -2
  229. chia/_tests/wallet/test_offer_parsing_performance.py +1 -1
  230. chia/_tests/wallet/test_puzzle_store.py +2 -3
  231. chia/_tests/wallet/test_sign_coin_spends.py +3 -3
  232. chia/_tests/wallet/test_signer_protocol.py +33 -34
  233. chia/_tests/wallet/test_singleton_lifecycle_fast.py +29 -29
  234. chia/_tests/wallet/test_taproot.py +1 -1
  235. chia/_tests/wallet/test_transaction_store.py +23 -19
  236. chia/_tests/wallet/test_util.py +36 -32
  237. chia/_tests/wallet/test_wallet.py +37 -37
  238. chia/_tests/wallet/test_wallet_action_scope.py +8 -8
  239. chia/_tests/wallet/test_wallet_blockchain.py +4 -6
  240. chia/_tests/wallet/test_wallet_coin_store.py +34 -34
  241. chia/_tests/wallet/test_wallet_node.py +69 -72
  242. chia/_tests/wallet/test_wallet_retry.py +3 -3
  243. chia/_tests/wallet/test_wallet_state_manager.py +12 -5
  244. chia/_tests/wallet/test_wallet_trade_store.py +2 -2
  245. chia/_tests/wallet/test_wallet_utils.py +5 -4
  246. chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +3 -3
  247. chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +18 -18
  248. chia/_tests/wallet/vc_wallet/test_vc_wallet.py +69 -40
  249. chia/_tests/wallet/wallet_block_tools.py +27 -27
  250. chia/_tests/weight_proof/test_weight_proof.py +30 -30
  251. chia/apis.py +19 -0
  252. chia/cmds/beta.py +8 -7
  253. chia/cmds/beta_funcs.py +15 -11
  254. chia/cmds/check_wallet_db.py +29 -27
  255. chia/cmds/chia.py +17 -9
  256. chia/cmds/cmd_classes.py +87 -79
  257. chia/cmds/cmd_helpers.py +242 -0
  258. chia/cmds/cmds_util.py +56 -66
  259. chia/cmds/coin_funcs.py +168 -153
  260. chia/cmds/coins.py +156 -194
  261. chia/cmds/configure.py +4 -3
  262. chia/cmds/dao.py +89 -33
  263. chia/cmds/dao_funcs.py +55 -33
  264. chia/cmds/data.py +7 -6
  265. chia/cmds/data_funcs.py +26 -21
  266. chia/cmds/db.py +4 -3
  267. chia/cmds/db_backup_func.py +2 -2
  268. chia/cmds/db_upgrade_func.py +3 -3
  269. chia/cmds/db_validate_func.py +2 -2
  270. chia/cmds/dev.py +2 -0
  271. chia/cmds/farm.py +18 -5
  272. chia/cmds/farm_funcs.py +17 -24
  273. chia/cmds/gh.py +275 -0
  274. chia/cmds/init.py +4 -11
  275. chia/cmds/init_funcs.py +9 -9
  276. chia/cmds/installers.py +5 -3
  277. chia/cmds/keys.py +56 -39
  278. chia/cmds/keys_funcs.py +30 -31
  279. chia/cmds/netspace.py +6 -3
  280. chia/cmds/netspace_funcs.py +3 -2
  281. chia/cmds/param_types.py +16 -6
  282. chia/cmds/passphrase.py +8 -7
  283. chia/cmds/passphrase_funcs.py +7 -61
  284. chia/cmds/peer.py +2 -1
  285. chia/cmds/peer_funcs.py +5 -5
  286. chia/cmds/plotnft.py +207 -153
  287. chia/cmds/plotnft_funcs.py +205 -174
  288. chia/cmds/plots.py +14 -6
  289. chia/cmds/plotters.py +2 -1
  290. chia/cmds/rpc.py +48 -28
  291. chia/cmds/show.py +2 -1
  292. chia/cmds/show_funcs.py +7 -6
  293. chia/cmds/signer.py +50 -58
  294. chia/cmds/sim.py +22 -14
  295. chia/cmds/sim_funcs.py +11 -11
  296. chia/cmds/start.py +3 -3
  297. chia/cmds/start_funcs.py +9 -12
  298. chia/cmds/stop.py +4 -3
  299. chia/cmds/units.py +1 -3
  300. chia/cmds/wallet.py +252 -96
  301. chia/cmds/wallet_funcs.py +217 -143
  302. chia/consensus/block_body_validation.py +133 -86
  303. chia/consensus/block_creation.py +42 -21
  304. chia/consensus/block_header_validation.py +32 -37
  305. chia/consensus/block_record.py +1 -2
  306. chia/consensus/blockchain.py +167 -180
  307. chia/consensus/blockchain_interface.py +10 -10
  308. chia/consensus/constants.py +2 -2
  309. chia/consensus/default_constants.py +3 -4
  310. chia/consensus/difficulty_adjustment.py +5 -5
  311. chia/consensus/find_fork_point.py +5 -5
  312. chia/consensus/full_block_to_block_record.py +4 -4
  313. chia/consensus/get_block_challenge.py +2 -2
  314. chia/consensus/get_block_generator.py +4 -3
  315. chia/consensus/multiprocess_validation.py +207 -304
  316. chia/consensus/vdf_info_computation.py +3 -3
  317. chia/daemon/client.py +46 -27
  318. chia/daemon/keychain_proxy.py +10 -9
  319. chia/daemon/keychain_server.py +18 -18
  320. chia/daemon/server.py +103 -113
  321. chia/daemon/windows_signal.py +2 -2
  322. chia/data_layer/data_layer.py +64 -76
  323. chia/data_layer/data_layer_api.py +8 -0
  324. chia/data_layer/data_layer_errors.py +3 -3
  325. chia/data_layer/data_layer_server.py +2 -2
  326. chia/data_layer/data_layer_util.py +71 -71
  327. chia/data_layer/data_layer_wallet.py +63 -67
  328. chia/data_layer/data_store.py +72 -72
  329. chia/data_layer/dl_wallet_store.py +10 -10
  330. chia/data_layer/download_data.py +5 -5
  331. chia/data_layer/s3_plugin_service.py +9 -9
  332. chia/data_layer/util/benchmark.py +0 -1
  333. chia/data_layer/util/plugin.py +2 -3
  334. chia/farmer/farmer.py +46 -43
  335. chia/farmer/farmer_api.py +27 -21
  336. chia/full_node/block_height_map.py +6 -6
  337. chia/full_node/block_store.py +41 -35
  338. chia/full_node/coin_store.py +42 -41
  339. chia/full_node/fee_estimate.py +2 -2
  340. chia/full_node/fee_estimation.py +1 -2
  341. chia/full_node/fee_history.py +5 -6
  342. chia/full_node/fee_tracker.py +24 -24
  343. chia/full_node/full_node.py +574 -300
  344. chia/full_node/full_node_api.py +181 -130
  345. chia/full_node/full_node_store.py +43 -43
  346. chia/full_node/hint_management.py +4 -4
  347. chia/full_node/hint_store.py +9 -10
  348. chia/full_node/mempool.py +25 -19
  349. chia/full_node/mempool_check_conditions.py +11 -42
  350. chia/full_node/mempool_manager.py +48 -53
  351. chia/full_node/pending_tx_cache.py +9 -9
  352. chia/full_node/subscriptions.py +23 -24
  353. chia/full_node/sync_store.py +8 -7
  354. chia/full_node/tx_processing_queue.py +3 -3
  355. chia/full_node/util/__init__.py +0 -0
  356. chia/full_node/weight_proof.py +79 -78
  357. chia/harvester/harvester.py +9 -8
  358. chia/harvester/harvester_api.py +19 -13
  359. chia/introducer/introducer.py +7 -5
  360. chia/introducer/introducer_api.py +9 -3
  361. chia/legacy/keyring.py +6 -5
  362. chia/plot_sync/delta.py +8 -8
  363. chia/plot_sync/receiver.py +12 -11
  364. chia/plot_sync/sender.py +15 -12
  365. chia/plotters/bladebit.py +12 -12
  366. chia/plotters/chiapos.py +2 -2
  367. chia/plotters/madmax.py +8 -8
  368. chia/plotters/plotters.py +6 -6
  369. chia/plotters/plotters_util.py +6 -4
  370. chia/plotting/cache.py +8 -7
  371. chia/plotting/check_plots.py +8 -8
  372. chia/plotting/create_plots.py +6 -6
  373. chia/plotting/manager.py +22 -22
  374. chia/plotting/util.py +31 -19
  375. chia/pools/pool_config.py +7 -7
  376. chia/pools/pool_puzzles.py +16 -16
  377. chia/pools/pool_wallet.py +64 -57
  378. chia/pools/pool_wallet_info.py +3 -3
  379. chia/protocols/full_node_protocol.py +3 -3
  380. chia/protocols/harvester_protocol.py +12 -12
  381. chia/protocols/introducer_protocol.py +1 -2
  382. chia/protocols/protocol_message_types.py +4 -4
  383. chia/protocols/protocol_state_machine.py +2 -2
  384. chia/protocols/protocol_timing.py +1 -0
  385. chia/protocols/shared_protocol.py +3 -3
  386. chia/protocols/timelord_protocol.py +2 -2
  387. chia/protocols/wallet_protocol.py +33 -33
  388. chia/rpc/crawler_rpc_api.py +12 -7
  389. chia/rpc/data_layer_rpc_api.py +49 -44
  390. chia/rpc/data_layer_rpc_client.py +41 -41
  391. chia/rpc/data_layer_rpc_util.py +7 -11
  392. chia/rpc/farmer_rpc_api.py +32 -27
  393. chia/rpc/farmer_rpc_client.py +14 -14
  394. chia/rpc/full_node_rpc_api.py +53 -48
  395. chia/rpc/full_node_rpc_client.py +30 -30
  396. chia/rpc/harvester_rpc_api.py +16 -11
  397. chia/rpc/harvester_rpc_client.py +6 -6
  398. chia/rpc/rpc_client.py +34 -14
  399. chia/rpc/rpc_server.py +117 -43
  400. chia/rpc/timelord_rpc_api.py +9 -4
  401. chia/rpc/util.py +11 -211
  402. chia/rpc/wallet_request_types.py +276 -60
  403. chia/rpc/wallet_rpc_api.py +563 -399
  404. chia/rpc/wallet_rpc_client.py +220 -250
  405. chia/seeder/crawl_store.py +6 -8
  406. chia/seeder/crawler.py +23 -36
  407. chia/seeder/crawler_api.py +28 -22
  408. chia/seeder/dns_server.py +99 -50
  409. chia/seeder/start_crawler.py +13 -9
  410. chia/server/address_manager.py +19 -19
  411. chia/server/address_manager_store.py +17 -17
  412. chia/server/api_protocol.py +106 -1
  413. chia/server/capabilities.py +3 -3
  414. chia/server/chia_policy.py +17 -16
  415. chia/server/introducer_peers.py +3 -3
  416. chia/server/node_discovery.py +34 -38
  417. chia/server/rate_limit_numbers.py +26 -16
  418. chia/server/rate_limits.py +67 -27
  419. chia/server/server.py +52 -31
  420. chia/server/signal_handlers.py +6 -3
  421. chia/server/ssl_context.py +5 -5
  422. chia/server/start_data_layer.py +37 -23
  423. chia/server/start_farmer.py +28 -16
  424. chia/server/start_full_node.py +29 -23
  425. chia/server/start_harvester.py +28 -15
  426. chia/server/start_introducer.py +27 -15
  427. chia/server/start_service.py +17 -29
  428. chia/server/start_timelord.py +25 -18
  429. chia/server/start_wallet.py +22 -18
  430. chia/server/upnp.py +4 -3
  431. chia/server/ws_connection.py +68 -54
  432. chia/simulator/add_blocks_in_batches.py +54 -0
  433. chia/simulator/block_tools.py +65 -64
  434. chia/simulator/full_node_simulator.py +66 -74
  435. chia/simulator/setup_services.py +10 -9
  436. chia/simulator/simulator_full_node_rpc_api.py +12 -14
  437. chia/simulator/simulator_full_node_rpc_client.py +3 -5
  438. chia/simulator/simulator_test_tools.py +8 -7
  439. chia/simulator/socket.py +1 -4
  440. chia/simulator/ssl_certs.py +5 -5
  441. chia/simulator/ssl_certs_1.py +2 -4
  442. chia/simulator/ssl_certs_10.py +2 -4
  443. chia/simulator/ssl_certs_2.py +2 -4
  444. chia/simulator/ssl_certs_3.py +2 -4
  445. chia/simulator/ssl_certs_4.py +2 -4
  446. chia/simulator/ssl_certs_5.py +2 -4
  447. chia/simulator/ssl_certs_6.py +2 -4
  448. chia/simulator/ssl_certs_7.py +2 -4
  449. chia/simulator/ssl_certs_8.py +2 -4
  450. chia/simulator/ssl_certs_9.py +2 -4
  451. chia/simulator/start_simulator.py +14 -6
  452. chia/simulator/wallet_tools.py +21 -20
  453. chia/ssl/create_ssl.py +11 -11
  454. chia/timelord/iters_from_block.py +2 -2
  455. chia/timelord/timelord.py +57 -33
  456. chia/timelord/timelord_api.py +12 -6
  457. chia/timelord/timelord_launcher.py +10 -8
  458. chia/timelord/timelord_state.py +5 -5
  459. chia/types/block_protocol.py +2 -2
  460. chia/types/blockchain_format/coin.py +3 -3
  461. chia/types/blockchain_format/program.py +17 -18
  462. chia/types/blockchain_format/tree_hash.py +9 -9
  463. chia/types/coin_spend.py +8 -8
  464. chia/types/condition_with_args.py +1 -2
  465. chia/types/eligible_coin_spends.py +16 -15
  466. chia/types/generator_types.py +1 -2
  467. chia/types/internal_mempool_item.py +1 -2
  468. chia/types/mempool_item.py +7 -7
  469. chia/types/mempool_submission_status.py +2 -2
  470. chia/types/peer_info.py +1 -1
  471. chia/types/spend_bundle.py +1 -2
  472. chia/types/transaction_queue_entry.py +2 -2
  473. chia/types/unfinished_header_block.py +2 -2
  474. chia/types/validation_state.py +14 -0
  475. chia/types/weight_proof.py +5 -6
  476. chia/util/action_scope.py +8 -8
  477. chia/util/async_pool.py +6 -4
  478. chia/util/augmented_chain.py +13 -9
  479. chia/util/batches.py +5 -2
  480. chia/util/bech32m.py +14 -11
  481. chia/util/beta_metrics.py +5 -4
  482. chia/util/block_cache.py +5 -5
  483. chia/util/byte_types.py +2 -0
  484. chia/util/check_fork_next_block.py +3 -2
  485. chia/util/chia_logging.py +41 -21
  486. chia/util/collection.py +3 -3
  487. chia/util/condition_tools.py +18 -18
  488. chia/util/config.py +26 -25
  489. chia/util/cpu.py +2 -0
  490. chia/util/db_synchronous.py +2 -0
  491. chia/util/db_version.py +2 -0
  492. chia/util/db_wrapper.py +13 -10
  493. chia/util/default_root.py +17 -0
  494. chia/util/dump_keyring.py +6 -6
  495. chia/util/errors.py +5 -3
  496. chia/util/file_keyring.py +22 -33
  497. chia/util/files.py +2 -0
  498. chia/util/full_block_utils.py +31 -7
  499. chia/util/generator_tools.py +18 -8
  500. chia/util/hash.py +3 -1
  501. chia/util/initial-config.yaml +19 -0
  502. chia/util/inline_executor.py +2 -0
  503. chia/util/ip_address.py +39 -0
  504. chia/util/json_util.py +0 -4
  505. chia/util/keychain.py +27 -24
  506. chia/util/keyring_wrapper.py +65 -4
  507. chia/util/limited_semaphore.py +3 -1
  508. chia/util/lock.py +4 -2
  509. chia/util/log_exceptions.py +5 -2
  510. chia/util/logging.py +3 -1
  511. chia/util/lru_cache.py +2 -0
  512. chia/util/math.py +4 -4
  513. chia/util/network.py +15 -73
  514. chia/util/paginator.py +3 -1
  515. chia/util/path.py +2 -0
  516. chia/util/permissions.py +3 -2
  517. chia/util/prev_transaction_block.py +1 -3
  518. chia/util/priority_mutex.py +6 -3
  519. chia/util/profiler.py +7 -4
  520. chia/util/recursive_replace.py +2 -0
  521. chia/util/safe_cancel_task.py +2 -0
  522. chia/util/service_groups.py +2 -2
  523. chia/util/setproctitle.py +2 -0
  524. chia/util/significant_bits.py +2 -0
  525. chia/util/ssl_check.py +11 -11
  526. chia/util/streamable.py +44 -56
  527. chia/util/task_referencer.py +59 -0
  528. chia/util/task_timing.py +22 -18
  529. chia/util/timing.py +4 -1
  530. chia/util/vdf_prover.py +2 -3
  531. chia/util/virtual_project_analysis.py +540 -0
  532. chia/util/ws_message.py +6 -6
  533. chia/wallet/cat_wallet/cat_info.py +3 -3
  534. chia/wallet/cat_wallet/cat_outer_puzzle.py +3 -3
  535. chia/wallet/cat_wallet/cat_utils.py +5 -4
  536. chia/wallet/cat_wallet/cat_wallet.py +56 -70
  537. chia/wallet/cat_wallet/dao_cat_info.py +3 -3
  538. chia/wallet/cat_wallet/dao_cat_wallet.py +18 -18
  539. chia/wallet/cat_wallet/lineage_store.py +2 -2
  540. chia/wallet/coin_selection.py +15 -15
  541. chia/wallet/conditions.py +257 -71
  542. chia/wallet/dao_wallet/dao_info.py +4 -4
  543. chia/wallet/dao_wallet/dao_utils.py +43 -42
  544. chia/wallet/dao_wallet/dao_wallet.py +66 -68
  545. chia/wallet/db_wallet/db_wallet_puzzles.py +12 -8
  546. chia/wallet/derive_keys.py +11 -11
  547. chia/wallet/did_wallet/did_info.py +3 -3
  548. chia/wallet/did_wallet/did_wallet.py +56 -47
  549. chia/wallet/did_wallet/did_wallet_puzzles.py +7 -6
  550. chia/wallet/lineage_proof.py +4 -4
  551. chia/wallet/nft_wallet/metadata_outer_puzzle.py +2 -2
  552. chia/wallet/nft_wallet/nft_info.py +4 -4
  553. chia/wallet/nft_wallet/nft_puzzles.py +16 -16
  554. chia/wallet/nft_wallet/nft_wallet.py +90 -89
  555. chia/wallet/nft_wallet/ownership_outer_puzzle.py +2 -2
  556. chia/wallet/nft_wallet/singleton_outer_puzzle.py +2 -2
  557. chia/wallet/nft_wallet/transfer_program_puzzle.py +2 -2
  558. chia/wallet/nft_wallet/uncurry_nft.py +2 -2
  559. chia/wallet/notification_manager.py +5 -5
  560. chia/wallet/notification_store.py +6 -6
  561. chia/wallet/outer_puzzles.py +2 -2
  562. chia/wallet/payment.py +4 -5
  563. chia/wallet/puzzle_drivers.py +4 -4
  564. chia/wallet/puzzles/clawback/drivers.py +5 -5
  565. chia/wallet/puzzles/clawback/puzzle_decorator.py +7 -7
  566. chia/wallet/puzzles/load_clvm.py +2 -3
  567. chia/wallet/puzzles/p2_conditions.py +1 -2
  568. chia/wallet/puzzles/p2_delegated_conditions.py +1 -2
  569. chia/wallet/puzzles/p2_delegated_puzzle.py +2 -3
  570. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +3 -4
  571. chia/wallet/puzzles/p2_m_of_n_delegate_direct.py +1 -2
  572. chia/wallet/puzzles/p2_puzzle_hash.py +1 -2
  573. chia/wallet/puzzles/puzzle_utils.py +7 -7
  574. chia/wallet/puzzles/singleton_top_layer.py +6 -5
  575. chia/wallet/puzzles/singleton_top_layer_v1_1.py +6 -5
  576. chia/wallet/puzzles/tails.py +34 -30
  577. chia/wallet/signer_protocol.py +7 -8
  578. chia/wallet/singleton.py +4 -4
  579. chia/wallet/trade_manager.py +155 -141
  580. chia/wallet/trade_record.py +5 -5
  581. chia/wallet/trading/offer.py +100 -101
  582. chia/wallet/trading/trade_store.py +14 -14
  583. chia/wallet/transaction_record.py +31 -16
  584. chia/wallet/util/address_type.py +4 -4
  585. chia/wallet/util/blind_signer_tl.py +8 -12
  586. chia/wallet/util/clvm_streamable.py +15 -15
  587. chia/wallet/util/compute_hints.py +5 -5
  588. chia/wallet/util/compute_memos.py +4 -6
  589. chia/wallet/util/curry_and_treehash.py +3 -2
  590. chia/wallet/util/debug_spend_bundle.py +6 -8
  591. chia/wallet/util/merkle_tree.py +10 -10
  592. chia/wallet/util/merkle_utils.py +10 -10
  593. chia/wallet/util/new_peak_queue.py +3 -3
  594. chia/wallet/util/peer_request_cache.py +8 -8
  595. chia/{util → wallet/util}/pprint.py +2 -3
  596. chia/wallet/util/puzzle_compression.py +3 -4
  597. chia/wallet/util/puzzle_decorator.py +10 -10
  598. chia/wallet/util/query_filter.py +9 -10
  599. chia/wallet/util/tx_config.py +12 -12
  600. chia/wallet/util/wallet_sync_utils.py +24 -21
  601. chia/wallet/util/wallet_types.py +9 -2
  602. chia/wallet/vc_wallet/cr_cat_drivers.py +28 -27
  603. chia/wallet/vc_wallet/cr_cat_wallet.py +42 -40
  604. chia/wallet/vc_wallet/cr_outer_puzzle.py +4 -4
  605. chia/wallet/vc_wallet/vc_drivers.py +16 -16
  606. chia/wallet/vc_wallet/vc_store.py +9 -9
  607. chia/wallet/vc_wallet/vc_wallet.py +35 -35
  608. chia/wallet/wallet.py +54 -54
  609. chia/wallet/wallet_action_scope.py +14 -13
  610. chia/wallet/wallet_blockchain.py +10 -10
  611. chia/wallet/wallet_coin_record.py +2 -2
  612. chia/wallet/wallet_coin_store.py +10 -10
  613. chia/wallet/wallet_info.py +1 -2
  614. chia/wallet/wallet_interested_store.py +5 -5
  615. chia/wallet/wallet_nft_store.py +6 -6
  616. chia/wallet/wallet_node.py +72 -76
  617. chia/wallet/wallet_node_api.py +33 -27
  618. chia/wallet/wallet_pool_store.py +1 -2
  619. chia/wallet/wallet_protocol.py +15 -15
  620. chia/wallet/wallet_puzzle_store.py +35 -4
  621. chia/wallet/wallet_retry_store.py +2 -2
  622. chia/wallet/wallet_singleton_store.py +10 -9
  623. chia/wallet/wallet_spend_bundle.py +4 -20
  624. chia/wallet/wallet_state_manager.py +223 -224
  625. chia/wallet/wallet_transaction_store.py +44 -18
  626. chia/wallet/wallet_user_store.py +2 -2
  627. chia/wallet/wallet_weight_proof_handler.py +2 -2
  628. {chia_blockchain-2.5.0rc2.dist-info → chia_blockchain-2.5.1rc2.dist-info}/LICENSE +1 -1
  629. {chia_blockchain-2.5.0rc2.dist-info → chia_blockchain-2.5.1rc2.dist-info}/METADATA +67 -72
  630. chia_blockchain-2.5.1rc2.dist-info/RECORD +1042 -0
  631. {chia_blockchain-2.5.0rc2.dist-info → chia_blockchain-2.5.1rc2.dist-info}/WHEEL +1 -1
  632. mozilla-ca/cacert.pem +32 -87
  633. chia/_tests/cmds/wallet/test_coins.py +0 -195
  634. chia/consensus/block_root_validation.py +0 -46
  635. chia/util/api_decorators.py +0 -89
  636. chia_blockchain-2.5.0rc2.dist-info/RECORD +0 -1028
  637. {chia_blockchain-2.5.0rc2.dist-info → chia_blockchain-2.5.1rc2.dist-info}/entry_points.txt +0 -0
@@ -5,9 +5,9 @@ import json
5
5
  import logging
6
6
  import zlib
7
7
  from pathlib import Path
8
- from typing import Any, ClassVar, Dict, List, Optional, Set, Tuple, Union
8
+ from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, Union, cast
9
9
 
10
- from chia_rs import AugSchemeMPL, G1Element, G2Element, PrivateKey
10
+ from chia_rs import AugSchemeMPL, Coin, G1Element, G2Element, PrivateKey
11
11
  from clvm_tools.binutils import assemble
12
12
 
13
13
  from chia.consensus.block_rewards import calculate_base_farmer_reward
@@ -18,7 +18,7 @@ from chia.pools.pool_wallet import PoolWallet
18
18
  from chia.pools.pool_wallet_info import FARMING_TO_POOL, PoolState, PoolWalletInfo, create_pool_state
19
19
  from chia.protocols.wallet_protocol import CoinState
20
20
  from chia.rpc.rpc_server import Endpoint, EndpointResult, default_get_connections
21
- from chia.rpc.util import marshal, tx_endpoint
21
+ from chia.rpc.util import ALL_TRANSLATION_LAYERS, RpcEndpoint, marshal
22
22
  from chia.rpc.wallet_request_types import (
23
23
  AddKey,
24
24
  AddKeyResponse,
@@ -35,6 +35,7 @@ from chia.rpc.wallet_request_types import (
35
35
  GatherSigningInfo,
36
36
  GatherSigningInfoResponse,
37
37
  GenerateMnemonicResponse,
38
+ GetHeightInfoResponse,
38
39
  GetLoggedInFingerprintResponse,
39
40
  GetNotifications,
40
41
  GetNotificationsResponse,
@@ -42,16 +43,39 @@ from chia.rpc.wallet_request_types import (
42
43
  GetPrivateKeyFormat,
43
44
  GetPrivateKeyResponse,
44
45
  GetPublicKeysResponse,
46
+ GetSyncStatusResponse,
47
+ GetTimestampForHeight,
48
+ GetTimestampForHeightResponse,
45
49
  LogIn,
46
50
  LogInResponse,
51
+ PushTransactions,
52
+ PushTransactionsResponse,
53
+ PushTX,
54
+ SetWalletResyncOnStartup,
47
55
  SplitCoins,
48
56
  SplitCoinsResponse,
49
57
  SubmitTransactions,
50
58
  SubmitTransactionsResponse,
59
+ VCAddProofs,
60
+ VCGet,
61
+ VCGetList,
62
+ VCGetListResponse,
63
+ VCGetProofsForRoot,
64
+ VCGetProofsForRootResponse,
65
+ VCGetResponse,
66
+ VCMint,
67
+ VCMintResponse,
68
+ VCProofsRPC,
69
+ VCProofWithHash,
70
+ VCRecordWithCoinID,
71
+ VCRevoke,
72
+ VCRevokeResponse,
73
+ VCSpend,
74
+ VCSpendResponse,
51
75
  )
52
76
  from chia.server.outbound_message import NodeType
53
77
  from chia.server.ws_connection import WSChiaConnection
54
- from chia.types.blockchain_format.coin import Coin, coin_as_list
78
+ from chia.types.blockchain_format.coin import coin_as_list
55
79
  from chia.types.blockchain_format.program import INFINITE_COST, Program
56
80
  from chia.types.blockchain_format.sized_bytes import bytes32
57
81
  from chia.types.coin_record import CoinRecord
@@ -76,9 +100,12 @@ from chia.wallet.conditions import (
76
100
  AssertCoinAnnouncement,
77
101
  AssertPuzzleAnnouncement,
78
102
  Condition,
103
+ ConditionValidTimes,
79
104
  CreateCoinAnnouncement,
80
105
  CreatePuzzleAnnouncement,
106
+ conditions_from_json_dicts,
81
107
  parse_conditions_non_consensus,
108
+ parse_timelock_info,
82
109
  )
83
110
  from chia.wallet.dao_wallet.dao_info import DAORules
84
111
  from chia.wallet.dao_wallet.dao_utils import (
@@ -127,6 +154,7 @@ from chia.wallet.trading.offer import Offer
127
154
  from chia.wallet.transaction_record import TransactionRecord
128
155
  from chia.wallet.uncurried_puzzle import uncurry_puzzle
129
156
  from chia.wallet.util.address_type import AddressType, is_valid_address
157
+ from chia.wallet.util.clvm_streamable import json_serialize_with_clvm_streamable
130
158
  from chia.wallet.util.compute_hints import compute_spend_hints_and_additions
131
159
  from chia.wallet.util.compute_memos import compute_memos
132
160
  from chia.wallet.util.curry_and_treehash import NIL_TREEHASH
@@ -156,7 +184,200 @@ MAX_NFT_CHUNK_SIZE = 25
156
184
  log = logging.getLogger(__name__)
157
185
 
158
186
 
187
+ def tx_endpoint(
188
+ push: bool = False,
189
+ merge_spends: bool = True,
190
+ ) -> Callable[[RpcEndpoint], RpcEndpoint]:
191
+ def _inner(func: RpcEndpoint) -> RpcEndpoint:
192
+ async def rpc_endpoint(
193
+ self: WalletRpcApi, request: dict[str, Any], *args: object, **kwargs: object
194
+ ) -> EndpointResult:
195
+ assert self.service.logged_in_fingerprint is not None
196
+ tx_config_loader: TXConfigLoader = TXConfigLoader.from_json_dict(request)
197
+
198
+ # Some backwards compat fill-ins
199
+ if tx_config_loader.excluded_coin_ids is None:
200
+ tx_config_loader = tx_config_loader.override(
201
+ excluded_coin_ids=request.get("exclude_coin_ids"),
202
+ )
203
+ if tx_config_loader.excluded_coin_amounts is None:
204
+ tx_config_loader = tx_config_loader.override(
205
+ excluded_coin_amounts=request.get("exclude_coin_amounts"),
206
+ )
207
+ if tx_config_loader.excluded_coin_ids is None:
208
+ excluded_coins: Optional[list[dict[str, Any]]] = request.get(
209
+ "exclude_coins", request.get("excluded_coins")
210
+ )
211
+ if excluded_coins is not None:
212
+ tx_config_loader = tx_config_loader.override(
213
+ excluded_coin_ids=[Coin.from_json_dict(c).name() for c in excluded_coins],
214
+ )
215
+
216
+ tx_config: TXConfig = tx_config_loader.autofill(
217
+ constants=self.service.wallet_state_manager.constants,
218
+ config=self.service.wallet_state_manager.config,
219
+ logged_in_fingerprint=self.service.logged_in_fingerprint,
220
+ )
221
+
222
+ extra_conditions: tuple[Condition, ...] = tuple()
223
+ if "extra_conditions" in request:
224
+ extra_conditions = tuple(conditions_from_json_dicts(request["extra_conditions"]))
225
+ extra_conditions = (*extra_conditions, *ConditionValidTimes.from_json_dict(request).to_conditions())
226
+
227
+ valid_times: ConditionValidTimes = parse_timelock_info(extra_conditions)
228
+ if (
229
+ valid_times.max_secs_after_created is not None
230
+ or valid_times.min_secs_since_created is not None
231
+ or valid_times.max_blocks_after_created is not None
232
+ or valid_times.min_blocks_since_created is not None
233
+ ):
234
+ raise ValueError("Relative timelocks are not currently supported in the RPC")
235
+
236
+ async with self.service.wallet_state_manager.new_action_scope(
237
+ tx_config,
238
+ push=request.get("push", push),
239
+ merge_spends=request.get("merge_spends", merge_spends),
240
+ sign=request.get("sign", self.service.config.get("auto_sign_txs", True)),
241
+ ) as action_scope:
242
+ response: EndpointResult = await func(
243
+ self,
244
+ request,
245
+ *args,
246
+ action_scope,
247
+ extra_conditions=extra_conditions,
248
+ **kwargs,
249
+ )
250
+
251
+ if func.__name__ == "create_new_wallet" and "transactions" not in response:
252
+ # unfortunately, this API isn't solely a tx endpoint
253
+ return response
254
+
255
+ unsigned_txs = await self.service.wallet_state_manager.gather_signing_info_for_txs(
256
+ action_scope.side_effects.transactions
257
+ )
258
+
259
+ if request.get("CHIP-0029", False):
260
+ response["unsigned_transactions"] = [
261
+ json_serialize_with_clvm_streamable(
262
+ tx,
263
+ translation_layer=(
264
+ ALL_TRANSLATION_LAYERS[request["translation"]] if "translation" in request else None
265
+ ),
266
+ )
267
+ for tx in unsigned_txs
268
+ ]
269
+ else:
270
+ response["unsigned_transactions"] = [tx.to_json_dict() for tx in unsigned_txs]
271
+
272
+ response["transactions"] = [
273
+ TransactionRecord.to_json_dict_convenience(tx, self.service.config)
274
+ for tx in action_scope.side_effects.transactions
275
+ ]
276
+
277
+ # Some backwards compatibility code here because transaction information being returned was not uniform
278
+ # until the "transactions" key was applied to all of them. Unfortunately, since .add_pending_transactions
279
+ # now applies transformations to the transactions, we have to special case edit all of the previous
280
+ # spots where the information was being surfaced outside of the knowledge of this wrapper.
281
+ new_txs = action_scope.side_effects.transactions
282
+ if "transaction" in response:
283
+ if (
284
+ func.__name__ == "create_new_wallet" and request["wallet_type"] == "pool_wallet"
285
+ ) or func.__name__ in {"pw_join_pool", "pw_self_pool", "pw_absorb_rewards"}:
286
+ # Theses RPCs return not "convenience" for some reason
287
+ response["transaction"] = new_txs[-1].to_json_dict()
288
+ else:
289
+ response["transaction"] = response["transactions"][0]
290
+ if "tx_record" in response:
291
+ response["tx_record"] = response["transactions"][0]
292
+ if "fee_transaction" in response:
293
+ # Theses RPCs return not "convenience" for some reason
294
+ fee_transactions = [tx for tx in new_txs if tx.wallet_id == 1]
295
+ if len(fee_transactions) == 0:
296
+ response["fee_transaction"] = None
297
+ else:
298
+ response["fee_transaction"] = fee_transactions[0].to_json_dict()
299
+ if "transaction_id" in response:
300
+ response["transaction_id"] = new_txs[0].name
301
+ if "transaction_ids" in response:
302
+ response["transaction_ids"] = [
303
+ tx.name.hex() for tx in new_txs if tx.type == TransactionType.OUTGOING_CLAWBACK.value
304
+ ]
305
+ if "spend_bundle" in response:
306
+ response["spend_bundle"] = WalletSpendBundle.aggregate(
307
+ [tx.spend_bundle for tx in new_txs if tx.spend_bundle is not None]
308
+ )
309
+ if "signed_txs" in response:
310
+ response["signed_txs"] = response["transactions"]
311
+ if "signed_tx" in response:
312
+ response["signed_tx"] = response["transactions"][0]
313
+ if "tx" in response:
314
+ if func.__name__ == "send_notification":
315
+ response["tx"] = response["transactions"][0]
316
+ else:
317
+ response["tx"] = new_txs[0].to_json_dict()
318
+ if "txs" in response:
319
+ response["txs"] = [tx.to_json_dict() for tx in new_txs]
320
+ if "tx_id" in response:
321
+ response["tx_id"] = new_txs[0].name
322
+ if "trade_record" in response:
323
+ old_offer: Offer = Offer.from_bech32(response["offer"])
324
+ signed_coin_spends: list[CoinSpend] = [
325
+ coin_spend
326
+ for tx in new_txs
327
+ if tx.spend_bundle is not None
328
+ for coin_spend in tx.spend_bundle.coin_spends
329
+ ]
330
+ involved_coins: list[Coin] = [spend.coin for spend in signed_coin_spends]
331
+ signed_coin_spends.extend(
332
+ [spend for spend in old_offer._bundle.coin_spends if spend.coin not in involved_coins]
333
+ )
334
+ new_offer_bundle = WalletSpendBundle(
335
+ signed_coin_spends,
336
+ AugSchemeMPL.aggregate(
337
+ [tx.spend_bundle.aggregated_signature for tx in new_txs if tx.spend_bundle is not None]
338
+ ),
339
+ )
340
+ new_offer: Offer = Offer(old_offer.requested_payments, new_offer_bundle, old_offer.driver_dict)
341
+ response["offer"] = new_offer.to_bech32()
342
+ old_trade_record: TradeRecord = TradeRecord.from_json_dict_convenience(
343
+ response["trade_record"], bytes(old_offer).hex()
344
+ )
345
+ new_trade: TradeRecord = dataclasses.replace(
346
+ old_trade_record,
347
+ offer=bytes(new_offer),
348
+ trade_id=new_offer.name(),
349
+ )
350
+ response["trade_record"] = new_trade.to_json_dict_convenience()
351
+ if (
352
+ await self.service.wallet_state_manager.trade_manager.trade_store.get_trade_record(
353
+ old_trade_record.trade_id
354
+ )
355
+ is not None
356
+ ):
357
+ await self.service.wallet_state_manager.trade_manager.trade_store.delete_trade_record(
358
+ old_trade_record.trade_id
359
+ )
360
+ await self.service.wallet_state_manager.trade_manager.save_trade(new_trade, new_offer)
361
+ for tx in await self.service.wallet_state_manager.tx_store.get_transactions_by_trade_id(
362
+ old_trade_record.trade_id
363
+ ):
364
+ await self.service.wallet_state_manager.tx_store.add_transaction_record(
365
+ dataclasses.replace(tx, trade_id=new_trade.trade_id)
366
+ )
367
+
368
+ return response
369
+
370
+ return rpc_endpoint
371
+
372
+ return _inner
373
+
374
+
159
375
  class WalletRpcApi:
376
+ if TYPE_CHECKING:
377
+ from chia.rpc.rpc_server import RpcApiProtocol
378
+
379
+ _protocol_check: ClassVar[RpcApiProtocol] = cast("WalletRpcApi", None)
380
+
160
381
  max_get_coin_records_limit: ClassVar[uint32] = uint32(1000)
161
382
  max_get_coin_records_filter_items: ClassVar[uint32] = uint32(1000)
162
383
 
@@ -165,7 +386,7 @@ class WalletRpcApi:
165
386
  self.service = wallet_node
166
387
  self.service_name = "chia_wallet"
167
388
 
168
- def get_routes(self) -> Dict[str, Endpoint]:
389
+ def get_routes(self) -> dict[str, Endpoint]:
169
390
  return {
170
391
  # Key management
171
392
  "/log_in": self.log_in,
@@ -320,10 +541,10 @@ class WalletRpcApi:
320
541
  "/execute_signing_instructions": self.execute_signing_instructions,
321
542
  }
322
543
 
323
- def get_connections(self, request_node_type: Optional[NodeType]) -> List[Dict[str, Any]]:
544
+ def get_connections(self, request_node_type: Optional[NodeType]) -> list[dict[str, Any]]:
324
545
  return default_get_connections(server=self.service.server, request_node_type=request_node_type)
325
546
 
326
- async def _state_changed(self, change: str, change_data: Optional[Dict[str, Any]]) -> List[WsRpcMessage]:
547
+ async def _state_changed(self, change: str, change_data: Optional[dict[str, Any]]) -> list[WsRpcMessage]:
327
548
  """
328
549
  Called by the WalletNode or WalletStateManager when something has changed in the wallet. This
329
550
  gives us an opportunity to send notifications to all connected clients via WebSocket.
@@ -356,8 +577,8 @@ class WalletRpcApi:
356
577
 
357
578
  async def get_latest_singleton_coin_spend(
358
579
  self, peer: WSChiaConnection, coin_id: bytes32, latest: bool = True
359
- ) -> Tuple[CoinSpend, CoinState]:
360
- coin_state_list: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
580
+ ) -> tuple[CoinSpend, CoinState]:
581
+ coin_state_list: list[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
361
582
  [coin_id], peer=peer
362
583
  )
363
584
  if coin_state_list is None or len(coin_state_list) < 1:
@@ -379,7 +600,7 @@ class WalletRpcApi:
379
600
  raise ValueError("Cannot find child coin, please wait then retry.")
380
601
  coin_state = odd_coin
381
602
  # Get parent coin
382
- parent_coin_state_list: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
603
+ parent_coin_state_list: list[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
383
604
  [coin_state.coin.parent_coin_info], peer=peer
384
605
  )
385
606
  if parent_coin_state_list is None or len(parent_coin_state_list) < 1:
@@ -429,7 +650,7 @@ class WalletRpcApi:
429
650
  else:
430
651
  return GetPublicKeysResponse(keyring_is_locked=False, public_key_fingerprints=fingerprints)
431
652
 
432
- async def _get_private_key(self, fingerprint: int) -> Tuple[Optional[PrivateKey], Optional[bytes]]:
653
+ async def _get_private_key(self, fingerprint: int) -> tuple[Optional[PrivateKey], Optional[bytes]]:
433
654
  try:
434
655
  all_keys = await self.service.keychain_proxy.get_all_private_keys()
435
656
  for sk, seed in all_keys:
@@ -501,7 +722,7 @@ class WalletRpcApi:
501
722
 
502
723
  async def _check_key_used_for_rewards(
503
724
  self, new_root: Path, sk: PrivateKey, max_ph_to_search: int
504
- ) -> Tuple[bool, bool]:
725
+ ) -> tuple[bool, bool]:
505
726
  """Checks if the given key is used for either the farmer rewards or pool rewards
506
727
  returns a tuple of two booleans
507
728
  The first is true if the key is used as the Farmer rewards, otherwise false
@@ -511,10 +732,10 @@ class WalletRpcApi:
511
732
  if sk is None:
512
733
  return False, False
513
734
 
514
- config: Dict[str, Any] = load_config(new_root, "config.yaml")
735
+ config: dict[str, Any] = load_config(new_root, "config.yaml")
515
736
  farmer_target = config["farmer"].get("xch_target_address", "")
516
737
  pool_target = config["pool"].get("xch_target_address", "")
517
- address_to_check: List[bytes32] = []
738
+ address_to_check: list[bytes32] = []
518
739
 
519
740
  try:
520
741
  farmer_decoded = decode_puzzle_hash(farmer_target)
@@ -528,7 +749,7 @@ class WalletRpcApi:
528
749
  except ValueError:
529
750
  pool_decoded = None
530
751
 
531
- found_addresses: Set[bytes32] = match_address_to_sk(sk, address_to_check, max_ph_to_search)
752
+ found_addresses: set[bytes32] = match_address_to_sk(sk, address_to_check, max_ph_to_search)
532
753
  found_farmer = False
533
754
  found_pool = False
534
755
 
@@ -560,7 +781,7 @@ class WalletRpcApi:
560
781
  await self._stop_wallet()
561
782
  await self.service._start_with_fingerprint(fingerprint=request.fingerprint)
562
783
 
563
- wallets: List[WalletInfo] = await self.service.wallet_state_manager.get_all_wallet_info_entries()
784
+ wallets: list[WalletInfo] = await self.service.wallet_state_manager.get_all_wallet_info_entries()
564
785
  for w in wallets:
565
786
  wallet = self.service.wallet_state_manager.wallets[w.id]
566
787
  unspent = await self.service.wallet_state_manager.coin_store.get_unspent_coins_for_wallet(w.id)
@@ -594,64 +815,55 @@ class WalletRpcApi:
594
815
  ##########################################################################################
595
816
  # Wallet Node
596
817
  ##########################################################################################
597
- async def set_wallet_resync_on_startup(self, request: Dict[str, Any]) -> Dict[str, Any]:
818
+ @marshal
819
+ async def set_wallet_resync_on_startup(self, request: SetWalletResyncOnStartup) -> Empty:
598
820
  """
599
821
  Resync the current logged in wallet. The transaction and offer records will be kept.
600
822
  :param request: optionally pass in `enable` as bool to enable/disable resync
601
823
  :return:
602
824
  """
603
825
  assert self.service.wallet_state_manager is not None
604
- try:
605
- enable = bool(request.get("enable", True))
606
- except ValueError:
607
- raise ValueError("Please provide a boolean value for `enable` parameter in request")
608
826
  fingerprint = self.service.logged_in_fingerprint
609
827
  if fingerprint is not None:
610
- self.service.set_resync_on_startup(fingerprint, enable)
828
+ self.service.set_resync_on_startup(fingerprint, request.enable)
611
829
  else:
612
830
  raise ValueError("You need to login into wallet to use this RPC call")
613
- return {"success": True}
831
+ return Empty()
614
832
 
615
- async def get_sync_status(self, request: Dict[str, Any]) -> EndpointResult:
833
+ @marshal
834
+ async def get_sync_status(self, request: Empty) -> GetSyncStatusResponse:
616
835
  sync_mode = self.service.wallet_state_manager.sync_mode
617
836
  has_pending_queue_items = self.service.new_peak_queue.has_pending_data_process_items()
618
837
  syncing = sync_mode or has_pending_queue_items
619
838
  synced = await self.service.wallet_state_manager.synced()
620
- return {"synced": synced, "syncing": syncing, "genesis_initialized": True}
839
+ return GetSyncStatusResponse(synced=synced, syncing=syncing)
621
840
 
622
- async def get_height_info(self, request: Dict[str, Any]) -> EndpointResult:
841
+ @marshal
842
+ async def get_height_info(self, request: Empty) -> GetHeightInfoResponse:
623
843
  height = await self.service.wallet_state_manager.blockchain.get_finished_sync_up_to()
624
- return {"height": height}
844
+ return GetHeightInfoResponse(height=height)
625
845
 
626
- async def push_tx(self, request: Dict[str, Any]) -> EndpointResult:
846
+ @marshal
847
+ async def push_tx(self, request: PushTX) -> Empty:
627
848
  nodes = self.service.server.get_connections(NodeType.FULL_NODE)
628
849
  if len(nodes) == 0:
629
850
  raise ValueError("Wallet is not currently connected to any full node peers")
630
- await self.service.push_tx(WalletSpendBundle.from_bytes(hexstr_to_bytes(request["spend_bundle"])))
631
- return {}
851
+ await self.service.push_tx(request.spend_bundle)
852
+ return Empty()
632
853
 
633
854
  @tx_endpoint(push=True)
855
+ @marshal
634
856
  async def push_transactions(
635
857
  self,
636
- request: Dict[str, Any],
858
+ request: PushTransactions,
637
859
  action_scope: WalletActionScope,
638
- extra_conditions: Tuple[Condition, ...] = tuple(),
639
- ) -> EndpointResult:
860
+ extra_conditions: tuple[Condition, ...] = tuple(),
861
+ ) -> PushTransactionsResponse:
640
862
  if not action_scope.config.push:
641
863
  raise ValueError("Cannot push transactions if push is False")
642
864
  async with action_scope.use() as interface:
643
- for transaction_hexstr_or_json in request["transactions"]:
644
- if isinstance(transaction_hexstr_or_json, str):
645
- tx = TransactionRecord.from_bytes(hexstr_to_bytes(transaction_hexstr_or_json))
646
- interface.side_effects.transactions.append(tx)
647
- else:
648
- try:
649
- tx = TransactionRecord.from_json_dict_convenience(transaction_hexstr_or_json)
650
- except AttributeError:
651
- tx = TransactionRecord.from_json_dict(transaction_hexstr_or_json)
652
- interface.side_effects.transactions.append(tx)
653
-
654
- if request.get("fee", 0) != 0:
865
+ interface.side_effects.transactions.extend(request.transactions)
866
+ if request.fee != 0:
655
867
  all_conditions_and_origins = [
656
868
  (condition, cs.coin.name())
657
869
  for tx in interface.side_effects.transactions
@@ -682,7 +894,7 @@ class WalletRpcApi:
682
894
  push=False,
683
895
  ) as inner_action_scope:
684
896
  await self.service.wallet_state_manager.main_wallet.create_tandem_xch_tx(
685
- uint64(request["fee"]),
897
+ request.fee,
686
898
  inner_action_scope,
687
899
  (
688
900
  *extra_conditions,
@@ -694,20 +906,23 @@ class WalletRpcApi:
694
906
 
695
907
  interface.side_effects.transactions.extend(inner_action_scope.side_effects.transactions)
696
908
 
697
- return {}
909
+ return PushTransactionsResponse([], []) # tx_endpoint takes care of this
698
910
 
699
- async def get_timestamp_for_height(self, request: Dict[str, Any]) -> EndpointResult:
700
- return {"timestamp": await self.service.get_timestamp_for_height(uint32(request["height"]))}
911
+ @marshal
912
+ async def get_timestamp_for_height(self, request: GetTimestampForHeight) -> GetTimestampForHeightResponse:
913
+ return GetTimestampForHeightResponse(await self.service.get_timestamp_for_height(request.height))
701
914
 
702
- async def set_auto_claim(self, request: Dict[str, Any]) -> EndpointResult:
915
+ @marshal
916
+ async def set_auto_claim(self, request: AutoClaimSettings) -> AutoClaimSettings:
703
917
  """
704
918
  Set auto claim merkle coins config
705
919
  :param request: Example {"enable": true, "tx_fee": 100000, "min_amount": 0, "batch_size": 50}
706
920
  :return:
707
921
  """
708
- return self.service.set_auto_claim(AutoClaimSettings.from_json_dict(request))
922
+ return AutoClaimSettings.from_json_dict(self.service.set_auto_claim(request))
709
923
 
710
- async def get_auto_claim(self, request: Dict[str, Any]) -> EndpointResult:
924
+ @marshal
925
+ async def get_auto_claim(self, request: Empty) -> AutoClaimSettings:
711
926
  """
712
927
  Get auto claim merkle coins config
713
928
  :param request: None
@@ -716,21 +931,21 @@ class WalletRpcApi:
716
931
  auto_claim_settings = AutoClaimSettings.from_json_dict(
717
932
  self.service.wallet_state_manager.config.get("auto_claim", {})
718
933
  )
719
- return auto_claim_settings.to_json_dict()
934
+ return auto_claim_settings
720
935
 
721
936
  ##########################################################################################
722
937
  # Wallet Management
723
938
  ##########################################################################################
724
939
 
725
- async def get_wallets(self, request: Dict[str, Any]) -> EndpointResult:
940
+ async def get_wallets(self, request: dict[str, Any]) -> EndpointResult:
726
941
  include_data: bool = request.get("include_data", True)
727
942
  wallet_type: Optional[WalletType] = None
728
943
  if "type" in request:
729
944
  wallet_type = WalletType(request["type"])
730
945
 
731
- wallets: List[WalletInfo] = await self.service.wallet_state_manager.get_all_wallet_info_entries(wallet_type)
946
+ wallets: list[WalletInfo] = await self.service.wallet_state_manager.get_all_wallet_info_entries(wallet_type)
732
947
  if not include_data:
733
- result: List[WalletInfo] = []
948
+ result: list[WalletInfo] = []
734
949
  for wallet in wallets:
735
950
  result.append(WalletInfo(wallet.id, wallet.name, wallet.type, ""))
736
951
  wallets = result
@@ -759,9 +974,9 @@ class WalletRpcApi:
759
974
  @tx_endpoint(push=True)
760
975
  async def create_new_wallet(
761
976
  self,
762
- request: Dict[str, Any],
977
+ request: dict[str, Any],
763
978
  action_scope: WalletActionScope,
764
- extra_conditions: Tuple[Condition, ...] = tuple(),
979
+ extra_conditions: tuple[Condition, ...] = tuple(),
765
980
  ) -> EndpointResult:
766
981
  wallet_state_manager = self.service.wallet_state_manager
767
982
 
@@ -819,7 +1034,7 @@ class WalletRpcApi:
819
1034
  backup_dids.append(decode_puzzle_hash(d))
820
1035
  if len(backup_dids) > 0:
821
1036
  num_needed = uint64(request["num_of_backup_ids_needed"])
822
- metadata: Dict[str, str] = {}
1037
+ metadata: dict[str, str] = {}
823
1038
  if "metadata" in request:
824
1039
  if type(request["metadata"]) is dict:
825
1040
  metadata = request["metadata"]
@@ -959,7 +1174,9 @@ class WalletRpcApi:
959
1174
  if "initial_target_state" not in request:
960
1175
  raise AttributeError("Daemon didn't send `initial_target_state`. Try updating the daemon.")
961
1176
 
962
- owner_puzzle_hash: bytes32 = await self.service.wallet_state_manager.main_wallet.get_puzzle_hash(True)
1177
+ owner_puzzle_hash: bytes32 = await self.service.wallet_state_manager.main_wallet.get_puzzle_hash(
1178
+ new=not action_scope.config.tx_config.reuse_puzhash
1179
+ )
963
1180
 
964
1181
  from chia.pools.pool_wallet_info import initial_pool_state_from_dict
965
1182
 
@@ -1027,7 +1244,7 @@ class WalletRpcApi:
1027
1244
  # Wallet
1028
1245
  ##########################################################################################
1029
1246
 
1030
- async def _get_wallet_balance(self, wallet_id: uint32) -> Dict[str, Any]:
1247
+ async def _get_wallet_balance(self, wallet_id: uint32) -> dict[str, Any]:
1031
1248
  wallet = self.service.wallet_state_manager.wallets[wallet_id]
1032
1249
  balance = await self.service.get_balance(wallet_id)
1033
1250
  wallet_balance = balance.to_json_dict()
@@ -1044,22 +1261,22 @@ class WalletRpcApi:
1044
1261
 
1045
1262
  return wallet_balance
1046
1263
 
1047
- async def get_wallet_balance(self, request: Dict[str, Any]) -> EndpointResult:
1264
+ async def get_wallet_balance(self, request: dict[str, Any]) -> EndpointResult:
1048
1265
  wallet_id = uint32(int(request["wallet_id"]))
1049
1266
  wallet_balance = await self._get_wallet_balance(wallet_id)
1050
1267
  return {"wallet_balance": wallet_balance}
1051
1268
 
1052
- async def get_wallet_balances(self, request: Dict[str, Any]) -> EndpointResult:
1269
+ async def get_wallet_balances(self, request: dict[str, Any]) -> EndpointResult:
1053
1270
  try:
1054
- wallet_ids: List[uint32] = [uint32(int(wallet_id)) for wallet_id in request["wallet_ids"]]
1271
+ wallet_ids: list[uint32] = [uint32(int(wallet_id)) for wallet_id in request["wallet_ids"]]
1055
1272
  except (TypeError, KeyError):
1056
1273
  wallet_ids = list(self.service.wallet_state_manager.wallets.keys())
1057
- wallet_balances: Dict[uint32, Dict[str, Any]] = {}
1274
+ wallet_balances: dict[uint32, dict[str, Any]] = {}
1058
1275
  for wallet_id in wallet_ids:
1059
1276
  wallet_balances[wallet_id] = await self._get_wallet_balance(wallet_id)
1060
1277
  return {"wallet_balances": wallet_balances}
1061
1278
 
1062
- async def get_transaction(self, request: Dict[str, Any]) -> EndpointResult:
1279
+ async def get_transaction(self, request: dict[str, Any]) -> EndpointResult:
1063
1280
  transaction_id: bytes32 = bytes32.from_hexstr(request["transaction_id"])
1064
1281
  tr: Optional[TransactionRecord] = await self.service.wallet_state_manager.get_transaction(transaction_id)
1065
1282
  if tr is None:
@@ -1070,7 +1287,7 @@ class WalletRpcApi:
1070
1287
  "transaction_id": tr.name,
1071
1288
  }
1072
1289
 
1073
- async def get_transaction_memo(self, request: Dict[str, Any]) -> EndpointResult:
1290
+ async def get_transaction_memo(self, request: dict[str, Any]) -> EndpointResult:
1074
1291
  transaction_id: bytes32 = bytes32.from_hexstr(request["transaction_id"])
1075
1292
  tr: Optional[TransactionRecord] = await self.service.wallet_state_manager.get_transaction(transaction_id)
1076
1293
  if tr is None:
@@ -1080,7 +1297,7 @@ class WalletRpcApi:
1080
1297
  # Fetch incoming tx coin spend
1081
1298
  peer = self.service.get_full_node_peer()
1082
1299
  assert len(tr.additions) == 1
1083
- coin_state_list: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
1300
+ coin_state_list: list[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
1084
1301
  [tr.additions[0].parent_coin_info], peer=peer
1085
1302
  )
1086
1303
  assert len(coin_state_list) == 1
@@ -1089,7 +1306,7 @@ class WalletRpcApi:
1089
1306
  else:
1090
1307
  raise ValueError(f"Transaction 0x{transaction_id.hex()} doesn't have any coin spend.")
1091
1308
  assert tr.spend_bundle is not None
1092
- memos: Dict[bytes32, List[bytes]] = compute_memos(tr.spend_bundle)
1309
+ memos: dict[bytes32, list[bytes]] = compute_memos(tr.spend_bundle)
1093
1310
  response = {}
1094
1311
  # Convert to hex string
1095
1312
  for coin_id, memo_list in memos.items():
@@ -1099,7 +1316,7 @@ class WalletRpcApi:
1099
1316
  @tx_endpoint(push=False)
1100
1317
  @marshal
1101
1318
  async def split_coins(
1102
- self, request: SplitCoins, action_scope: WalletActionScope, extra_conditions: Tuple[Condition, ...] = tuple()
1319
+ self, request: SplitCoins, action_scope: WalletActionScope, extra_conditions: tuple[Condition, ...] = tuple()
1103
1320
  ) -> SplitCoinsResponse:
1104
1321
  if request.number_of_coins > 500:
1105
1322
  raise ValueError(f"{request.number_of_coins} coins is greater then the maximum limit of 500 coins.")
@@ -1124,7 +1341,12 @@ class WalletRpcApi:
1124
1341
  raise ValueError("Cannot split coins from non-fungible wallet types")
1125
1342
 
1126
1343
  outputs = [
1127
- Payment(await wallet.get_puzzle_hash(new=True), request.amount_per_coin)
1344
+ Payment(
1345
+ await wallet.get_puzzle_hash(new=True)
1346
+ if isinstance(wallet, Wallet)
1347
+ else await wallet.standard_wallet.get_puzzle_hash(new=True),
1348
+ request.amount_per_coin,
1349
+ )
1128
1350
  for _ in range(request.number_of_coins)
1129
1351
  ]
1130
1352
  if len(outputs) == 0:
@@ -1168,9 +1390,8 @@ class WalletRpcApi:
1168
1390
  @tx_endpoint(push=False)
1169
1391
  @marshal
1170
1392
  async def combine_coins(
1171
- self, request: CombineCoins, action_scope: WalletActionScope, extra_conditions: Tuple[Condition, ...] = tuple()
1393
+ self, request: CombineCoins, action_scope: WalletActionScope, extra_conditions: tuple[Condition, ...] = tuple()
1172
1394
  ) -> CombineCoinsResponse:
1173
-
1174
1395
  # Some "number of coins" validation
1175
1396
  if request.number_of_coins > request.coin_num_limit:
1176
1397
  raise ValueError(
@@ -1187,7 +1408,7 @@ class WalletRpcApi:
1187
1408
  if not isinstance(wallet, (Wallet, CATWallet)):
1188
1409
  raise ValueError("Cannot combine coins from non-fungible wallet types")
1189
1410
 
1190
- coins: List[Coin] = []
1411
+ coins: list[Coin] = []
1191
1412
 
1192
1413
  # First get the coin IDs specified
1193
1414
  if request.target_coin_ids != []:
@@ -1250,7 +1471,7 @@ class WalletRpcApi:
1250
1471
  primary_output_amount = uint64(primary_output_amount - request.fee)
1251
1472
  await wallet.generate_signed_transaction(
1252
1473
  primary_output_amount,
1253
- await wallet.get_puzzle_hash(new=action_scope.config.tx_config.reuse_puzhash),
1474
+ await wallet.get_puzzle_hash(new=not action_scope.config.tx_config.reuse_puzhash),
1254
1475
  action_scope,
1255
1476
  request.fee,
1256
1477
  set(coins),
@@ -1260,7 +1481,7 @@ class WalletRpcApi:
1260
1481
  assert isinstance(wallet, CATWallet)
1261
1482
  await wallet.generate_signed_transaction(
1262
1483
  [primary_output_amount],
1263
- [await wallet.get_puzzle_hash(new=action_scope.config.tx_config.reuse_puzhash)],
1484
+ [await wallet.standard_wallet.get_puzzle_hash(new=not action_scope.config.tx_config.reuse_puzhash)],
1264
1485
  action_scope,
1265
1486
  request.fee,
1266
1487
  coins=set(coins),
@@ -1269,7 +1490,7 @@ class WalletRpcApi:
1269
1490
 
1270
1491
  return CombineCoinsResponse([], []) # tx_endpoint will take care to fill this out
1271
1492
 
1272
- async def get_transactions(self, request: Dict[str, Any]) -> EndpointResult:
1493
+ async def get_transactions(self, request: dict[str, Any]) -> EndpointResult:
1273
1494
  wallet_id = int(request["wallet_id"])
1274
1495
 
1275
1496
  start = request.get("start", 0)
@@ -1321,7 +1542,7 @@ class WalletRpcApi:
1321
1542
  "wallet_id": wallet_id,
1322
1543
  }
1323
1544
 
1324
- async def get_transaction_count(self, request: Dict[str, Any]) -> EndpointResult:
1545
+ async def get_transaction_count(self, request: dict[str, Any]) -> EndpointResult:
1325
1546
  wallet_id = int(request["wallet_id"])
1326
1547
  type_filter = None
1327
1548
  if "type_filter" in request:
@@ -1334,7 +1555,7 @@ class WalletRpcApi:
1334
1555
  "wallet_id": wallet_id,
1335
1556
  }
1336
1557
 
1337
- async def get_next_address(self, request: Dict[str, Any]) -> EndpointResult:
1558
+ async def get_next_address(self, request: dict[str, Any]) -> EndpointResult:
1338
1559
  """
1339
1560
  Returns a new address
1340
1561
  """
@@ -1365,9 +1586,9 @@ class WalletRpcApi:
1365
1586
  @tx_endpoint(push=True)
1366
1587
  async def send_transaction(
1367
1588
  self,
1368
- request: Dict[str, Any],
1589
+ request: dict[str, Any],
1369
1590
  action_scope: WalletActionScope,
1370
- extra_conditions: Tuple[Condition, ...] = tuple(),
1591
+ extra_conditions: tuple[Condition, ...] = tuple(),
1371
1592
  ) -> EndpointResult:
1372
1593
  if await self.service.wallet_state_manager.synced() is False:
1373
1594
  raise ValueError("Wallet needs to be fully synced before sending transactions")
@@ -1385,7 +1606,7 @@ class WalletRpcApi:
1385
1606
  raise ValueError("Unexpected Address Prefix")
1386
1607
  puzzle_hash: bytes32 = decode_puzzle_hash(address)
1387
1608
 
1388
- memos: List[bytes] = []
1609
+ memos: list[bytes] = []
1389
1610
  if "memos" in request:
1390
1611
  memos = [mem.encode("utf-8") for mem in request["memos"]]
1391
1612
 
@@ -1409,7 +1630,7 @@ class WalletRpcApi:
1409
1630
  "transaction_id": None, # tx_endpoint wrapper will take care of this
1410
1631
  }
1411
1632
 
1412
- async def send_transaction_multi(self, request: Dict[str, Any]) -> EndpointResult:
1633
+ async def send_transaction_multi(self, request: dict[str, Any]) -> EndpointResult:
1413
1634
  if await self.service.wallet_state_manager.synced() is False:
1414
1635
  raise ValueError("Wallet needs to be fully synced before sending transactions")
1415
1636
 
@@ -1442,9 +1663,9 @@ class WalletRpcApi:
1442
1663
  @tx_endpoint(push=True, merge_spends=False)
1443
1664
  async def spend_clawback_coins(
1444
1665
  self,
1445
- request: Dict[str, Any],
1666
+ request: dict[str, Any],
1446
1667
  action_scope: WalletActionScope,
1447
- extra_conditions: Tuple[Condition, ...] = tuple(),
1668
+ extra_conditions: tuple[Condition, ...] = tuple(),
1448
1669
  ) -> EndpointResult:
1449
1670
  """Spend clawback coins that were sent (to claw them back) or received (to claim them).
1450
1671
 
@@ -1455,7 +1676,7 @@ class WalletRpcApi:
1455
1676
  """
1456
1677
  if "coin_ids" not in request:
1457
1678
  raise ValueError("Coin IDs are required.")
1458
- coin_ids: List[bytes32] = [bytes32.from_hexstr(coin) for coin in request["coin_ids"]]
1679
+ coin_ids: list[bytes32] = [bytes32.from_hexstr(coin) for coin in request["coin_ids"]]
1459
1680
  tx_fee: uint64 = uint64(request.get("fee", 0))
1460
1681
  # Get inner puzzle
1461
1682
  coin_records = await self.service.wallet_state_manager.coin_store.get_coin_records(
@@ -1465,7 +1686,7 @@ class WalletRpcApi:
1465
1686
  spent_range=UInt32Range(stop=uint32(0)),
1466
1687
  )
1467
1688
 
1468
- coins: Dict[Coin, ClawbackMetadata] = {}
1689
+ coins: dict[Coin, ClawbackMetadata] = {}
1469
1690
  batch_size = request.get(
1470
1691
  "batch_size", self.service.wallet_state_manager.config.get("auto_claim", {}).get("batch_size", 50)
1471
1692
  )
@@ -1482,18 +1703,6 @@ class WalletRpcApi:
1482
1703
  request.get("force", False),
1483
1704
  extra_conditions=extra_conditions,
1484
1705
  )
1485
- async with action_scope.use() as interface:
1486
- # TODO: editing this is not ideal. Action scopes should know what coins have been spent.
1487
- action_scope._config = dataclasses.replace(
1488
- action_scope._config,
1489
- tx_config=dataclasses.replace(
1490
- action_scope._config.tx_config,
1491
- excluded_coin_ids=[
1492
- *action_scope._config.tx_config.excluded_coin_ids,
1493
- *(c.name() for tx in interface.side_effects.transactions for c in tx.removals),
1494
- ],
1495
- ),
1496
- )
1497
1706
  coins = {}
1498
1707
  except Exception as e:
1499
1708
  log.error(f"Failed to spend clawback coin {coin_id.hex()}: %s", e)
@@ -1512,7 +1721,7 @@ class WalletRpcApi:
1512
1721
  "transactions": None, # tx_endpoint wrapper will take care of this
1513
1722
  }
1514
1723
 
1515
- async def delete_unconfirmed_transactions(self, request: Dict[str, Any]) -> EndpointResult:
1724
+ async def delete_unconfirmed_transactions(self, request: dict[str, Any]) -> EndpointResult:
1516
1725
  wallet_id = uint32(request["wallet_id"])
1517
1726
  if wallet_id not in self.service.wallet_state_manager.wallets:
1518
1727
  raise ValueError(f"Wallet id {wallet_id} does not exist")
@@ -1529,14 +1738,14 @@ class WalletRpcApi:
1529
1738
 
1530
1739
  async def select_coins(
1531
1740
  self,
1532
- request: Dict[str, Any],
1741
+ request: dict[str, Any],
1533
1742
  ) -> EndpointResult:
1534
1743
  assert self.service.logged_in_fingerprint is not None
1535
1744
  tx_config_loader: TXConfigLoader = TXConfigLoader.from_json_dict(request)
1536
1745
 
1537
1746
  # Some backwards compat fill-ins
1538
1747
  if tx_config_loader.excluded_coin_ids is None:
1539
- excluded_coins: Optional[List[Dict[str, Any]]] = request.get("excluded_coins", request.get("exclude_coins"))
1748
+ excluded_coins: Optional[list[dict[str, Any]]] = request.get("excluded_coins", request.get("exclude_coins"))
1540
1749
  if excluded_coins is not None:
1541
1750
  tx_config_loader = tx_config_loader.override(
1542
1751
  excluded_coin_ids=[Coin.from_json_dict(c).name() for c in excluded_coins],
@@ -1558,7 +1767,7 @@ class WalletRpcApi:
1558
1767
 
1559
1768
  return {"coins": [coin.to_json_dict() for coin in selected_coins]}
1560
1769
 
1561
- async def get_spendable_coins(self, request: Dict[str, Any]) -> EndpointResult:
1770
+ async def get_spendable_coins(self, request: dict[str, Any]) -> EndpointResult:
1562
1771
  if await self.service.wallet_state_manager.synced() is False:
1563
1772
  raise ValueError("Wallet needs to be fully synced before getting all coins")
1564
1773
 
@@ -1567,17 +1776,17 @@ class WalletRpcApi:
1567
1776
  max_coin_amount: uint64 = uint64(request.get("max_coin_amount", 0))
1568
1777
  if max_coin_amount == 0:
1569
1778
  max_coin_amount = uint64(self.service.wallet_state_manager.constants.MAX_COIN_AMOUNT)
1570
- excluded_coin_amounts: Optional[List[uint64]] = request.get("excluded_coin_amounts")
1779
+ excluded_coin_amounts: Optional[list[uint64]] = request.get("excluded_coin_amounts")
1571
1780
  if excluded_coin_amounts is not None:
1572
1781
  excluded_coin_amounts = [uint64(a) for a in excluded_coin_amounts]
1573
1782
  else:
1574
1783
  excluded_coin_amounts = []
1575
- excluded_coins_input: Optional[Dict[str, Dict[str, Any]]] = request.get("excluded_coins")
1784
+ excluded_coins_input: Optional[dict[str, dict[str, Any]]] = request.get("excluded_coins")
1576
1785
  if excluded_coins_input is not None:
1577
1786
  excluded_coins = [Coin.from_json_dict(json_coin) for json_coin in excluded_coins_input.values()]
1578
1787
  else:
1579
1788
  excluded_coins = []
1580
- excluded_coin_ids_input: Optional[List[str]] = request.get("excluded_coin_ids")
1789
+ excluded_coin_ids_input: Optional[list[str]] = request.get("excluded_coin_ids")
1581
1790
  if excluded_coin_ids_input is not None:
1582
1791
  excluded_coin_ids = [bytes32.from_hexstr(hex_id) for hex_id in excluded_coin_ids_input]
1583
1792
  else:
@@ -1588,27 +1797,27 @@ class WalletRpcApi:
1588
1797
  all_coin_records = await state_mgr.coin_store.get_unspent_coins_for_wallet(wallet_id)
1589
1798
  if wallet.type() in {WalletType.CAT, WalletType.CRCAT}:
1590
1799
  assert isinstance(wallet, CATWallet)
1591
- spendable_coins: List[WalletCoinRecord] = await wallet.get_cat_spendable_coins(all_coin_records)
1800
+ spendable_coins: list[WalletCoinRecord] = await wallet.get_cat_spendable_coins(all_coin_records)
1592
1801
  else:
1593
1802
  spendable_coins = list(await state_mgr.get_spendable_coins_for_wallet(wallet_id, all_coin_records))
1594
1803
 
1595
1804
  # Now we get the unconfirmed transactions and manually derive the additions and removals.
1596
- unconfirmed_transactions: List[TransactionRecord] = await state_mgr.tx_store.get_unconfirmed_for_wallet(
1805
+ unconfirmed_transactions: list[TransactionRecord] = await state_mgr.tx_store.get_unconfirmed_for_wallet(
1597
1806
  wallet_id
1598
1807
  )
1599
- unconfirmed_removal_ids: Dict[bytes32, uint64] = {
1808
+ unconfirmed_removal_ids: dict[bytes32, uint64] = {
1600
1809
  coin.name(): transaction.created_at_time
1601
1810
  for transaction in unconfirmed_transactions
1602
1811
  for coin in transaction.removals
1603
1812
  }
1604
- unconfirmed_additions: List[Coin] = [
1813
+ unconfirmed_additions: list[Coin] = [
1605
1814
  coin
1606
1815
  for transaction in unconfirmed_transactions
1607
1816
  for coin in transaction.additions
1608
1817
  if await state_mgr.does_coin_belong_to_wallet(coin, wallet_id)
1609
1818
  ]
1610
- valid_spendable_cr: List[CoinRecord] = []
1611
- unconfirmed_removals: List[CoinRecord] = []
1819
+ valid_spendable_cr: list[CoinRecord] = []
1820
+ unconfirmed_removals: list[CoinRecord] = []
1612
1821
  for coin_record in all_coin_records:
1613
1822
  if coin_record.name() in unconfirmed_removal_ids:
1614
1823
  unconfirmed_removals.append(coin_record.to_coin_record(unconfirmed_removal_ids[coin_record.name()]))
@@ -1633,14 +1842,14 @@ class WalletRpcApi:
1633
1842
  "unconfirmed_additions": [coin.to_json_dict() for coin in unconfirmed_additions],
1634
1843
  }
1635
1844
 
1636
- async def get_coin_records_by_names(self, request: Dict[str, Any]) -> EndpointResult:
1845
+ async def get_coin_records_by_names(self, request: dict[str, Any]) -> EndpointResult:
1637
1846
  if await self.service.wallet_state_manager.synced() is False:
1638
1847
  raise ValueError("Wallet needs to be fully synced before finding coin information")
1639
1848
 
1640
1849
  if "names" not in request:
1641
1850
  raise ValueError("Names not in request")
1642
1851
  coin_ids = [bytes32.from_hexstr(name) for name in request["names"]]
1643
- kwargs: Dict[str, Any] = {
1852
+ kwargs: dict[str, Any] = {
1644
1853
  "coin_id_filter": HashFilter.include(coin_ids),
1645
1854
  }
1646
1855
 
@@ -1656,10 +1865,10 @@ class WalletRpcApi:
1656
1865
  kwargs["spent_range"] = unspent_range
1657
1866
 
1658
1867
  async with self.service.wallet_state_manager.lock:
1659
- coin_records: List[CoinRecord] = await self.service.wallet_state_manager.get_coin_records_by_coin_ids(
1868
+ coin_records: list[CoinRecord] = await self.service.wallet_state_manager.get_coin_records_by_coin_ids(
1660
1869
  **kwargs
1661
1870
  )
1662
- missed_coins: List[str] = [
1871
+ missed_coins: list[str] = [
1663
1872
  "0x" + c_id.hex() for c_id in coin_ids if c_id not in [cr.name for cr in coin_records]
1664
1873
  ]
1665
1874
  if missed_coins:
@@ -1667,14 +1876,14 @@ class WalletRpcApi:
1667
1876
 
1668
1877
  return {"coin_records": [cr.to_json_dict() for cr in coin_records]}
1669
1878
 
1670
- async def get_current_derivation_index(self, request: Dict[str, Any]) -> Dict[str, Any]:
1879
+ async def get_current_derivation_index(self, request: dict[str, Any]) -> dict[str, Any]:
1671
1880
  assert self.service.wallet_state_manager is not None
1672
1881
 
1673
1882
  index: Optional[uint32] = await self.service.wallet_state_manager.puzzle_store.get_last_derivation_path()
1674
1883
 
1675
1884
  return {"success": True, "index": index}
1676
1885
 
1677
- async def extend_derivation_index(self, request: Dict[str, Any]) -> Dict[str, Any]:
1886
+ async def extend_derivation_index(self, request: dict[str, Any]) -> dict[str, Any]:
1678
1887
  assert self.service.wallet_state_manager is not None
1679
1888
 
1680
1889
  # Require a new max derivation index
@@ -1718,10 +1927,10 @@ class WalletRpcApi:
1718
1927
  @marshal
1719
1928
  async def get_notifications(self, request: GetNotifications) -> GetNotificationsResponse:
1720
1929
  if request.ids is None:
1721
- notifications: List[Notification] = (
1722
- await self.service.wallet_state_manager.notification_manager.notification_store.get_all_notifications(
1723
- pagination=(request.start, request.end)
1724
- )
1930
+ notifications: list[
1931
+ Notification
1932
+ ] = await self.service.wallet_state_manager.notification_manager.notification_store.get_all_notifications(
1933
+ pagination=(request.start, request.end)
1725
1934
  )
1726
1935
  else:
1727
1936
  notifications = (
@@ -1732,8 +1941,8 @@ class WalletRpcApi:
1732
1941
 
1733
1942
  return GetNotificationsResponse(notifications)
1734
1943
 
1735
- async def delete_notifications(self, request: Dict[str, Any]) -> EndpointResult:
1736
- ids: Optional[List[str]] = request.get("ids", None)
1944
+ async def delete_notifications(self, request: dict[str, Any]) -> EndpointResult:
1945
+ ids: Optional[list[str]] = request.get("ids", None)
1737
1946
  if ids is None:
1738
1947
  await self.service.wallet_state_manager.notification_manager.notification_store.delete_all_notifications()
1739
1948
  else:
@@ -1746,9 +1955,9 @@ class WalletRpcApi:
1746
1955
  @tx_endpoint(push=True)
1747
1956
  async def send_notification(
1748
1957
  self,
1749
- request: Dict[str, Any],
1958
+ request: dict[str, Any],
1750
1959
  action_scope: WalletActionScope,
1751
- extra_conditions: Tuple[Condition, ...] = tuple(),
1960
+ extra_conditions: tuple[Condition, ...] = tuple(),
1752
1961
  ) -> EndpointResult:
1753
1962
  await self.service.wallet_state_manager.notification_manager.send_new_notification(
1754
1963
  bytes32.from_hexstr(request["target"]),
@@ -1761,7 +1970,7 @@ class WalletRpcApi:
1761
1970
 
1762
1971
  return {"tx": None, "transactions": None} # tx_endpoint wrapper will take care of this
1763
1972
 
1764
- async def verify_signature(self, request: Dict[str, Any]) -> EndpointResult:
1973
+ async def verify_signature(self, request: dict[str, Any]) -> EndpointResult:
1765
1974
  """
1766
1975
  Given a public key, message and signature, verify if it is valid.
1767
1976
  :param request:
@@ -1779,7 +1988,7 @@ class WalletRpcApi:
1779
1988
  except ValueError:
1780
1989
  raise ValueError(f"Invalid signing mode: {signing_mode_str!r}")
1781
1990
 
1782
- if signing_mode == SigningMode.CHIP_0002 or signing_mode == SigningMode.CHIP_0002_P2_DELEGATED_CONDITIONS:
1991
+ if signing_mode in {SigningMode.CHIP_0002, SigningMode.CHIP_0002_P2_DELEGATED_CONDITIONS}:
1783
1992
  # CHIP-0002 message signatures are made over the tree hash of:
1784
1993
  # ("Chia Signed Message", message)
1785
1994
  message_to_verify: bytes = Program.to((CHIP_0002_SIGN_MESSAGE_PREFIX, input_message)).get_tree_hash()
@@ -1819,7 +2028,7 @@ class WalletRpcApi:
1819
2028
  else:
1820
2029
  return {"isValid": False, "error": "Signature is invalid."}
1821
2030
 
1822
- async def sign_message_by_address(self, request: Dict[str, Any]) -> EndpointResult:
2031
+ async def sign_message_by_address(self, request: dict[str, Any]) -> EndpointResult:
1823
2032
  """
1824
2033
  Given a derived P2 address, sign the message by its private key.
1825
2034
  :param request:
@@ -1849,7 +2058,7 @@ class WalletRpcApi:
1849
2058
  "signing_mode": mode.value,
1850
2059
  }
1851
2060
 
1852
- async def sign_message_by_id(self, request: Dict[str, Any]) -> EndpointResult:
2061
+ async def sign_message_by_id(self, request: dict[str, Any]) -> EndpointResult:
1853
2062
  """
1854
2063
  Given a NFT/DID ID, sign the message by the P2 private key.
1855
2064
  :param request:
@@ -1914,22 +2123,22 @@ class WalletRpcApi:
1914
2123
  # CATs and Trading
1915
2124
  ##########################################################################################
1916
2125
 
1917
- async def get_cat_list(self, request: Dict[str, Any]) -> EndpointResult:
2126
+ async def get_cat_list(self, request: dict[str, Any]) -> EndpointResult:
1918
2127
  return {"cat_list": list(DEFAULT_CATS.values())}
1919
2128
 
1920
- async def cat_set_name(self, request: Dict[str, Any]) -> EndpointResult:
2129
+ async def cat_set_name(self, request: dict[str, Any]) -> EndpointResult:
1921
2130
  wallet_id = uint32(request["wallet_id"])
1922
2131
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=CATWallet)
1923
2132
  await wallet.set_name(str(request["name"]))
1924
2133
  return {"wallet_id": wallet_id}
1925
2134
 
1926
- async def cat_get_name(self, request: Dict[str, Any]) -> EndpointResult:
2135
+ async def cat_get_name(self, request: dict[str, Any]) -> EndpointResult:
1927
2136
  wallet_id = uint32(request["wallet_id"])
1928
2137
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=CATWallet)
1929
2138
  name: str = wallet.get_name()
1930
2139
  return {"wallet_id": wallet_id, "name": name}
1931
2140
 
1932
- async def get_stray_cats(self, request: Dict[str, Any]) -> EndpointResult:
2141
+ async def get_stray_cats(self, request: dict[str, Any]) -> EndpointResult:
1933
2142
  """
1934
2143
  Get a list of all unacknowledged CATs
1935
2144
  :param request: RPC request
@@ -1941,9 +2150,9 @@ class WalletRpcApi:
1941
2150
  @tx_endpoint(push=True)
1942
2151
  async def cat_spend(
1943
2152
  self,
1944
- request: Dict[str, Any],
2153
+ request: dict[str, Any],
1945
2154
  action_scope: WalletActionScope,
1946
- extra_conditions: Tuple[Condition, ...] = tuple(),
2155
+ extra_conditions: tuple[Condition, ...] = tuple(),
1947
2156
  hold_lock: bool = True,
1948
2157
  ) -> EndpointResult:
1949
2158
  if await self.service.wallet_state_manager.synced() is False:
@@ -1951,10 +2160,10 @@ class WalletRpcApi:
1951
2160
  wallet_id = uint32(request["wallet_id"])
1952
2161
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=CATWallet)
1953
2162
 
1954
- amounts: List[uint64] = []
1955
- puzzle_hashes: List[bytes32] = []
1956
- memos: List[List[bytes]] = []
1957
- additions: Optional[List[Dict[str, Any]]] = request.get("additions")
2163
+ amounts: list[uint64] = []
2164
+ puzzle_hashes: list[bytes32] = []
2165
+ memos: list[list[bytes]] = []
2166
+ additions: Optional[list[dict[str, Any]]] = request.get("additions")
1958
2167
  if not isinstance(request["fee"], int) or (additions is None and not isinstance(request["amount"], int)):
1959
2168
  raise ValueError("An integer amount or fee is required (too many decimals)")
1960
2169
  if additions is not None:
@@ -1974,17 +2183,17 @@ class WalletRpcApi:
1974
2183
  puzzle_hashes.append(decode_puzzle_hash(request["inner_address"]))
1975
2184
  if "memos" in request:
1976
2185
  memos.append([mem.encode("utf-8") for mem in request["memos"]])
1977
- coins: Optional[Set[Coin]] = None
2186
+ coins: Optional[set[Coin]] = None
1978
2187
  if "coins" in request and len(request["coins"]) > 0:
1979
2188
  coins = {Coin.from_json_dict(coin_json) for coin_json in request["coins"]}
1980
2189
  fee: uint64 = uint64(request.get("fee", 0))
1981
2190
 
1982
- cat_discrepancy_params: Tuple[Optional[int], Optional[str], Optional[str]] = (
2191
+ cat_discrepancy_params: tuple[Optional[int], Optional[str], Optional[str]] = (
1983
2192
  request.get("extra_delta", None),
1984
2193
  request.get("tail_reveal", None),
1985
2194
  request.get("tail_solution", None),
1986
2195
  )
1987
- cat_discrepancy: Optional[Tuple[int, Program, Program]] = None
2196
+ cat_discrepancy: Optional[tuple[int, Program, Program]] = None
1988
2197
  if cat_discrepancy_params != (None, None, None):
1989
2198
  if None in cat_discrepancy_params:
1990
2199
  raise ValueError("Specifying extra_delta, tail_reveal, or tail_solution requires specifying the others")
@@ -2027,13 +2236,13 @@ class WalletRpcApi:
2027
2236
  "transaction_id": None, # tx_endpoint wrapper will take care of this
2028
2237
  }
2029
2238
 
2030
- async def cat_get_asset_id(self, request: Dict[str, Any]) -> EndpointResult:
2239
+ async def cat_get_asset_id(self, request: dict[str, Any]) -> EndpointResult:
2031
2240
  wallet_id = uint32(request["wallet_id"])
2032
2241
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=CATWallet)
2033
2242
  asset_id: str = wallet.get_asset_id()
2034
2243
  return {"asset_id": asset_id, "wallet_id": wallet_id}
2035
2244
 
2036
- async def cat_asset_id_to_name(self, request: Dict[str, Any]) -> EndpointResult:
2245
+ async def cat_asset_id_to_name(self, request: dict[str, Any]) -> EndpointResult:
2037
2246
  wallet = await self.service.wallet_state_manager.get_wallet_for_asset_id(request["asset_id"])
2038
2247
  if wallet is None:
2039
2248
  if request["asset_id"] in DEFAULT_CATS:
@@ -2046,17 +2255,17 @@ class WalletRpcApi:
2046
2255
  @tx_endpoint(push=False)
2047
2256
  async def create_offer_for_ids(
2048
2257
  self,
2049
- request: Dict[str, Any],
2258
+ request: dict[str, Any],
2050
2259
  action_scope: WalletActionScope,
2051
- extra_conditions: Tuple[Condition, ...] = tuple(),
2260
+ extra_conditions: tuple[Condition, ...] = tuple(),
2052
2261
  ) -> EndpointResult:
2053
2262
  if action_scope.config.push:
2054
2263
  raise ValueError("Cannot push an incomplete spend") # pragma: no cover
2055
2264
 
2056
- offer: Dict[str, int] = request["offer"]
2265
+ offer: dict[str, int] = request["offer"]
2057
2266
  fee: uint64 = uint64(request.get("fee", 0))
2058
2267
  validate_only: bool = request.get("validate_only", False)
2059
- driver_dict_str: Optional[Dict[str, Any]] = request.get("driver_dict", None)
2268
+ driver_dict_str: Optional[dict[str, Any]] = request.get("driver_dict", None)
2060
2269
  marshalled_solver = request.get("solver")
2061
2270
  solver: Optional[Solver]
2062
2271
  if marshalled_solver is None:
@@ -2065,7 +2274,7 @@ class WalletRpcApi:
2065
2274
  solver = Solver(info=marshalled_solver)
2066
2275
 
2067
2276
  # This driver_dict construction is to maintain backward compatibility where everything is assumed to be a CAT
2068
- driver_dict: Dict[bytes32, PuzzleInfo] = {}
2277
+ driver_dict: dict[bytes32, PuzzleInfo] = {}
2069
2278
  if driver_dict_str is None:
2070
2279
  for key, amount in offer.items():
2071
2280
  if amount > 0:
@@ -2079,12 +2288,12 @@ class WalletRpcApi:
2079
2288
  for key, value in driver_dict_str.items():
2080
2289
  driver_dict[bytes32.from_hexstr(key)] = PuzzleInfo(value)
2081
2290
 
2082
- modified_offer: Dict[Union[int, bytes32], int] = {}
2083
- for key in offer:
2291
+ modified_offer: dict[Union[int, bytes32], int] = {}
2292
+ for wallet_identifier, change in offer.items():
2084
2293
  try:
2085
- modified_offer[bytes32.from_hexstr(key)] = offer[key]
2294
+ modified_offer[bytes32.from_hexstr(wallet_identifier)] = change
2086
2295
  except ValueError:
2087
- modified_offer[int(key)] = offer[key]
2296
+ modified_offer[int(wallet_identifier)] = change
2088
2297
 
2089
2298
  async with self.service.wallet_state_manager.lock:
2090
2299
  result = await self.service.wallet_state_manager.trade_manager.create_offer_for_ids(
@@ -2097,7 +2306,7 @@ class WalletRpcApi:
2097
2306
  extra_conditions=extra_conditions,
2098
2307
  )
2099
2308
  if result[0]:
2100
- success, trade_record, error = result
2309
+ _success, trade_record, _error = result
2101
2310
  return {
2102
2311
  "offer": Offer.from_bytes(trade_record.offer).to_bech32(),
2103
2312
  "trade_record": trade_record.to_json_dict_convenience(),
@@ -2105,7 +2314,7 @@ class WalletRpcApi:
2105
2314
  }
2106
2315
  raise ValueError(result[2])
2107
2316
 
2108
- async def get_offer_summary(self, request: Dict[str, Any]) -> EndpointResult:
2317
+ async def get_offer_summary(self, request: dict[str, Any]) -> EndpointResult:
2109
2318
  offer_hex: str = request["offer"]
2110
2319
 
2111
2320
  ###
@@ -2114,7 +2323,7 @@ class WalletRpcApi:
2114
2323
  from chia.util.bech32m import bech32_decode, convertbits
2115
2324
  from chia.wallet.util.puzzle_compression import OFFER_MOD_OLD, decompress_object_with_puzzles
2116
2325
 
2117
- hrpgot, data = bech32_decode(offer_hex, max_length=len(offer_hex))
2326
+ _hrpgot, data = bech32_decode(offer_hex, max_length=len(offer_hex))
2118
2327
  if data is None:
2119
2328
  raise ValueError("Invalid Offer")
2120
2329
  decoded = convertbits(list(data), 5, 8, False)
@@ -2143,12 +2352,12 @@ class WalletRpcApi:
2143
2352
  k: v
2144
2353
  for k, v in valid_times.to_json_dict().items()
2145
2354
  if k
2146
- not in (
2355
+ not in {
2147
2356
  "max_secs_after_created",
2148
2357
  "min_secs_since_created",
2149
2358
  "max_blocks_after_created",
2150
2359
  "min_blocks_since_created",
2151
- )
2360
+ }
2152
2361
  },
2153
2362
  },
2154
2363
  "id": offer.name(),
@@ -2184,7 +2393,7 @@ class WalletRpcApi:
2184
2393
  },
2185
2394
  }
2186
2395
 
2187
- async def check_offer_validity(self, request: Dict[str, Any]) -> EndpointResult:
2396
+ async def check_offer_validity(self, request: dict[str, Any]) -> EndpointResult:
2188
2397
  offer_hex: str = request["offer"]
2189
2398
 
2190
2399
  ###
@@ -2193,7 +2402,7 @@ class WalletRpcApi:
2193
2402
  from chia.util.bech32m import bech32_decode, convertbits
2194
2403
  from chia.wallet.util.puzzle_compression import OFFER_MOD_OLD, decompress_object_with_puzzles
2195
2404
 
2196
- hrpgot, data = bech32_decode(offer_hex, max_length=len(offer_hex))
2405
+ _hrpgot, data = bech32_decode(offer_hex, max_length=len(offer_hex))
2197
2406
  if data is None:
2198
2407
  raise ValueError("Invalid Offer") # pragma: no cover
2199
2408
  decoded = convertbits(list(data), 5, 8, False)
@@ -2216,9 +2425,9 @@ class WalletRpcApi:
2216
2425
  @tx_endpoint(push=True)
2217
2426
  async def take_offer(
2218
2427
  self,
2219
- request: Dict[str, Any],
2428
+ request: dict[str, Any],
2220
2429
  action_scope: WalletActionScope,
2221
- extra_conditions: Tuple[Condition, ...] = tuple(),
2430
+ extra_conditions: tuple[Condition, ...] = tuple(),
2222
2431
  ) -> EndpointResult:
2223
2432
  offer_hex: str = request["offer"]
2224
2433
 
@@ -2228,7 +2437,7 @@ class WalletRpcApi:
2228
2437
  from chia.util.bech32m import bech32_decode, convertbits
2229
2438
  from chia.wallet.util.puzzle_compression import OFFER_MOD_OLD, decompress_object_with_puzzles
2230
2439
 
2231
- hrpgot, data = bech32_decode(offer_hex, max_length=len(offer_hex))
2440
+ _hrpgot, data = bech32_decode(offer_hex, max_length=len(offer_hex))
2232
2441
  if data is None:
2233
2442
  raise ValueError("Invalid Offer") # pragma: no cover
2234
2443
  decoded = convertbits(list(data), 5, 8, False)
@@ -2243,7 +2452,7 @@ class WalletRpcApi:
2243
2452
 
2244
2453
  offer = Offer.from_bech32(offer_hex)
2245
2454
  fee: uint64 = uint64(request.get("fee", 0))
2246
- maybe_marshalled_solver: Optional[Dict[str, Any]] = request.get("solver")
2455
+ maybe_marshalled_solver: Optional[dict[str, Any]] = request.get("solver")
2247
2456
  solver: Optional[Solver]
2248
2457
  if maybe_marshalled_solver is None:
2249
2458
  solver = None
@@ -2272,7 +2481,7 @@ class WalletRpcApi:
2272
2481
  "signing_responses": None, # tx_endpoint wrapper will take care of this
2273
2482
  }
2274
2483
 
2275
- async def get_offer(self, request: Dict[str, Any]) -> EndpointResult:
2484
+ async def get_offer(self, request: dict[str, Any]) -> EndpointResult:
2276
2485
  trade_mgr = self.service.wallet_state_manager.trade_manager
2277
2486
 
2278
2487
  trade_id = bytes32.from_hexstr(request["trade_id"])
@@ -2285,7 +2494,7 @@ class WalletRpcApi:
2285
2494
  offer_value: Optional[str] = Offer.from_bytes(offer_to_return).to_bech32() if file_contents else None
2286
2495
  return {"trade_record": trade_record.to_json_dict_convenience(), "offer": offer_value}
2287
2496
 
2288
- async def get_all_offers(self, request: Dict[str, Any]) -> EndpointResult:
2497
+ async def get_all_offers(self, request: dict[str, Any]) -> EndpointResult:
2289
2498
  trade_mgr = self.service.wallet_state_manager.trade_manager
2290
2499
 
2291
2500
  start: int = request.get("start", 0)
@@ -2307,7 +2516,7 @@ class WalletRpcApi:
2307
2516
  include_completed=include_completed,
2308
2517
  )
2309
2518
  result = []
2310
- offer_values: Optional[List[str]] = [] if file_contents else None
2519
+ offer_values: Optional[list[str]] = [] if file_contents else None
2311
2520
  for trade in all_trades:
2312
2521
  result.append(trade.to_json_dict_convenience())
2313
2522
  if file_contents and offer_values is not None:
@@ -2316,7 +2525,7 @@ class WalletRpcApi:
2316
2525
 
2317
2526
  return {"trade_records": result, "offers": offer_values}
2318
2527
 
2319
- async def get_offers_count(self, request: Dict[str, Any]) -> EndpointResult:
2528
+ async def get_offers_count(self, request: dict[str, Any]) -> EndpointResult:
2320
2529
  trade_mgr = self.service.wallet_state_manager.trade_manager
2321
2530
 
2322
2531
  (total, my_offers_count, taken_offers_count) = await trade_mgr.trade_store.get_trades_count()
@@ -2326,9 +2535,9 @@ class WalletRpcApi:
2326
2535
  @tx_endpoint(push=True)
2327
2536
  async def cancel_offer(
2328
2537
  self,
2329
- request: Dict[str, Any],
2538
+ request: dict[str, Any],
2330
2539
  action_scope: WalletActionScope,
2331
- extra_conditions: Tuple[Condition, ...] = tuple(),
2540
+ extra_conditions: tuple[Condition, ...] = tuple(),
2332
2541
  ) -> EndpointResult:
2333
2542
  wsm = self.service.wallet_state_manager
2334
2543
  secure = request["secure"]
@@ -2336,7 +2545,7 @@ class WalletRpcApi:
2336
2545
  fee: uint64 = uint64(request.get("fee", 0))
2337
2546
  async with self.service.wallet_state_manager.lock:
2338
2547
  await wsm.trade_manager.cancel_pending_offers(
2339
- [bytes32(trade_id)], action_scope, fee=fee, secure=secure, extra_conditions=extra_conditions
2548
+ [trade_id], action_scope, fee=fee, secure=secure, extra_conditions=extra_conditions
2340
2549
  )
2341
2550
 
2342
2551
  return {"transactions": None} # tx_endpoint wrapper will take care of this
@@ -2344,9 +2553,9 @@ class WalletRpcApi:
2344
2553
  @tx_endpoint(push=True, merge_spends=False)
2345
2554
  async def cancel_offers(
2346
2555
  self,
2347
- request: Dict[str, Any],
2556
+ request: dict[str, Any],
2348
2557
  action_scope: WalletActionScope,
2349
- extra_conditions: Tuple[Condition, ...] = tuple(),
2558
+ extra_conditions: tuple[Condition, ...] = tuple(),
2350
2559
  ) -> EndpointResult:
2351
2560
  secure = request["secure"]
2352
2561
  batch_fee: uint64 = uint64(request.get("batch_fee", 0))
@@ -2366,7 +2575,7 @@ class WalletRpcApi:
2366
2575
  if asset_id is not None and asset_id != "xch":
2367
2576
  key = bytes32.from_hexstr(asset_id)
2368
2577
  while True:
2369
- records: Dict[bytes32, TradeRecord] = {}
2578
+ records: dict[bytes32, TradeRecord] = {}
2370
2579
  trades = await trade_mgr.trade_store.get_trades_between(
2371
2580
  start,
2372
2581
  end,
@@ -2411,13 +2620,13 @@ class WalletRpcApi:
2411
2620
  # Distributed Identities
2412
2621
  ##########################################################################################
2413
2622
 
2414
- async def did_set_wallet_name(self, request: Dict[str, Any]) -> EndpointResult:
2623
+ async def did_set_wallet_name(self, request: dict[str, Any]) -> EndpointResult:
2415
2624
  wallet_id = uint32(request["wallet_id"])
2416
2625
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2417
2626
  await wallet.set_name(str(request["name"]))
2418
2627
  return {"success": True, "wallet_id": wallet_id}
2419
2628
 
2420
- async def did_get_wallet_name(self, request: Dict[str, Any]) -> EndpointResult:
2629
+ async def did_get_wallet_name(self, request: dict[str, Any]) -> EndpointResult:
2421
2630
  wallet_id = uint32(request["wallet_id"])
2422
2631
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2423
2632
  name: str = wallet.get_name() # type: ignore[no-untyped-call] # Missing hint in `did_wallet.py`
@@ -2426,9 +2635,9 @@ class WalletRpcApi:
2426
2635
  @tx_endpoint(push=True)
2427
2636
  async def did_update_recovery_ids(
2428
2637
  self,
2429
- request: Dict[str, Any],
2638
+ request: dict[str, Any],
2430
2639
  action_scope: WalletActionScope,
2431
- extra_conditions: Tuple[Condition, ...] = tuple(),
2640
+ extra_conditions: tuple[Condition, ...] = tuple(),
2432
2641
  ) -> EndpointResult:
2433
2642
  wallet_id = uint32(request["wallet_id"])
2434
2643
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
@@ -2456,9 +2665,9 @@ class WalletRpcApi:
2456
2665
  @tx_endpoint(push=False)
2457
2666
  async def did_message_spend(
2458
2667
  self,
2459
- request: Dict[str, Any],
2668
+ request: dict[str, Any],
2460
2669
  action_scope: WalletActionScope,
2461
- extra_conditions: Tuple[Condition, ...] = tuple(),
2670
+ extra_conditions: tuple[Condition, ...] = tuple(),
2462
2671
  ) -> EndpointResult:
2463
2672
  wallet_id = uint32(request["wallet_id"])
2464
2673
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
@@ -2477,7 +2686,7 @@ class WalletRpcApi:
2477
2686
  "transactions": None, # tx_endpoint wrapper will take care of this
2478
2687
  }
2479
2688
 
2480
- async def did_get_info(self, request: Dict[str, Any]) -> EndpointResult:
2689
+ async def did_get_info(self, request: dict[str, Any]) -> EndpointResult:
2481
2690
  if "coin_id" not in request:
2482
2691
  return {"success": False, "error": "Coin ID is required."}
2483
2692
  coin_id = request["coin_id"]
@@ -2517,7 +2726,7 @@ class WalletRpcApi:
2517
2726
  "hints": hints,
2518
2727
  }
2519
2728
 
2520
- async def did_find_lost_did(self, request: Dict[str, Any]) -> EndpointResult:
2729
+ async def did_find_lost_did(self, request: dict[str, Any]) -> EndpointResult:
2521
2730
  """
2522
2731
  Recover a missing or unspendable DID wallet by a coin id of the DID
2523
2732
  :param coin_id: It can be DID ID, launcher coin ID or any coin ID of the DID you want to find.
@@ -2556,21 +2765,22 @@ class WalletRpcApi:
2556
2765
  if hinted_coin.coin.amount % 2 == 1 and hinted_coin.hint is not None:
2557
2766
  hint = hinted_coin.hint
2558
2767
  break
2559
- if hint is None:
2768
+ derivation_record = None
2769
+ if hint is not None:
2770
+ derivation_record = (
2771
+ await self.service.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(hint)
2772
+ )
2773
+ if derivation_record is None:
2560
2774
  # This is an invalid DID, check if we are owner
2561
2775
  derivation_record = (
2562
2776
  await self.service.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(
2563
2777
  p2_puzzle.get_tree_hash()
2564
2778
  )
2565
2779
  )
2566
- else:
2567
- derivation_record = (
2568
- await self.service.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(hint)
2569
- )
2570
2780
 
2571
2781
  launcher_id = bytes32(singleton_struct.rest().first().as_atom())
2572
2782
  if derivation_record is None:
2573
- return {"success": False, "error": f"This DID {launcher_id} is not belong to the connected wallet"}
2783
+ return {"success": False, "error": f"This DID {launcher_id} does not belong to the connected wallet"}
2574
2784
  else:
2575
2785
  our_inner_puzzle: Program = self.service.wallet_state_manager.main_wallet.puzzle_for_pk(
2576
2786
  derivation_record.pubkey
@@ -2650,7 +2860,7 @@ class WalletRpcApi:
2650
2860
 
2651
2861
  if did_wallet is None:
2652
2862
  # Create DID wallet
2653
- response: List[CoinState] = await self.service.get_coin_state([launcher_id], peer=peer)
2863
+ response: list[CoinState] = await self.service.get_coin_state([launcher_id], peer=peer)
2654
2864
  if len(response) == 0:
2655
2865
  return {"success": False, "error": f"Could not find the launch coin with ID: {launcher_id}"}
2656
2866
  launcher_coin: CoinState = response[0]
@@ -2668,7 +2878,7 @@ class WalletRpcApi:
2668
2878
  # Inner DID puzzle doesn't match, we need to update the DID info
2669
2879
  full_solution: Program = Program.from_bytes(bytes(coin_spend.solution))
2670
2880
  inner_solution: Program = full_solution.rest().rest().first()
2671
- recovery_list: List[bytes32] = []
2881
+ recovery_list: list[bytes32] = []
2672
2882
  backup_required: int = num_verification.as_int()
2673
2883
  if recovery_list_hash != NIL_TREEHASH:
2674
2884
  try:
@@ -2718,13 +2928,13 @@ class WalletRpcApi:
2718
2928
  @tx_endpoint(push=True)
2719
2929
  async def did_update_metadata(
2720
2930
  self,
2721
- request: Dict[str, Any],
2931
+ request: dict[str, Any],
2722
2932
  action_scope: WalletActionScope,
2723
- extra_conditions: Tuple[Condition, ...] = tuple(),
2933
+ extra_conditions: tuple[Condition, ...] = tuple(),
2724
2934
  ) -> EndpointResult:
2725
2935
  wallet_id = uint32(request["wallet_id"])
2726
2936
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2727
- metadata: Dict[str, str] = {}
2937
+ metadata: dict[str, str] = {}
2728
2938
  if "metadata" in request and type(request["metadata"]) is dict:
2729
2939
  metadata = request["metadata"]
2730
2940
  async with self.service.wallet_state_manager.lock:
@@ -2743,7 +2953,7 @@ class WalletRpcApi:
2743
2953
  else:
2744
2954
  return {"success": False, "error": f"Couldn't update metadata with input: {metadata}"}
2745
2955
 
2746
- async def did_get_did(self, request: Dict[str, Any]) -> EndpointResult:
2956
+ async def did_get_did(self, request: dict[str, Any]) -> EndpointResult:
2747
2957
  wallet_id = uint32(request["wallet_id"])
2748
2958
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2749
2959
  my_did: str = encode_puzzle_hash(bytes32.fromhex(wallet.get_my_DID()), AddressType.DID.hrp(self.service.config))
@@ -2754,7 +2964,7 @@ class WalletRpcApi:
2754
2964
  except RuntimeError:
2755
2965
  return {"success": True, "wallet_id": wallet_id, "my_did": my_did}
2756
2966
 
2757
- async def did_get_recovery_list(self, request: Dict[str, Any]) -> EndpointResult:
2967
+ async def did_get_recovery_list(self, request: dict[str, Any]) -> EndpointResult:
2758
2968
  wallet_id = uint32(request["wallet_id"])
2759
2969
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2760
2970
  recovery_list = wallet.did_info.backup_ids
@@ -2768,7 +2978,7 @@ class WalletRpcApi:
2768
2978
  "num_required": wallet.did_info.num_of_backup_ids_needed,
2769
2979
  }
2770
2980
 
2771
- async def did_get_metadata(self, request: Dict[str, Any]) -> EndpointResult:
2981
+ async def did_get_metadata(self, request: dict[str, Any]) -> EndpointResult:
2772
2982
  wallet_id = uint32(request["wallet_id"])
2773
2983
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2774
2984
  metadata = json.loads(wallet.did_info.metadata)
@@ -2780,7 +2990,7 @@ class WalletRpcApi:
2780
2990
 
2781
2991
  # TODO: this needs a test
2782
2992
  # Don't need full @tx_endpoint decorator here, but "push" is still a valid option
2783
- async def did_recovery_spend(self, request: Dict[str, Any]) -> EndpointResult: # pragma: no cover
2993
+ async def did_recovery_spend(self, request: dict[str, Any]) -> EndpointResult: # pragma: no cover
2784
2994
  wallet_id = uint32(request["wallet_id"])
2785
2995
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2786
2996
  if len(request["attest_data"]) < wallet.did_info.num_of_backup_ids_needed:
@@ -2822,7 +3032,7 @@ class WalletRpcApi:
2822
3032
  "transactions": [tx.to_json_dict_convenience(self.service.config)],
2823
3033
  }
2824
3034
 
2825
- async def did_get_pubkey(self, request: Dict[str, Any]) -> EndpointResult:
3035
+ async def did_get_pubkey(self, request: dict[str, Any]) -> EndpointResult:
2826
3036
  wallet_id = uint32(request["wallet_id"])
2827
3037
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2828
3038
  pubkey = bytes((await wallet.wallet_state_manager.get_unused_derivation_record(wallet_id)).pubkey).hex()
@@ -2832,9 +3042,9 @@ class WalletRpcApi:
2832
3042
  @tx_endpoint(push=True)
2833
3043
  async def did_create_attest(
2834
3044
  self,
2835
- request: Dict[str, Any],
3045
+ request: dict[str, Any],
2836
3046
  action_scope: WalletActionScope,
2837
- extra_conditions: Tuple[Condition, ...] = tuple(),
3047
+ extra_conditions: tuple[Condition, ...] = tuple(),
2838
3048
  ) -> EndpointResult: # pragma: no cover
2839
3049
  wallet_id = uint32(request["wallet_id"])
2840
3050
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
@@ -2860,7 +3070,7 @@ class WalletRpcApi:
2860
3070
  else:
2861
3071
  return {"success": False}
2862
3072
 
2863
- async def did_get_information_needed_for_recovery(self, request: Dict[str, Any]) -> EndpointResult:
3073
+ async def did_get_information_needed_for_recovery(self, request: dict[str, Any]) -> EndpointResult:
2864
3074
  wallet_id = uint32(request["wallet_id"])
2865
3075
  did_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2866
3076
  my_did = encode_puzzle_hash(
@@ -2878,7 +3088,7 @@ class WalletRpcApi:
2878
3088
  "backup_dids": did_wallet.did_info.backup_ids,
2879
3089
  }
2880
3090
 
2881
- async def did_get_current_coin_info(self, request: Dict[str, Any]) -> EndpointResult:
3091
+ async def did_get_current_coin_info(self, request: dict[str, Any]) -> EndpointResult:
2882
3092
  wallet_id = uint32(request["wallet_id"])
2883
3093
  did_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2884
3094
  my_did = encode_puzzle_hash(
@@ -2897,7 +3107,7 @@ class WalletRpcApi:
2897
3107
  "did_amount": did_coin_threeple[2],
2898
3108
  }
2899
3109
 
2900
- async def did_create_backup_file(self, request: Dict[str, Any]) -> EndpointResult:
3110
+ async def did_create_backup_file(self, request: dict[str, Any]) -> EndpointResult:
2901
3111
  wallet_id = uint32(request["wallet_id"])
2902
3112
  did_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
2903
3113
  return {"wallet_id": wallet_id, "success": True, "backup_data": did_wallet.create_backup()}
@@ -2905,9 +3115,9 @@ class WalletRpcApi:
2905
3115
  @tx_endpoint(push=True)
2906
3116
  async def did_transfer_did(
2907
3117
  self,
2908
- request: Dict[str, Any],
3118
+ request: dict[str, Any],
2909
3119
  action_scope: WalletActionScope,
2910
- extra_conditions: Tuple[Condition, ...] = tuple(),
3120
+ extra_conditions: tuple[Condition, ...] = tuple(),
2911
3121
  ) -> EndpointResult:
2912
3122
  if await self.service.wallet_state_manager.synced() is False:
2913
3123
  raise ValueError("Wallet needs to be fully synced.")
@@ -2934,7 +3144,7 @@ class WalletRpcApi:
2934
3144
  # DAO Wallet
2935
3145
  ##########################################################################################
2936
3146
 
2937
- async def dao_adjust_filter_level(self, request: Dict[str, Any]) -> EndpointResult:
3147
+ async def dao_adjust_filter_level(self, request: dict[str, Any]) -> EndpointResult:
2938
3148
  wallet_id = uint32(request["wallet_id"])
2939
3149
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
2940
3150
  await dao_wallet.adjust_filter_level(uint64(request["filter_level"]))
@@ -2946,9 +3156,9 @@ class WalletRpcApi:
2946
3156
  @tx_endpoint(push=True)
2947
3157
  async def dao_add_funds_to_treasury(
2948
3158
  self,
2949
- request: Dict[str, Any],
3159
+ request: dict[str, Any],
2950
3160
  action_scope: WalletActionScope,
2951
- extra_conditions: Tuple[Condition, ...] = tuple(),
3161
+ extra_conditions: tuple[Condition, ...] = tuple(),
2952
3162
  ) -> EndpointResult:
2953
3163
  wallet_id = uint32(request["wallet_id"])
2954
3164
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
@@ -2956,7 +3166,7 @@ class WalletRpcApi:
2956
3166
  wallet_type = self.service.wallet_state_manager.wallets[funding_wallet_id].type()
2957
3167
  amount = request.get("amount")
2958
3168
  assert amount
2959
- if wallet_type not in [WalletType.STANDARD_WALLET, WalletType.CAT]: # pragma: no cover
3169
+ if wallet_type not in {WalletType.STANDARD_WALLET, WalletType.CAT}: # pragma: no cover
2960
3170
  raise ValueError(f"Cannot fund a treasury with assets from a {wallet_type.name} wallet")
2961
3171
  await dao_wallet.create_add_funds_to_treasury_spend(
2962
3172
  uint64(amount),
@@ -2972,7 +3182,7 @@ class WalletRpcApi:
2972
3182
  "transactions": None, # tx_endpoint wrapper will take care of this
2973
3183
  }
2974
3184
 
2975
- async def dao_get_treasury_balance(self, request: Dict[str, Any]) -> EndpointResult:
3185
+ async def dao_get_treasury_balance(self, request: dict[str, Any]) -> EndpointResult:
2976
3186
  wallet_id = uint32(request["wallet_id"])
2977
3187
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
2978
3188
  assert dao_wallet is not None
@@ -2986,14 +3196,14 @@ class WalletRpcApi:
2986
3196
  balances[asset_id.hex()] = balance
2987
3197
  return {"success": True, "balances": balances}
2988
3198
 
2989
- async def dao_get_treasury_id(self, request: Dict[str, Any]) -> EndpointResult:
3199
+ async def dao_get_treasury_id(self, request: dict[str, Any]) -> EndpointResult:
2990
3200
  wallet_id = uint32(request["wallet_id"])
2991
3201
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
2992
3202
  assert dao_wallet is not None
2993
3203
  treasury_id = dao_wallet.dao_info.treasury_id
2994
3204
  return {"treasury_id": treasury_id}
2995
3205
 
2996
- async def dao_get_rules(self, request: Dict[str, Any]) -> EndpointResult:
3206
+ async def dao_get_rules(self, request: dict[str, Any]) -> EndpointResult:
2997
3207
  wallet_id = uint32(request["wallet_id"])
2998
3208
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
2999
3209
  assert dao_wallet is not None
@@ -3003,9 +3213,9 @@ class WalletRpcApi:
3003
3213
  @tx_endpoint(push=True)
3004
3214
  async def dao_send_to_lockup(
3005
3215
  self,
3006
- request: Dict[str, Any],
3216
+ request: dict[str, Any],
3007
3217
  action_scope: WalletActionScope,
3008
- extra_conditions: Tuple[Condition, ...] = tuple(),
3218
+ extra_conditions: tuple[Condition, ...] = tuple(),
3009
3219
  ) -> EndpointResult:
3010
3220
  wallet_id = uint32(request["wallet_id"])
3011
3221
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
@@ -3027,7 +3237,7 @@ class WalletRpcApi:
3027
3237
  "transactions": None, # tx_endpoint wrapper will take care of this
3028
3238
  }
3029
3239
 
3030
- async def dao_get_proposals(self, request: Dict[str, Any]) -> EndpointResult:
3240
+ async def dao_get_proposals(self, request: dict[str, Any]) -> EndpointResult:
3031
3241
  wallet_id = uint32(request["wallet_id"])
3032
3242
  include_closed = request.get("include_closed", True)
3033
3243
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
@@ -3043,7 +3253,7 @@ class WalletRpcApi:
3043
3253
  "soft_close_length": dao_rules.soft_close_length,
3044
3254
  }
3045
3255
 
3046
- async def dao_get_proposal_state(self, request: Dict[str, Any]) -> EndpointResult:
3256
+ async def dao_get_proposal_state(self, request: dict[str, Any]) -> EndpointResult:
3047
3257
  wallet_id = uint32(request["wallet_id"])
3048
3258
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
3049
3259
  assert dao_wallet is not None
@@ -3053,9 +3263,9 @@ class WalletRpcApi:
3053
3263
  @tx_endpoint(push=True)
3054
3264
  async def dao_exit_lockup(
3055
3265
  self,
3056
- request: Dict[str, Any],
3266
+ request: dict[str, Any],
3057
3267
  action_scope: WalletActionScope,
3058
- extra_conditions: Tuple[Condition, ...] = tuple(),
3268
+ extra_conditions: tuple[Condition, ...] = tuple(),
3059
3269
  ) -> EndpointResult:
3060
3270
  wallet_id = uint32(request["wallet_id"])
3061
3271
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
@@ -3066,7 +3276,7 @@ class WalletRpcApi:
3066
3276
  assert dao_cat_wallet is not None
3067
3277
  if request["coins"]: # pragma: no cover
3068
3278
  coin_list = [Coin.from_json_dict(coin) for coin in request["coins"]]
3069
- coins: List[LockedCoinInfo] = []
3279
+ coins: list[LockedCoinInfo] = []
3070
3280
  for lci in dao_cat_wallet.dao_cat_info.locked_coins:
3071
3281
  if lci.coin in coin_list:
3072
3282
  coins.append(lci)
@@ -3094,19 +3304,19 @@ class WalletRpcApi:
3094
3304
  @tx_endpoint(push=True)
3095
3305
  async def dao_create_proposal(
3096
3306
  self,
3097
- request: Dict[str, Any],
3307
+ request: dict[str, Any],
3098
3308
  action_scope: WalletActionScope,
3099
- extra_conditions: Tuple[Condition, ...] = tuple(),
3309
+ extra_conditions: tuple[Condition, ...] = tuple(),
3100
3310
  ) -> EndpointResult:
3101
3311
  wallet_id = uint32(request["wallet_id"])
3102
3312
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
3103
3313
  assert dao_wallet is not None
3104
3314
 
3105
3315
  if request["proposal_type"] == "spend":
3106
- amounts: List[uint64] = []
3107
- puzzle_hashes: List[bytes32] = []
3108
- asset_types: List[Optional[bytes32]] = []
3109
- additions: Optional[List[Dict[str, Any]]] = request.get("additions")
3316
+ amounts: list[uint64] = []
3317
+ puzzle_hashes: list[bytes32] = []
3318
+ asset_types: list[Optional[bytes32]] = []
3319
+ additions: Optional[list[dict[str, Any]]] = request.get("additions")
3110
3320
  if additions is not None:
3111
3321
  for addition in additions:
3112
3322
  if "asset_id" in addition:
@@ -3183,7 +3393,7 @@ class WalletRpcApi:
3183
3393
  return {
3184
3394
  "success": True,
3185
3395
  # Semantics guarantee proposal_id here
3186
- "proposal_id": proposal_id, # pylint: disable=possibly-used-before-assignment
3396
+ "proposal_id": proposal_id,
3187
3397
  "tx_id": None, # tx_endpoint wrapper will take care of this
3188
3398
  "tx": None, # tx_endpoint wrapper will take care of this
3189
3399
  "transactions": None, # tx_endpoint wrapper will take care of this
@@ -3192,9 +3402,9 @@ class WalletRpcApi:
3192
3402
  @tx_endpoint(push=True)
3193
3403
  async def dao_vote_on_proposal(
3194
3404
  self,
3195
- request: Dict[str, Any],
3405
+ request: dict[str, Any],
3196
3406
  action_scope: WalletActionScope,
3197
- extra_conditions: Tuple[Condition, ...] = tuple(),
3407
+ extra_conditions: tuple[Condition, ...] = tuple(),
3198
3408
  ) -> EndpointResult:
3199
3409
  wallet_id = uint32(request["wallet_id"])
3200
3410
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
@@ -3218,7 +3428,7 @@ class WalletRpcApi:
3218
3428
  "transactions": None, # tx_endpoint wrapper will take care of this
3219
3429
  }
3220
3430
 
3221
- async def dao_parse_proposal(self, request: Dict[str, Any]) -> EndpointResult:
3431
+ async def dao_parse_proposal(self, request: dict[str, Any]) -> EndpointResult:
3222
3432
  wallet_id = uint32(request["wallet_id"])
3223
3433
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
3224
3434
  assert dao_wallet is not None
@@ -3230,9 +3440,9 @@ class WalletRpcApi:
3230
3440
  @tx_endpoint(push=True)
3231
3441
  async def dao_close_proposal(
3232
3442
  self,
3233
- request: Dict[str, Any],
3443
+ request: dict[str, Any],
3234
3444
  action_scope: WalletActionScope,
3235
- extra_conditions: Tuple[Condition, ...] = tuple(),
3445
+ extra_conditions: tuple[Condition, ...] = tuple(),
3236
3446
  ) -> EndpointResult:
3237
3447
  wallet_id = uint32(request["wallet_id"])
3238
3448
  dao_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DAOWallet)
@@ -3261,9 +3471,9 @@ class WalletRpcApi:
3261
3471
  @tx_endpoint(push=True)
3262
3472
  async def dao_free_coins_from_finished_proposals(
3263
3473
  self,
3264
- request: Dict[str, Any],
3474
+ request: dict[str, Any],
3265
3475
  action_scope: WalletActionScope,
3266
- extra_conditions: Tuple[Condition, ...] = tuple(),
3476
+ extra_conditions: tuple[Condition, ...] = tuple(),
3267
3477
  ) -> EndpointResult:
3268
3478
  wallet_id = uint32(request["wallet_id"])
3269
3479
  fee = uint64(request.get("fee", 0))
@@ -3288,9 +3498,9 @@ class WalletRpcApi:
3288
3498
  @tx_endpoint(push=True)
3289
3499
  async def nft_mint_nft(
3290
3500
  self,
3291
- request: Dict[str, Any],
3501
+ request: dict[str, Any],
3292
3502
  action_scope: WalletActionScope,
3293
- extra_conditions: Tuple[Condition, ...] = tuple(),
3503
+ extra_conditions: tuple[Condition, ...] = tuple(),
3294
3504
  ) -> EndpointResult:
3295
3505
  log.debug("Got minting RPC request: %s", request)
3296
3506
  wallet_id = uint32(request["wallet_id"])
@@ -3303,14 +3513,18 @@ class WalletRpcApi:
3303
3513
  if isinstance(royalty_address, str):
3304
3514
  royalty_puzhash = decode_puzzle_hash(royalty_address)
3305
3515
  elif royalty_address is None:
3306
- royalty_puzhash = await nft_wallet.standard_wallet.get_new_puzzlehash()
3516
+ royalty_puzhash = await nft_wallet.standard_wallet.get_puzzle_hash(
3517
+ new=not action_scope.config.tx_config.reuse_puzhash
3518
+ )
3307
3519
  else:
3308
3520
  royalty_puzhash = royalty_address
3309
3521
  target_address = request.get("target_address")
3310
3522
  if isinstance(target_address, str):
3311
3523
  target_puzhash = decode_puzzle_hash(target_address)
3312
3524
  elif target_address is None:
3313
- target_puzhash = await nft_wallet.standard_wallet.get_new_puzzlehash()
3525
+ target_puzhash = await nft_wallet.standard_wallet.get_puzzle_hash(
3526
+ new=not action_scope.config.tx_config.reuse_puzhash
3527
+ )
3314
3528
  else:
3315
3529
  target_puzhash = target_address
3316
3530
  if "uris" not in request:
@@ -3361,7 +3575,7 @@ class WalletRpcApi:
3361
3575
  "transactions": None, # tx_endpoint wrapper will take care of this
3362
3576
  }
3363
3577
 
3364
- async def nft_count_nfts(self, request: Dict[str, Any]) -> EndpointResult:
3578
+ async def nft_count_nfts(self, request: dict[str, Any]) -> EndpointResult:
3365
3579
  wallet_id = request.get("wallet_id", None)
3366
3580
  count = 0
3367
3581
  if wallet_id is not None:
@@ -3375,9 +3589,9 @@ class WalletRpcApi:
3375
3589
  count = await self.service.wallet_state_manager.nft_store.count()
3376
3590
  return {"wallet_id": wallet_id, "success": True, "count": count}
3377
3591
 
3378
- async def nft_get_nfts(self, request: Dict[str, Any]) -> EndpointResult:
3592
+ async def nft_get_nfts(self, request: dict[str, Any]) -> EndpointResult:
3379
3593
  wallet_id = request.get("wallet_id", None)
3380
- nfts: List[NFTCoinInfo] = []
3594
+ nfts: list[NFTCoinInfo] = []
3381
3595
  if wallet_id is not None:
3382
3596
  nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
3383
3597
  else:
@@ -3403,9 +3617,9 @@ class WalletRpcApi:
3403
3617
  @tx_endpoint(push=True)
3404
3618
  async def nft_set_nft_did(
3405
3619
  self,
3406
- request: Dict[str, Any],
3620
+ request: dict[str, Any],
3407
3621
  action_scope: WalletActionScope,
3408
- extra_conditions: Tuple[Condition, ...] = tuple(),
3622
+ extra_conditions: tuple[Condition, ...] = tuple(),
3409
3623
  ) -> EndpointResult:
3410
3624
  wallet_id = uint32(request["wallet_id"])
3411
3625
  nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
@@ -3436,9 +3650,9 @@ class WalletRpcApi:
3436
3650
  @tx_endpoint(push=True)
3437
3651
  async def nft_set_did_bulk(
3438
3652
  self,
3439
- request: Dict[str, Any],
3653
+ request: dict[str, Any],
3440
3654
  action_scope: WalletActionScope,
3441
- extra_conditions: Tuple[Condition, ...] = tuple(),
3655
+ extra_conditions: tuple[Condition, ...] = tuple(),
3442
3656
  ) -> EndpointResult:
3443
3657
  """
3444
3658
  Bulk set DID for NFTs across different wallets.
@@ -3454,7 +3668,7 @@ class WalletRpcApi:
3454
3668
  did_id = request.get("did_id", b"")
3455
3669
  if did_id != b"":
3456
3670
  did_id = decode_puzzle_hash(did_id)
3457
- nft_dict: Dict[uint32, List[NFTCoinInfo]] = {}
3671
+ nft_dict: dict[uint32, list[NFTCoinInfo]] = {}
3458
3672
  coin_ids = []
3459
3673
  nft_ids = []
3460
3674
  fee = uint64(request.get("fee", 0))
@@ -3514,9 +3728,9 @@ class WalletRpcApi:
3514
3728
  @tx_endpoint(push=True)
3515
3729
  async def nft_transfer_bulk(
3516
3730
  self,
3517
- request: Dict[str, Any],
3731
+ request: dict[str, Any],
3518
3732
  action_scope: WalletActionScope,
3519
- extra_conditions: Tuple[Condition, ...] = tuple(),
3733
+ extra_conditions: tuple[Condition, ...] = tuple(),
3520
3734
  ) -> EndpointResult:
3521
3735
  """
3522
3736
  Bulk transfer NFTs to an address.
@@ -3534,7 +3748,7 @@ class WalletRpcApi:
3534
3748
  puzzle_hash = decode_puzzle_hash(address)
3535
3749
  else:
3536
3750
  return dict(success=False, error="target_address parameter missing")
3537
- nft_dict: Dict[uint32, List[NFTCoinInfo]] = {}
3751
+ nft_dict: dict[uint32, list[NFTCoinInfo]] = {}
3538
3752
  coin_ids = []
3539
3753
  fee = uint64(request.get("fee", 0))
3540
3754
 
@@ -3585,7 +3799,7 @@ class WalletRpcApi:
3585
3799
  "transactions": None, # tx_endpoint wrapper will take care of this
3586
3800
  }
3587
3801
 
3588
- async def nft_get_by_did(self, request: Dict[str, Any]) -> EndpointResult:
3802
+ async def nft_get_by_did(self, request: dict[str, Any]) -> EndpointResult:
3589
3803
  did_id: Optional[bytes32] = None
3590
3804
  if request.get("did_id", None) is not None:
3591
3805
  did_id = decode_puzzle_hash(request["did_id"])
@@ -3594,7 +3808,7 @@ class WalletRpcApi:
3594
3808
  return {"wallet_id": wallet.wallet_id, "success": True}
3595
3809
  return {"error": f"Cannot find a NFT wallet DID = {did_id}", "success": False}
3596
3810
 
3597
- async def nft_get_wallet_did(self, request: Dict[str, Any]) -> EndpointResult:
3811
+ async def nft_get_wallet_did(self, request: dict[str, Any]) -> EndpointResult:
3598
3812
  wallet_id = uint32(request["wallet_id"])
3599
3813
  nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
3600
3814
  did_bytes: Optional[bytes32] = nft_wallet.get_did()
@@ -3603,9 +3817,9 @@ class WalletRpcApi:
3603
3817
  did_id = encode_puzzle_hash(did_bytes, AddressType.DID.hrp(self.service.config))
3604
3818
  return {"success": True, "did_id": None if len(did_id) == 0 else did_id}
3605
3819
 
3606
- async def nft_get_wallets_with_dids(self, request: Dict[str, Any]) -> EndpointResult:
3820
+ async def nft_get_wallets_with_dids(self, request: dict[str, Any]) -> EndpointResult:
3607
3821
  all_wallets = self.service.wallet_state_manager.wallets.values()
3608
- did_wallets_by_did_id: Dict[bytes32, uint32] = {}
3822
+ did_wallets_by_did_id: dict[bytes32, uint32] = {}
3609
3823
 
3610
3824
  for wallet in all_wallets:
3611
3825
  if wallet.type() == WalletType.DECENTRALIZED_ID:
@@ -3613,7 +3827,7 @@ class WalletRpcApi:
3613
3827
  if wallet.did_info.origin_coin is not None:
3614
3828
  did_wallets_by_did_id[wallet.did_info.origin_coin.name()] = wallet.id()
3615
3829
 
3616
- did_nft_wallets: List[Dict[str, Any]] = []
3830
+ did_nft_wallets: list[dict[str, Any]] = []
3617
3831
  for wallet in all_wallets:
3618
3832
  if isinstance(wallet, NFTWallet):
3619
3833
  nft_wallet_did: Optional[bytes32] = wallet.get_did()
@@ -3631,7 +3845,7 @@ class WalletRpcApi:
3631
3845
  )
3632
3846
  return {"success": True, "nft_wallets": did_nft_wallets}
3633
3847
 
3634
- async def nft_set_nft_status(self, request: Dict[str, Any]) -> EndpointResult:
3848
+ async def nft_set_nft_status(self, request: dict[str, Any]) -> EndpointResult:
3635
3849
  wallet_id: uint32 = uint32(request["wallet_id"])
3636
3850
  coin_id: bytes32 = bytes32.from_hexstr(request["coin_id"])
3637
3851
  status: bool = request["in_transaction"]
@@ -3643,9 +3857,9 @@ class WalletRpcApi:
3643
3857
  @tx_endpoint(push=True)
3644
3858
  async def nft_transfer_nft(
3645
3859
  self,
3646
- request: Dict[str, Any],
3860
+ request: dict[str, Any],
3647
3861
  action_scope: WalletActionScope,
3648
- extra_conditions: Tuple[Condition, ...] = tuple(),
3862
+ extra_conditions: tuple[Condition, ...] = tuple(),
3649
3863
  ) -> EndpointResult:
3650
3864
  wallet_id = uint32(request["wallet_id"])
3651
3865
  address = request["target_address"]
@@ -3686,7 +3900,7 @@ class WalletRpcApi:
3686
3900
  log.exception(f"Failed to transfer NFT: {e}")
3687
3901
  return {"success": False, "error": str(e)}
3688
3902
 
3689
- async def nft_get_info(self, request: Dict[str, Any]) -> EndpointResult:
3903
+ async def nft_get_info(self, request: dict[str, Any]) -> EndpointResult:
3690
3904
  if "coin_id" not in request:
3691
3905
  return {"success": False, "error": "Coin ID is required."}
3692
3906
  coin_id = request["coin_id"]
@@ -3726,7 +3940,7 @@ class WalletRpcApi:
3726
3940
  )
3727
3941
 
3728
3942
  # Get launcher coin
3729
- launcher_coin: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
3943
+ launcher_coin: list[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
3730
3944
  [uncurried_nft.singleton_launcher_id], peer=peer
3731
3945
  )
3732
3946
  if launcher_coin is None or len(launcher_coin) < 1 or launcher_coin[0].spent_height is None:
@@ -3755,9 +3969,9 @@ class WalletRpcApi:
3755
3969
  @tx_endpoint(push=True)
3756
3970
  async def nft_add_uri(
3757
3971
  self,
3758
- request: Dict[str, Any],
3972
+ request: dict[str, Any],
3759
3973
  action_scope: WalletActionScope,
3760
- extra_conditions: Tuple[Condition, ...] = tuple(),
3974
+ extra_conditions: tuple[Condition, ...] = tuple(),
3761
3975
  ) -> EndpointResult:
3762
3976
  wallet_id = uint32(request["wallet_id"])
3763
3977
  # Note metadata updater can only add one uri for one field per spend.
@@ -3783,7 +3997,7 @@ class WalletRpcApi:
3783
3997
  "transactions": None, # tx_endpoint wrapper will take care of this
3784
3998
  }
3785
3999
 
3786
- async def nft_calculate_royalties(self, request: Dict[str, Any]) -> EndpointResult:
4000
+ async def nft_calculate_royalties(self, request: dict[str, Any]) -> EndpointResult:
3787
4001
  return NFTWallet.royalty_calculation(
3788
4002
  {
3789
4003
  asset["asset"]: (asset["royalty_address"], uint16(asset["royalty_percentage"]))
@@ -3795,9 +4009,9 @@ class WalletRpcApi:
3795
4009
  @tx_endpoint(push=False)
3796
4010
  async def nft_mint_bulk(
3797
4011
  self,
3798
- request: Dict[str, Any],
4012
+ request: dict[str, Any],
3799
4013
  action_scope: WalletActionScope,
3800
- extra_conditions: Tuple[Condition, ...] = tuple(),
4014
+ extra_conditions: tuple[Condition, ...] = tuple(),
3801
4015
  ) -> EndpointResult:
3802
4016
  if action_scope.config.push:
3803
4017
  raise ValueError("Automatic pushing of nft minting transactions not yet available") # pragma: no cover
@@ -3808,7 +4022,7 @@ class WalletRpcApi:
3808
4022
  royalty_address = request.get("royalty_address", None)
3809
4023
  if isinstance(royalty_address, str) and royalty_address != "":
3810
4024
  royalty_puzhash = decode_puzzle_hash(royalty_address)
3811
- elif royalty_address in [None, ""]:
4025
+ elif royalty_address in {None, ""}:
3812
4026
  royalty_puzhash = await nft_wallet.standard_wallet.get_new_puzzlehash()
3813
4027
  else:
3814
4028
  royalty_puzhash = bytes32.from_hexstr(royalty_address)
@@ -3924,7 +4138,7 @@ class WalletRpcApi:
3924
4138
  "transactions": None, # tx_endpoint wrapper will take care of this
3925
4139
  }
3926
4140
 
3927
- async def get_coin_records(self, request: Dict[str, Any]) -> EndpointResult:
4141
+ async def get_coin_records(self, request: dict[str, Any]) -> EndpointResult:
3928
4142
  parsed_request = GetCoinRecords.from_json_dict(request)
3929
4143
 
3930
4144
  if parsed_request.limit != uint32.MAXIMUM and parsed_request.limit > self.max_get_coin_records_limit:
@@ -3966,8 +4180,8 @@ class WalletRpcApi:
3966
4180
  "total_count": result.total_count,
3967
4181
  }
3968
4182
 
3969
- async def get_farmed_amount(self, request: Dict[str, Any]) -> EndpointResult:
3970
- tx_records: List[TransactionRecord] = await self.service.wallet_state_manager.tx_store.get_farming_rewards()
4183
+ async def get_farmed_amount(self, request: dict[str, Any]) -> EndpointResult:
4184
+ tx_records: list[TransactionRecord] = await self.service.wallet_state_manager.tx_store.get_farming_rewards()
3971
4185
  amount = 0
3972
4186
  pool_reward_amount = 0
3973
4187
  farmer_reward_amount = 0
@@ -4013,9 +4227,9 @@ class WalletRpcApi:
4013
4227
  @tx_endpoint(push=False)
4014
4228
  async def create_signed_transaction(
4015
4229
  self,
4016
- request: Dict[str, Any],
4230
+ request: dict[str, Any],
4017
4231
  action_scope: WalletActionScope,
4018
- extra_conditions: Tuple[Condition, ...] = tuple(),
4232
+ extra_conditions: tuple[Condition, ...] = tuple(),
4019
4233
  hold_lock: bool = True,
4020
4234
  ) -> EndpointResult:
4021
4235
  if "wallet_id" in request:
@@ -4031,7 +4245,7 @@ class WalletRpcApi:
4031
4245
  if "additions" not in request or len(request["additions"]) < 1:
4032
4246
  raise ValueError("Specify additions list")
4033
4247
 
4034
- additions: List[Dict[str, Any]] = request["additions"]
4248
+ additions: list[dict[str, Any]] = request["additions"]
4035
4249
  amount_0: uint64 = uint64(additions[0]["amount"])
4036
4250
  assert amount_0 <= self.service.constants.MAX_COIN_AMOUNT
4037
4251
  puzzle_hash_0 = bytes32.from_hexstr(additions[0]["puzzle_hash"])
@@ -4040,7 +4254,7 @@ class WalletRpcApi:
4040
4254
 
4041
4255
  memos_0 = [] if "memos" not in additions[0] else [mem.encode("utf-8") for mem in additions[0]["memos"]]
4042
4256
 
4043
- additional_outputs: List[Payment] = []
4257
+ additional_outputs: list[Payment] = []
4044
4258
  for addition in additions[1:]:
4045
4259
  receiver_ph = bytes32.from_hexstr(addition["puzzle_hash"])
4046
4260
  if len(receiver_ph) != 32:
@@ -4147,9 +4361,9 @@ class WalletRpcApi:
4147
4361
  @tx_endpoint(push=True)
4148
4362
  async def pw_join_pool(
4149
4363
  self,
4150
- request: Dict[str, Any],
4364
+ request: dict[str, Any],
4151
4365
  action_scope: WalletActionScope,
4152
- extra_conditions: Tuple[Condition, ...] = tuple(),
4366
+ extra_conditions: tuple[Condition, ...] = tuple(),
4153
4367
  ) -> EndpointResult:
4154
4368
  fee = uint64(request.get("fee", 0))
4155
4369
  wallet_id = uint32(request["wallet_id"])
@@ -4185,9 +4399,9 @@ class WalletRpcApi:
4185
4399
  @tx_endpoint(push=True)
4186
4400
  async def pw_self_pool(
4187
4401
  self,
4188
- request: Dict[str, Any],
4402
+ request: dict[str, Any],
4189
4403
  action_scope: WalletActionScope,
4190
- extra_conditions: Tuple[Condition, ...] = tuple(),
4404
+ extra_conditions: tuple[Condition, ...] = tuple(),
4191
4405
  ) -> EndpointResult:
4192
4406
  # Leaving a pool requires two state transitions.
4193
4407
  # First we transition to PoolSingletonState.LEAVING_POOL
@@ -4211,9 +4425,9 @@ class WalletRpcApi:
4211
4425
  @tx_endpoint(push=True)
4212
4426
  async def pw_absorb_rewards(
4213
4427
  self,
4214
- request: Dict[str, Any],
4428
+ request: dict[str, Any],
4215
4429
  action_scope: WalletActionScope,
4216
- extra_conditions: Tuple[Condition, ...] = tuple(),
4430
+ extra_conditions: tuple[Condition, ...] = tuple(),
4217
4431
  ) -> EndpointResult:
4218
4432
  """Perform a sweep of the p2_singleton rewards controlled by the pool wallet singleton"""
4219
4433
  if await self.service.wallet_state_manager.synced() is False:
@@ -4234,14 +4448,14 @@ class WalletRpcApi:
4234
4448
  "transactions": None, # tx_endpoint wrapper will take care of this
4235
4449
  }
4236
4450
 
4237
- async def pw_status(self, request: Dict[str, Any]) -> EndpointResult:
4451
+ async def pw_status(self, request: dict[str, Any]) -> EndpointResult:
4238
4452
  """Return the complete state of the Pool wallet with id `request["wallet_id"]`"""
4239
4453
  wallet_id = uint32(request["wallet_id"])
4240
4454
  wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=PoolWallet)
4241
4455
 
4242
4456
  assert isinstance(wallet, PoolWallet)
4243
4457
  state: PoolWalletInfo = await wallet.get_current_state()
4244
- unconfirmed_transactions: List[TransactionRecord] = await wallet.get_unconfirmed_transactions()
4458
+ unconfirmed_transactions: list[TransactionRecord] = await wallet.get_unconfirmed_transactions()
4245
4459
  return {
4246
4460
  "state": state.to_json_dict(),
4247
4461
  "unconfirmed_transactions": unconfirmed_transactions,
@@ -4253,9 +4467,9 @@ class WalletRpcApi:
4253
4467
  @tx_endpoint(push=True)
4254
4468
  async def create_new_dl(
4255
4469
  self,
4256
- request: Dict[str, Any],
4470
+ request: dict[str, Any],
4257
4471
  action_scope: WalletActionScope,
4258
- extra_conditions: Tuple[Condition, ...] = tuple(),
4472
+ extra_conditions: tuple[Condition, ...] = tuple(),
4259
4473
  ) -> EndpointResult:
4260
4474
  """Initialize the DataLayer Wallet (only one can exist)"""
4261
4475
  if self.service.wallet_state_manager is None:
@@ -4284,7 +4498,7 @@ class WalletRpcApi:
4284
4498
  "launcher_id": launcher_id,
4285
4499
  }
4286
4500
 
4287
- async def dl_track_new(self, request: Dict[str, Any]) -> EndpointResult:
4501
+ async def dl_track_new(self, request: dict[str, Any]) -> EndpointResult:
4288
4502
  """Initialize the DataLayer Wallet (only one can exist)"""
4289
4503
  if self.service.wallet_state_manager is None:
4290
4504
  raise ValueError("The wallet service is not currently initialized")
@@ -4309,7 +4523,7 @@ class WalletRpcApi:
4309
4523
  continue # try some other peers, maybe someone has it
4310
4524
  return {}
4311
4525
 
4312
- async def dl_stop_tracking(self, request: Dict[str, Any]) -> EndpointResult:
4526
+ async def dl_stop_tracking(self, request: dict[str, Any]) -> EndpointResult:
4313
4527
  """Initialize the DataLayer Wallet (only one can exist)"""
4314
4528
  if self.service.wallet_state_manager is None:
4315
4529
  raise ValueError("The wallet service is not currently initialized")
@@ -4318,7 +4532,7 @@ class WalletRpcApi:
4318
4532
  await dl_wallet.stop_tracking_singleton(bytes32.from_hexstr(request["launcher_id"]))
4319
4533
  return {}
4320
4534
 
4321
- async def dl_latest_singleton(self, request: Dict[str, Any]) -> EndpointResult:
4535
+ async def dl_latest_singleton(self, request: dict[str, Any]) -> EndpointResult:
4322
4536
  """Get the singleton record for the latest singleton of a launcher ID"""
4323
4537
  if self.service.wallet_state_manager is None:
4324
4538
  raise ValueError("The wallet service is not currently initialized")
@@ -4330,7 +4544,7 @@ class WalletRpcApi:
4330
4544
  record = await wallet.get_latest_singleton(bytes32.from_hexstr(request["launcher_id"]), only_confirmed)
4331
4545
  return {"singleton": None if record is None else record.to_json_dict()}
4332
4546
 
4333
- async def dl_singletons_by_root(self, request: Dict[str, Any]) -> EndpointResult:
4547
+ async def dl_singletons_by_root(self, request: dict[str, Any]) -> EndpointResult:
4334
4548
  """Get the singleton records that contain the specified root"""
4335
4549
  if self.service.wallet_state_manager is None:
4336
4550
  raise ValueError("The wallet service is not currently initialized")
@@ -4345,9 +4559,9 @@ class WalletRpcApi:
4345
4559
  @tx_endpoint(push=True)
4346
4560
  async def dl_update_root(
4347
4561
  self,
4348
- request: Dict[str, Any],
4562
+ request: dict[str, Any],
4349
4563
  action_scope: WalletActionScope,
4350
- extra_conditions: Tuple[Condition, ...] = tuple(),
4564
+ extra_conditions: tuple[Condition, ...] = tuple(),
4351
4565
  ) -> EndpointResult:
4352
4566
  """Get the singleton record for the latest singleton of a launcher ID"""
4353
4567
  if self.service.wallet_state_manager is None:
@@ -4371,9 +4585,9 @@ class WalletRpcApi:
4371
4585
  @tx_endpoint(push=True)
4372
4586
  async def dl_update_multiple(
4373
4587
  self,
4374
- request: Dict[str, Any],
4588
+ request: dict[str, Any],
4375
4589
  action_scope: WalletActionScope,
4376
- extra_conditions: Tuple[Condition, ...] = tuple(),
4590
+ extra_conditions: tuple[Condition, ...] = tuple(),
4377
4591
  ) -> EndpointResult:
4378
4592
  """Update multiple singletons with new merkle roots"""
4379
4593
  if self.service.wallet_state_manager is None:
@@ -4397,7 +4611,7 @@ class WalletRpcApi:
4397
4611
  "transactions": None, # tx_endpoint wrapper will take care of this
4398
4612
  }
4399
4613
 
4400
- async def dl_history(self, request: Dict[str, Any]) -> EndpointResult:
4614
+ async def dl_history(self, request: dict[str, Any]) -> EndpointResult:
4401
4615
  """Get the singleton record for the latest singleton of a launcher ID"""
4402
4616
  if self.service.wallet_state_manager is None:
4403
4617
  raise ValueError("The wallet service is not currently initialized")
@@ -4416,7 +4630,7 @@ class WalletRpcApi:
4416
4630
  history_json = [rec.to_json_dict() for rec in history]
4417
4631
  return {"history": history_json, "count": len(history_json)}
4418
4632
 
4419
- async def dl_owned_singletons(self, request: Dict[str, Any]) -> EndpointResult:
4633
+ async def dl_owned_singletons(self, request: dict[str, Any]) -> EndpointResult:
4420
4634
  """Get all owned singleton records"""
4421
4635
  if self.service.wallet_state_manager is None:
4422
4636
  raise ValueError("The wallet service is not currently initialized")
@@ -4431,7 +4645,7 @@ class WalletRpcApi:
4431
4645
 
4432
4646
  return {"singletons": singletons_json, "count": len(singletons_json)}
4433
4647
 
4434
- async def dl_get_mirrors(self, request: Dict[str, Any]) -> EndpointResult:
4648
+ async def dl_get_mirrors(self, request: dict[str, Any]) -> EndpointResult:
4435
4649
  """Get all of the mirrors for a specific singleton"""
4436
4650
  if self.service.wallet_state_manager is None:
4437
4651
  raise ValueError("The wallet service is not currently initialized")
@@ -4446,9 +4660,9 @@ class WalletRpcApi:
4446
4660
  @tx_endpoint(push=True)
4447
4661
  async def dl_new_mirror(
4448
4662
  self,
4449
- request: Dict[str, Any],
4663
+ request: dict[str, Any],
4450
4664
  action_scope: WalletActionScope,
4451
- extra_conditions: Tuple[Condition, ...] = tuple(),
4665
+ extra_conditions: tuple[Condition, ...] = tuple(),
4452
4666
  ) -> EndpointResult:
4453
4667
  """Add a new on chain message for a specific singleton"""
4454
4668
  if self.service.wallet_state_manager is None:
@@ -4472,9 +4686,9 @@ class WalletRpcApi:
4472
4686
  @tx_endpoint(push=True)
4473
4687
  async def dl_delete_mirror(
4474
4688
  self,
4475
- request: Dict[str, Any],
4689
+ request: dict[str, Any],
4476
4690
  action_scope: WalletActionScope,
4477
- extra_conditions: Tuple[Condition, ...] = tuple(),
4691
+ extra_conditions: tuple[Condition, ...] = tuple(),
4478
4692
  ) -> EndpointResult:
4479
4693
  """Remove an existing mirror for a specific singleton"""
4480
4694
  if self.service.wallet_state_manager is None:
@@ -4497,7 +4711,7 @@ class WalletRpcApi:
4497
4711
 
4498
4712
  async def dl_verify_proof(
4499
4713
  self,
4500
- request: Dict[str, Any],
4714
+ request: dict[str, Any],
4501
4715
  ) -> EndpointResult:
4502
4716
  """Verify a proof of inclusion for a DL singleton"""
4503
4717
  res = await dl_verify_proof(
@@ -4512,12 +4726,13 @@ class WalletRpcApi:
4512
4726
  # Verified Credential
4513
4727
  ##########################################################################################
4514
4728
  @tx_endpoint(push=True)
4729
+ @marshal
4515
4730
  async def vc_mint(
4516
4731
  self,
4517
- request: Dict[str, Any],
4732
+ request: VCMint,
4518
4733
  action_scope: WalletActionScope,
4519
- extra_conditions: Tuple[Condition, ...] = tuple(),
4520
- ) -> EndpointResult:
4734
+ extra_conditions: tuple[Condition, ...] = tuple(),
4735
+ ) -> VCMintResponse:
4521
4736
  """
4522
4737
  Mint a verified credential using the assigned DID
4523
4738
  :param request: We require 'did_id' that will be minting the VC and options for a new 'target_address' as well
@@ -4525,84 +4740,58 @@ class WalletRpcApi:
4525
4740
  :return: a 'vc_record' containing all the information of the soon-to-be-confirmed vc as well as any relevant
4526
4741
  'transactions'
4527
4742
  """
4528
-
4529
- @streamable
4530
- @dataclasses.dataclass(frozen=True)
4531
- class VCMint(Streamable):
4532
- did_id: str
4533
- target_address: Optional[str] = None
4534
- fee: uint64 = uint64(0)
4535
-
4536
- parsed_request = VCMint.from_json_dict(request)
4537
-
4538
- did_id = decode_puzzle_hash(parsed_request.did_id)
4743
+ did_id = decode_puzzle_hash(request.did_id)
4539
4744
  puzhash: Optional[bytes32] = None
4540
- if parsed_request.target_address is not None:
4541
- puzhash = decode_puzzle_hash(parsed_request.target_address)
4745
+ if request.target_address is not None:
4746
+ puzhash = decode_puzzle_hash(request.target_address)
4542
4747
 
4543
4748
  vc_wallet: VCWallet = await self.service.wallet_state_manager.get_or_create_vc_wallet()
4544
4749
  vc_record = await vc_wallet.launch_new_vc(
4545
- did_id, action_scope, puzhash, parsed_request.fee, extra_conditions=extra_conditions
4750
+ did_id, action_scope, puzhash, request.fee, extra_conditions=extra_conditions
4546
4751
  )
4547
- return {
4548
- "vc_record": vc_record.to_json_dict(),
4549
- "transactions": None, # tx_endpoint wrapper will take care of this
4550
- }
4752
+ return VCMintResponse([], [], vc_record)
4551
4753
 
4552
- async def vc_get(self, request: Dict[str, Any]) -> EndpointResult:
4754
+ @marshal
4755
+ async def vc_get(self, request: VCGet) -> VCGetResponse:
4553
4756
  """
4554
4757
  Given a launcher ID get the verified credential
4555
4758
  :param request: the 'vc_id' launcher id of a verifiable credential
4556
4759
  :return: the 'vc_record' representing the specified verifiable credential
4557
4760
  """
4761
+ vc_record = await self.service.wallet_state_manager.vc_store.get_vc_record(request.vc_id)
4762
+ return VCGetResponse(vc_record)
4558
4763
 
4559
- @streamable
4560
- @dataclasses.dataclass(frozen=True)
4561
- class VCGet(Streamable):
4562
- vc_id: bytes32
4563
-
4564
- parsed_request = VCGet.from_json_dict(request)
4565
-
4566
- vc_record = await self.service.wallet_state_manager.vc_store.get_vc_record(parsed_request.vc_id)
4567
- return {"vc_record": vc_record}
4568
-
4569
- async def vc_get_list(self, request: Dict[str, Any]) -> EndpointResult:
4764
+ @marshal
4765
+ async def vc_get_list(self, request: VCGetList) -> VCGetListResponse:
4570
4766
  """
4571
4767
  Get a list of verified credentials
4572
4768
  :param request: optional parameters for pagination 'start' and 'count'
4573
4769
  :return: all 'vc_records' in the specified range and any 'proofs' associated with the roots contained within
4574
4770
  """
4575
4771
 
4576
- @streamable
4577
- @dataclasses.dataclass(frozen=True)
4578
- class VCGetList(Streamable):
4579
- start: uint32 = uint32(0)
4580
- end: uint32 = uint32(50)
4581
-
4582
- parsed_request = VCGetList.from_json_dict(request)
4583
-
4584
- vc_list = await self.service.wallet_state_manager.vc_store.get_vc_record_list(
4585
- parsed_request.start, parsed_request.end
4586
- )
4587
- return {
4588
- "vc_records": [{"coin_id": "0x" + vc.vc.coin.name().hex(), **vc.to_json_dict()} for vc in vc_list],
4589
- "proofs": {
4590
- rec.vc.proof_hash.hex(): None if fetched_proof is None else fetched_proof.key_value_pairs
4772
+ vc_list = await self.service.wallet_state_manager.vc_store.get_vc_record_list(request.start, request.end)
4773
+ return VCGetListResponse(
4774
+ [VCRecordWithCoinID.from_vc_record(vc) for vc in vc_list],
4775
+ [
4776
+ VCProofWithHash(
4777
+ rec.vc.proof_hash, None if fetched_proof is None else VCProofsRPC.from_vc_proofs(fetched_proof)
4778
+ )
4591
4779
  for rec in vc_list
4592
4780
  if rec.vc.proof_hash is not None
4593
4781
  for fetched_proof in (
4594
4782
  await self.service.wallet_state_manager.vc_store.get_proofs_for_root(rec.vc.proof_hash),
4595
4783
  )
4596
- },
4597
- }
4784
+ ],
4785
+ )
4598
4786
 
4599
4787
  @tx_endpoint(push=True)
4788
+ @marshal
4600
4789
  async def vc_spend(
4601
4790
  self,
4602
- request: Dict[str, Any],
4791
+ request: VCSpend,
4603
4792
  action_scope: WalletActionScope,
4604
- extra_conditions: Tuple[Condition, ...] = tuple(),
4605
- ) -> EndpointResult:
4793
+ extra_conditions: tuple[Condition, ...] = tuple(),
4794
+ ) -> VCSpendResponse:
4606
4795
  """
4607
4796
  Spend a verified credential
4608
4797
  :param request: Required 'vc_id' launcher id of the vc we wish to spend. Optional parameters for a 'new_puzhash'
@@ -4611,34 +4800,22 @@ class WalletRpcApi:
4611
4800
  :return: a list of all relevant 'transactions' (TransactionRecord) that this spend generates (VC TX + fee TX)
4612
4801
  """
4613
4802
 
4614
- @streamable
4615
- @dataclasses.dataclass(frozen=True)
4616
- class VCSpend(Streamable):
4617
- vc_id: bytes32
4618
- new_puzhash: Optional[bytes32] = None
4619
- new_proof_hash: Optional[bytes32] = None
4620
- provider_inner_puzhash: Optional[bytes32] = None
4621
- fee: uint64 = uint64(0)
4622
-
4623
- parsed_request = VCSpend.from_json_dict(request)
4624
-
4625
4803
  vc_wallet: VCWallet = await self.service.wallet_state_manager.get_or_create_vc_wallet()
4626
4804
 
4627
4805
  await vc_wallet.generate_signed_transaction(
4628
- parsed_request.vc_id,
4806
+ request.vc_id,
4629
4807
  action_scope,
4630
- parsed_request.fee,
4631
- parsed_request.new_puzhash,
4632
- new_proof_hash=parsed_request.new_proof_hash,
4633
- provider_inner_puzhash=parsed_request.provider_inner_puzhash,
4808
+ request.fee,
4809
+ request.new_puzhash,
4810
+ new_proof_hash=request.new_proof_hash,
4811
+ provider_inner_puzhash=request.provider_inner_puzhash,
4634
4812
  extra_conditions=extra_conditions,
4635
4813
  )
4636
4814
 
4637
- return {
4638
- "transactions": None, # tx_endpoint wrapper will take care of this
4639
- }
4815
+ return VCSpendResponse([], []) # tx_endpoint takes care of filling this out
4640
4816
 
4641
- async def vc_add_proofs(self, request: Dict[str, Any]) -> EndpointResult:
4817
+ @marshal
4818
+ async def vc_add_proofs(self, request: VCAddProofs) -> Empty:
4642
4819
  """
4643
4820
  Add a set of proofs to the DB that can be used when spending a VC. VCs are near useless until their proofs have
4644
4821
  been added.
@@ -4647,70 +4824,57 @@ class WalletRpcApi:
4647
4824
  """
4648
4825
  vc_wallet: VCWallet = await self.service.wallet_state_manager.get_or_create_vc_wallet()
4649
4826
 
4650
- await vc_wallet.store.add_vc_proofs(VCProofs(request["proofs"]))
4827
+ await vc_wallet.store.add_vc_proofs(request.to_vc_proofs())
4651
4828
 
4652
- return {}
4829
+ return Empty()
4653
4830
 
4654
- async def vc_get_proofs_for_root(self, request: Dict[str, Any]) -> EndpointResult:
4831
+ @marshal
4832
+ async def vc_get_proofs_for_root(self, request: VCGetProofsForRoot) -> VCGetProofsForRootResponse:
4655
4833
  """
4656
4834
  Given a specified vc root, get any proofs associated with that root.
4657
4835
  :param request: must specify 'root' representing the tree hash of some set of proofs
4658
4836
  :return: a dictionary of root hashes mapped to dictionaries of key value pairs of 'proofs'
4659
4837
  """
4660
4838
 
4661
- @streamable
4662
- @dataclasses.dataclass(frozen=True)
4663
- class VCGetProofsForRoot(Streamable):
4664
- root: bytes32
4665
-
4666
- parsed_request = VCGetProofsForRoot.from_json_dict(request)
4667
4839
  vc_wallet: VCWallet = await self.service.wallet_state_manager.get_or_create_vc_wallet()
4668
4840
 
4669
- vc_proofs: Optional[VCProofs] = await vc_wallet.store.get_proofs_for_root(parsed_request.root)
4841
+ vc_proofs: Optional[VCProofs] = await vc_wallet.store.get_proofs_for_root(request.root)
4670
4842
  if vc_proofs is None:
4671
4843
  raise ValueError("no proofs found for specified root") # pragma: no cover
4672
- return {"proofs": vc_proofs.key_value_pairs}
4844
+ return VCGetProofsForRootResponse.from_vc_proofs(vc_proofs)
4673
4845
 
4674
4846
  @tx_endpoint(push=True)
4847
+ @marshal
4675
4848
  async def vc_revoke(
4676
4849
  self,
4677
- request: Dict[str, Any],
4850
+ request: VCRevoke,
4678
4851
  action_scope: WalletActionScope,
4679
- extra_conditions: Tuple[Condition, ...] = tuple(),
4680
- ) -> EndpointResult:
4852
+ extra_conditions: tuple[Condition, ...] = tuple(),
4853
+ ) -> VCRevokeResponse:
4681
4854
  """
4682
4855
  Revoke an on chain VC provided the correct DID is available
4683
4856
  :param request: required 'vc_parent_id' for the VC coin. Standard transaction params 'fee' & 'reuse_puzhash'.
4684
4857
  :return: a list of all relevant 'transactions' (TransactionRecord) that this spend generates (VC TX + fee TX)
4685
4858
  """
4686
4859
 
4687
- @streamable
4688
- @dataclasses.dataclass(frozen=True)
4689
- class VCRevoke(Streamable):
4690
- vc_parent_id: bytes32
4691
- fee: uint64 = uint64(0)
4692
-
4693
- parsed_request = VCRevoke.from_json_dict(request)
4694
4860
  vc_wallet: VCWallet = await self.service.wallet_state_manager.get_or_create_vc_wallet()
4695
4861
 
4696
4862
  await vc_wallet.revoke_vc(
4697
- parsed_request.vc_parent_id,
4863
+ request.vc_parent_id,
4698
4864
  self.service.get_full_node_peer(),
4699
4865
  action_scope,
4700
- parsed_request.fee,
4866
+ request.fee,
4701
4867
  extra_conditions=extra_conditions,
4702
4868
  )
4703
4869
 
4704
- return {
4705
- "transactions": None, # tx_endpoint wrapper will take care of this
4706
- }
4870
+ return VCRevokeResponse([], []) # tx_endpoint takes care of filling this out
4707
4871
 
4708
4872
  @tx_endpoint(push=True)
4709
4873
  async def crcat_approve_pending(
4710
4874
  self,
4711
- request: Dict[str, Any],
4875
+ request: dict[str, Any],
4712
4876
  action_scope: WalletActionScope,
4713
- extra_conditions: Tuple[Condition, ...] = tuple(),
4877
+ extra_conditions: tuple[Condition, ...] = tuple(),
4714
4878
  ) -> EndpointResult:
4715
4879
  """
4716
4880
  Moving any "pending approval" CR-CATs into the spendable balance of the wallet