chia-blockchain 2.5.1rc1__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.
- chia/__init__.py +10 -0
- chia/__main__.py +5 -0
- chia/_tests/README.md +53 -0
- chia/_tests/__init__.py +0 -0
- chia/_tests/blockchain/__init__.py +0 -0
- chia/_tests/blockchain/blockchain_test_utils.py +195 -0
- chia/_tests/blockchain/config.py +4 -0
- chia/_tests/blockchain/test_augmented_chain.py +145 -0
- chia/_tests/blockchain/test_blockchain.py +4202 -0
- chia/_tests/blockchain/test_blockchain_transactions.py +1031 -0
- chia/_tests/blockchain/test_build_chains.py +59 -0
- chia/_tests/blockchain/test_get_block_generator.py +72 -0
- chia/_tests/blockchain/test_lookup_fork_chain.py +194 -0
- chia/_tests/build-init-files.py +92 -0
- chia/_tests/build-job-matrix.py +204 -0
- chia/_tests/check_pytest_monitor_output.py +34 -0
- chia/_tests/check_sql_statements.py +72 -0
- chia/_tests/chia-start-sim +42 -0
- chia/_tests/clvm/__init__.py +0 -0
- chia/_tests/clvm/benchmark_costs.py +23 -0
- chia/_tests/clvm/coin_store.py +149 -0
- chia/_tests/clvm/test_chialisp_deserialization.py +101 -0
- chia/_tests/clvm/test_clvm_step.py +37 -0
- chia/_tests/clvm/test_condition_codes.py +13 -0
- chia/_tests/clvm/test_curry_and_treehash.py +55 -0
- chia/_tests/clvm/test_message_conditions.py +184 -0
- chia/_tests/clvm/test_program.py +150 -0
- chia/_tests/clvm/test_puzzle_compression.py +143 -0
- chia/_tests/clvm/test_puzzle_drivers.py +45 -0
- chia/_tests/clvm/test_puzzles.py +242 -0
- chia/_tests/clvm/test_singletons.py +540 -0
- chia/_tests/clvm/test_spend_sim.py +181 -0
- chia/_tests/cmds/__init__.py +0 -0
- chia/_tests/cmds/cmd_test_utils.py +469 -0
- chia/_tests/cmds/config.py +3 -0
- chia/_tests/cmds/conftest.py +23 -0
- chia/_tests/cmds/test_click_types.py +200 -0
- chia/_tests/cmds/test_cmd_framework.py +620 -0
- chia/_tests/cmds/test_cmds_util.py +97 -0
- chia/_tests/cmds/test_daemon.py +92 -0
- chia/_tests/cmds/test_dev_gh.py +131 -0
- chia/_tests/cmds/test_farm_cmd.py +66 -0
- chia/_tests/cmds/test_show.py +116 -0
- chia/_tests/cmds/test_sim.py +207 -0
- chia/_tests/cmds/test_timelock_args.py +75 -0
- chia/_tests/cmds/test_tx_config_args.py +154 -0
- chia/_tests/cmds/testing_classes.py +59 -0
- chia/_tests/cmds/wallet/__init__.py +0 -0
- chia/_tests/cmds/wallet/test_consts.py +47 -0
- chia/_tests/cmds/wallet/test_dao.py +565 -0
- chia/_tests/cmds/wallet/test_did.py +403 -0
- chia/_tests/cmds/wallet/test_nft.py +471 -0
- chia/_tests/cmds/wallet/test_notifications.py +124 -0
- chia/_tests/cmds/wallet/test_offer.toffer +1 -0
- chia/_tests/cmds/wallet/test_tx_decorators.py +27 -0
- chia/_tests/cmds/wallet/test_vcs.py +400 -0
- chia/_tests/cmds/wallet/test_wallet.py +1125 -0
- chia/_tests/cmds/wallet/test_wallet_check.py +109 -0
- chia/_tests/conftest.py +1419 -0
- chia/_tests/connection_utils.py +125 -0
- chia/_tests/core/__init__.py +0 -0
- chia/_tests/core/cmds/__init__.py +0 -0
- chia/_tests/core/cmds/test_beta.py +382 -0
- chia/_tests/core/cmds/test_keys.py +1734 -0
- chia/_tests/core/cmds/test_wallet.py +126 -0
- chia/_tests/core/config.py +3 -0
- chia/_tests/core/consensus/__init__.py +0 -0
- chia/_tests/core/consensus/test_block_creation.py +54 -0
- chia/_tests/core/consensus/test_pot_iterations.py +117 -0
- chia/_tests/core/custom_types/__init__.py +0 -0
- chia/_tests/core/custom_types/test_coin.py +107 -0
- chia/_tests/core/custom_types/test_proof_of_space.py +144 -0
- chia/_tests/core/custom_types/test_spend_bundle.py +70 -0
- chia/_tests/core/daemon/__init__.py +0 -0
- chia/_tests/core/daemon/config.py +4 -0
- chia/_tests/core/daemon/test_daemon.py +2128 -0
- chia/_tests/core/daemon/test_daemon_register.py +109 -0
- chia/_tests/core/daemon/test_keychain_proxy.py +101 -0
- chia/_tests/core/data_layer/__init__.py +0 -0
- chia/_tests/core/data_layer/config.py +5 -0
- chia/_tests/core/data_layer/conftest.py +106 -0
- chia/_tests/core/data_layer/test_data_cli.py +56 -0
- chia/_tests/core/data_layer/test_data_layer.py +83 -0
- chia/_tests/core/data_layer/test_data_layer_util.py +218 -0
- chia/_tests/core/data_layer/test_data_rpc.py +3847 -0
- chia/_tests/core/data_layer/test_data_store.py +2424 -0
- chia/_tests/core/data_layer/test_data_store_schema.py +381 -0
- chia/_tests/core/data_layer/test_plugin.py +91 -0
- chia/_tests/core/data_layer/util.py +233 -0
- chia/_tests/core/farmer/__init__.py +0 -0
- chia/_tests/core/farmer/config.py +3 -0
- chia/_tests/core/farmer/test_farmer_api.py +103 -0
- chia/_tests/core/full_node/__init__.py +0 -0
- chia/_tests/core/full_node/config.py +4 -0
- chia/_tests/core/full_node/dos/__init__.py +0 -0
- chia/_tests/core/full_node/dos/config.py +3 -0
- chia/_tests/core/full_node/full_sync/__init__.py +0 -0
- chia/_tests/core/full_node/full_sync/config.py +4 -0
- chia/_tests/core/full_node/full_sync/test_full_sync.py +443 -0
- chia/_tests/core/full_node/ram_db.py +27 -0
- chia/_tests/core/full_node/stores/__init__.py +0 -0
- chia/_tests/core/full_node/stores/config.py +4 -0
- chia/_tests/core/full_node/stores/test_block_store.py +590 -0
- chia/_tests/core/full_node/stores/test_coin_store.py +897 -0
- chia/_tests/core/full_node/stores/test_full_node_store.py +1219 -0
- chia/_tests/core/full_node/stores/test_hint_store.py +229 -0
- chia/_tests/core/full_node/stores/test_sync_store.py +135 -0
- chia/_tests/core/full_node/test_address_manager.py +588 -0
- chia/_tests/core/full_node/test_block_height_map.py +556 -0
- chia/_tests/core/full_node/test_conditions.py +556 -0
- chia/_tests/core/full_node/test_full_node.py +2700 -0
- chia/_tests/core/full_node/test_generator_tools.py +82 -0
- chia/_tests/core/full_node/test_hint_management.py +104 -0
- chia/_tests/core/full_node/test_node_load.py +34 -0
- chia/_tests/core/full_node/test_performance.py +179 -0
- chia/_tests/core/full_node/test_subscriptions.py +492 -0
- chia/_tests/core/full_node/test_transactions.py +203 -0
- chia/_tests/core/full_node/test_tx_processing_queue.py +155 -0
- chia/_tests/core/large_block.py +2388 -0
- chia/_tests/core/make_block_generator.py +70 -0
- chia/_tests/core/mempool/__init__.py +0 -0
- chia/_tests/core/mempool/config.py +4 -0
- chia/_tests/core/mempool/test_mempool.py +3255 -0
- chia/_tests/core/mempool/test_mempool_fee_estimator.py +104 -0
- chia/_tests/core/mempool/test_mempool_fee_protocol.py +55 -0
- chia/_tests/core/mempool/test_mempool_item_queries.py +190 -0
- chia/_tests/core/mempool/test_mempool_manager.py +2084 -0
- chia/_tests/core/mempool/test_mempool_performance.py +64 -0
- chia/_tests/core/mempool/test_singleton_fast_forward.py +567 -0
- chia/_tests/core/node_height.py +28 -0
- chia/_tests/core/server/__init__.py +0 -0
- chia/_tests/core/server/config.py +3 -0
- chia/_tests/core/server/flood.py +84 -0
- chia/_tests/core/server/serve.py +135 -0
- chia/_tests/core/server/test_api_protocol.py +21 -0
- chia/_tests/core/server/test_capabilities.py +66 -0
- chia/_tests/core/server/test_dos.py +319 -0
- chia/_tests/core/server/test_event_loop.py +109 -0
- chia/_tests/core/server/test_loop.py +294 -0
- chia/_tests/core/server/test_node_discovery.py +73 -0
- chia/_tests/core/server/test_rate_limits.py +482 -0
- chia/_tests/core/server/test_server.py +226 -0
- chia/_tests/core/server/test_upnp.py +8 -0
- chia/_tests/core/services/__init__.py +0 -0
- chia/_tests/core/services/config.py +3 -0
- chia/_tests/core/services/test_services.py +188 -0
- chia/_tests/core/ssl/__init__.py +0 -0
- chia/_tests/core/ssl/config.py +3 -0
- chia/_tests/core/ssl/test_ssl.py +202 -0
- chia/_tests/core/test_coins.py +33 -0
- chia/_tests/core/test_cost_calculation.py +313 -0
- chia/_tests/core/test_crawler.py +175 -0
- chia/_tests/core/test_crawler_rpc.py +53 -0
- chia/_tests/core/test_daemon_rpc.py +24 -0
- chia/_tests/core/test_db_conversion.py +130 -0
- chia/_tests/core/test_db_validation.py +162 -0
- chia/_tests/core/test_farmer_harvester_rpc.py +505 -0
- chia/_tests/core/test_filter.py +35 -0
- chia/_tests/core/test_full_node_rpc.py +768 -0
- chia/_tests/core/test_merkle_set.py +343 -0
- chia/_tests/core/test_program.py +47 -0
- chia/_tests/core/test_rpc_util.py +86 -0
- chia/_tests/core/test_seeder.py +420 -0
- chia/_tests/core/test_setproctitle.py +13 -0
- chia/_tests/core/util/__init__.py +0 -0
- chia/_tests/core/util/config.py +4 -0
- chia/_tests/core/util/test_block_cache.py +44 -0
- chia/_tests/core/util/test_cached_bls.py +57 -0
- chia/_tests/core/util/test_config.py +337 -0
- chia/_tests/core/util/test_file_keyring_synchronization.py +105 -0
- chia/_tests/core/util/test_files.py +391 -0
- chia/_tests/core/util/test_jsonify.py +146 -0
- chia/_tests/core/util/test_keychain.py +522 -0
- chia/_tests/core/util/test_keyring_wrapper.py +491 -0
- chia/_tests/core/util/test_lockfile.py +380 -0
- chia/_tests/core/util/test_log_exceptions.py +187 -0
- chia/_tests/core/util/test_lru_cache.py +56 -0
- chia/_tests/core/util/test_significant_bits.py +40 -0
- chia/_tests/core/util/test_streamable.py +883 -0
- chia/_tests/db/__init__.py +0 -0
- chia/_tests/db/test_db_wrapper.py +566 -0
- chia/_tests/environments/__init__.py +0 -0
- chia/_tests/environments/common.py +35 -0
- chia/_tests/environments/full_node.py +47 -0
- chia/_tests/environments/wallet.py +429 -0
- chia/_tests/ether.py +19 -0
- chia/_tests/farmer_harvester/__init__.py +0 -0
- chia/_tests/farmer_harvester/config.py +3 -0
- chia/_tests/farmer_harvester/test_farmer.py +1264 -0
- chia/_tests/farmer_harvester/test_farmer_harvester.py +292 -0
- chia/_tests/farmer_harvester/test_filter_prefix_bits.py +131 -0
- chia/_tests/farmer_harvester/test_third_party_harvesters.py +528 -0
- chia/_tests/farmer_harvester/test_third_party_harvesters_data.json +29 -0
- chia/_tests/fee_estimation/__init__.py +0 -0
- chia/_tests/fee_estimation/config.py +3 -0
- chia/_tests/fee_estimation/test_fee_estimation_integration.py +262 -0
- chia/_tests/fee_estimation/test_fee_estimation_rpc.py +287 -0
- chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +144 -0
- chia/_tests/fee_estimation/test_mempoolitem_height_added.py +146 -0
- chia/_tests/generator/__init__.py +0 -0
- chia/_tests/generator/puzzles/__init__.py +0 -0
- chia/_tests/generator/puzzles/test_generator_deserialize.clsp +3 -0
- chia/_tests/generator/puzzles/test_generator_deserialize.clsp.hex +1 -0
- chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp +19 -0
- chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp.hex +1 -0
- chia/_tests/generator/test_compression.py +201 -0
- chia/_tests/generator/test_generator_types.py +44 -0
- chia/_tests/generator/test_rom.py +180 -0
- chia/_tests/plot_sync/__init__.py +0 -0
- chia/_tests/plot_sync/config.py +3 -0
- chia/_tests/plot_sync/test_delta.py +101 -0
- chia/_tests/plot_sync/test_plot_sync.py +618 -0
- chia/_tests/plot_sync/test_receiver.py +451 -0
- chia/_tests/plot_sync/test_sender.py +116 -0
- chia/_tests/plot_sync/test_sync_simulated.py +451 -0
- chia/_tests/plot_sync/util.py +68 -0
- chia/_tests/plotting/__init__.py +0 -0
- chia/_tests/plotting/config.py +3 -0
- chia/_tests/plotting/test_plot_manager.py +781 -0
- chia/_tests/plotting/util.py +12 -0
- chia/_tests/pools/__init__.py +0 -0
- chia/_tests/pools/config.py +5 -0
- chia/_tests/pools/test_pool_cli_parsing.py +128 -0
- chia/_tests/pools/test_pool_cmdline.py +1001 -0
- chia/_tests/pools/test_pool_config.py +42 -0
- chia/_tests/pools/test_pool_puzzles_lifecycle.py +397 -0
- chia/_tests/pools/test_pool_rpc.py +1123 -0
- chia/_tests/pools/test_pool_wallet.py +205 -0
- chia/_tests/pools/test_wallet_pool_store.py +161 -0
- chia/_tests/process_junit.py +348 -0
- chia/_tests/rpc/__init__.py +0 -0
- chia/_tests/rpc/test_rpc_client.py +138 -0
- chia/_tests/rpc/test_rpc_server.py +183 -0
- chia/_tests/simulation/__init__.py +0 -0
- chia/_tests/simulation/config.py +6 -0
- chia/_tests/simulation/test_simulation.py +501 -0
- chia/_tests/simulation/test_simulator.py +232 -0
- chia/_tests/simulation/test_start_simulator.py +107 -0
- chia/_tests/testconfig.py +13 -0
- chia/_tests/timelord/__init__.py +0 -0
- chia/_tests/timelord/config.py +3 -0
- chia/_tests/timelord/test_new_peak.py +437 -0
- chia/_tests/timelord/test_timelord.py +11 -0
- chia/_tests/tools/1315537.json +170 -0
- chia/_tests/tools/1315544.json +160 -0
- chia/_tests/tools/1315630.json +150 -0
- chia/_tests/tools/300000.json +105 -0
- chia/_tests/tools/442734.json +140 -0
- chia/_tests/tools/466212.json +130 -0
- chia/_tests/tools/__init__.py +0 -0
- chia/_tests/tools/config.py +5 -0
- chia/_tests/tools/test-blockchain-db.sqlite +0 -0
- chia/_tests/tools/test_full_sync.py +30 -0
- chia/_tests/tools/test_legacy_keyring.py +82 -0
- chia/_tests/tools/test_run_block.py +128 -0
- chia/_tests/tools/test_virtual_project.py +591 -0
- chia/_tests/util/__init__.py +0 -0
- chia/_tests/util/benchmark_cost.py +170 -0
- chia/_tests/util/benchmarks.py +153 -0
- chia/_tests/util/bip39_test_vectors.json +148 -0
- chia/_tests/util/blockchain.py +134 -0
- chia/_tests/util/blockchain_mock.py +132 -0
- chia/_tests/util/build_network_protocol_files.py +302 -0
- chia/_tests/util/clvm_generator.bin +0 -0
- chia/_tests/util/config.py +3 -0
- chia/_tests/util/constants.py +20 -0
- chia/_tests/util/db_connection.py +37 -0
- chia/_tests/util/full_sync.py +253 -0
- chia/_tests/util/gen_ssl_certs.py +114 -0
- chia/_tests/util/generator_tools_testing.py +45 -0
- chia/_tests/util/get_name_puzzle_conditions.py +52 -0
- chia/_tests/util/key_tool.py +36 -0
- chia/_tests/util/misc.py +675 -0
- chia/_tests/util/network_protocol_data.py +1072 -0
- chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
- chia/_tests/util/protocol_messages_json.py +2701 -0
- chia/_tests/util/rpc.py +26 -0
- chia/_tests/util/run_block.py +163 -0
- chia/_tests/util/setup_nodes.py +481 -0
- chia/_tests/util/spend_sim.py +492 -0
- chia/_tests/util/split_managers.py +102 -0
- chia/_tests/util/temp_file.py +14 -0
- chia/_tests/util/test_action_scope.py +144 -0
- chia/_tests/util/test_async_pool.py +366 -0
- chia/_tests/util/test_build_job_matrix.py +42 -0
- chia/_tests/util/test_build_network_protocol_files.py +7 -0
- chia/_tests/util/test_chia_version.py +50 -0
- chia/_tests/util/test_collection.py +11 -0
- chia/_tests/util/test_condition_tools.py +229 -0
- chia/_tests/util/test_config.py +426 -0
- chia/_tests/util/test_dump_keyring.py +60 -0
- chia/_tests/util/test_errors.py +10 -0
- chia/_tests/util/test_full_block_utils.py +279 -0
- chia/_tests/util/test_installed.py +20 -0
- chia/_tests/util/test_limited_semaphore.py +53 -0
- chia/_tests/util/test_logging_filter.py +42 -0
- chia/_tests/util/test_misc.py +445 -0
- chia/_tests/util/test_network.py +73 -0
- chia/_tests/util/test_network_protocol_files.py +578 -0
- chia/_tests/util/test_network_protocol_json.py +267 -0
- chia/_tests/util/test_network_protocol_test.py +256 -0
- chia/_tests/util/test_paginator.py +71 -0
- chia/_tests/util/test_pprint.py +17 -0
- chia/_tests/util/test_priority_mutex.py +488 -0
- chia/_tests/util/test_recursive_replace.py +116 -0
- chia/_tests/util/test_replace_str_to_bytes.py +137 -0
- chia/_tests/util/test_service_groups.py +15 -0
- chia/_tests/util/test_ssl_check.py +31 -0
- chia/_tests/util/test_testnet_overrides.py +19 -0
- chia/_tests/util/test_tests_misc.py +38 -0
- chia/_tests/util/test_timing.py +37 -0
- chia/_tests/util/test_trusted_peer.py +51 -0
- chia/_tests/util/time_out_assert.py +191 -0
- chia/_tests/wallet/__init__.py +0 -0
- chia/_tests/wallet/cat_wallet/__init__.py +0 -0
- chia/_tests/wallet/cat_wallet/config.py +4 -0
- chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +468 -0
- chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +69 -0
- chia/_tests/wallet/cat_wallet/test_cat_wallet.py +1826 -0
- chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +291 -0
- chia/_tests/wallet/cat_wallet/test_trades.py +2600 -0
- chia/_tests/wallet/clawback/__init__.py +0 -0
- chia/_tests/wallet/clawback/config.py +3 -0
- chia/_tests/wallet/clawback/test_clawback_decorator.py +78 -0
- chia/_tests/wallet/clawback/test_clawback_lifecycle.py +292 -0
- chia/_tests/wallet/clawback/test_clawback_metadata.py +50 -0
- chia/_tests/wallet/config.py +4 -0
- chia/_tests/wallet/conftest.py +278 -0
- chia/_tests/wallet/dao_wallet/__init__.py +0 -0
- chia/_tests/wallet/dao_wallet/config.py +3 -0
- chia/_tests/wallet/dao_wallet/test_dao_clvm.py +1330 -0
- chia/_tests/wallet/dao_wallet/test_dao_wallets.py +3488 -0
- chia/_tests/wallet/db_wallet/__init__.py +0 -0
- chia/_tests/wallet/db_wallet/config.py +3 -0
- chia/_tests/wallet/db_wallet/test_db_graftroot.py +141 -0
- chia/_tests/wallet/db_wallet/test_dl_offers.py +491 -0
- chia/_tests/wallet/db_wallet/test_dl_wallet.py +823 -0
- chia/_tests/wallet/did_wallet/__init__.py +0 -0
- chia/_tests/wallet/did_wallet/config.py +4 -0
- chia/_tests/wallet/did_wallet/test_did.py +2284 -0
- chia/_tests/wallet/nft_wallet/__init__.py +0 -0
- chia/_tests/wallet/nft_wallet/config.py +4 -0
- chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +1493 -0
- chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +1024 -0
- chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +375 -0
- chia/_tests/wallet/nft_wallet/test_nft_offers.py +1209 -0
- chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +172 -0
- chia/_tests/wallet/nft_wallet/test_nft_wallet.py +2584 -0
- chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +70 -0
- chia/_tests/wallet/rpc/__init__.py +0 -0
- chia/_tests/wallet/rpc/config.py +4 -0
- chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +285 -0
- chia/_tests/wallet/rpc/test_wallet_rpc.py +3153 -0
- chia/_tests/wallet/simple_sync/__init__.py +0 -0
- chia/_tests/wallet/simple_sync/config.py +3 -0
- chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +718 -0
- chia/_tests/wallet/sync/__init__.py +0 -0
- chia/_tests/wallet/sync/config.py +4 -0
- chia/_tests/wallet/sync/test_wallet_sync.py +1692 -0
- chia/_tests/wallet/test_address_type.py +189 -0
- chia/_tests/wallet/test_bech32m.py +45 -0
- chia/_tests/wallet/test_clvm_streamable.py +244 -0
- chia/_tests/wallet/test_coin_management.py +354 -0
- chia/_tests/wallet/test_coin_selection.py +588 -0
- chia/_tests/wallet/test_conditions.py +400 -0
- chia/_tests/wallet/test_debug_spend_bundle.py +218 -0
- chia/_tests/wallet/test_new_wallet_protocol.py +1174 -0
- chia/_tests/wallet/test_nft_store.py +192 -0
- chia/_tests/wallet/test_notifications.py +196 -0
- chia/_tests/wallet/test_offer_parsing_performance.py +48 -0
- chia/_tests/wallet/test_puzzle_store.py +132 -0
- chia/_tests/wallet/test_sign_coin_spends.py +159 -0
- chia/_tests/wallet/test_signer_protocol.py +947 -0
- chia/_tests/wallet/test_singleton.py +122 -0
- chia/_tests/wallet/test_singleton_lifecycle_fast.py +772 -0
- chia/_tests/wallet/test_singleton_store.py +152 -0
- chia/_tests/wallet/test_taproot.py +19 -0
- chia/_tests/wallet/test_transaction_store.py +945 -0
- chia/_tests/wallet/test_util.py +185 -0
- chia/_tests/wallet/test_wallet.py +2139 -0
- chia/_tests/wallet/test_wallet_action_scope.py +85 -0
- chia/_tests/wallet/test_wallet_blockchain.py +111 -0
- chia/_tests/wallet/test_wallet_coin_store.py +1002 -0
- chia/_tests/wallet/test_wallet_interested_store.py +43 -0
- chia/_tests/wallet/test_wallet_key_val_store.py +40 -0
- chia/_tests/wallet/test_wallet_node.py +780 -0
- chia/_tests/wallet/test_wallet_retry.py +95 -0
- chia/_tests/wallet/test_wallet_state_manager.py +259 -0
- chia/_tests/wallet/test_wallet_test_framework.py +275 -0
- chia/_tests/wallet/test_wallet_trade_store.py +218 -0
- chia/_tests/wallet/test_wallet_user_store.py +34 -0
- chia/_tests/wallet/test_wallet_utils.py +156 -0
- chia/_tests/wallet/vc_wallet/__init__.py +0 -0
- chia/_tests/wallet/vc_wallet/config.py +3 -0
- chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +70 -0
- chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +883 -0
- chia/_tests/wallet/vc_wallet/test_vc_wallet.py +830 -0
- chia/_tests/wallet/wallet_block_tools.py +327 -0
- chia/_tests/weight_proof/__init__.py +0 -0
- chia/_tests/weight_proof/config.py +3 -0
- chia/_tests/weight_proof/test_weight_proof.py +528 -0
- chia/apis.py +19 -0
- chia/clvm/__init__.py +0 -0
- chia/cmds/__init__.py +0 -0
- chia/cmds/beta.py +184 -0
- chia/cmds/beta_funcs.py +137 -0
- chia/cmds/check_wallet_db.py +420 -0
- chia/cmds/chia.py +151 -0
- chia/cmds/cmd_classes.py +323 -0
- chia/cmds/cmd_helpers.py +242 -0
- chia/cmds/cmds_util.py +488 -0
- chia/cmds/coin_funcs.py +275 -0
- chia/cmds/coins.py +182 -0
- chia/cmds/completion.py +49 -0
- chia/cmds/configure.py +332 -0
- chia/cmds/dao.py +1064 -0
- chia/cmds/dao_funcs.py +598 -0
- chia/cmds/data.py +708 -0
- chia/cmds/data_funcs.py +385 -0
- chia/cmds/db.py +87 -0
- chia/cmds/db_backup_func.py +77 -0
- chia/cmds/db_upgrade_func.py +452 -0
- chia/cmds/db_validate_func.py +184 -0
- chia/cmds/dev.py +18 -0
- chia/cmds/farm.py +100 -0
- chia/cmds/farm_funcs.py +200 -0
- chia/cmds/gh.py +275 -0
- chia/cmds/init.py +63 -0
- chia/cmds/init_funcs.py +367 -0
- chia/cmds/installers.py +131 -0
- chia/cmds/keys.py +527 -0
- chia/cmds/keys_funcs.py +863 -0
- chia/cmds/netspace.py +50 -0
- chia/cmds/netspace_funcs.py +54 -0
- chia/cmds/options.py +32 -0
- chia/cmds/param_types.py +238 -0
- chia/cmds/passphrase.py +131 -0
- chia/cmds/passphrase_funcs.py +292 -0
- chia/cmds/peer.py +51 -0
- chia/cmds/peer_funcs.py +129 -0
- chia/cmds/plotnft.py +260 -0
- chia/cmds/plotnft_funcs.py +405 -0
- chia/cmds/plots.py +230 -0
- chia/cmds/plotters.py +18 -0
- chia/cmds/rpc.py +208 -0
- chia/cmds/show.py +72 -0
- chia/cmds/show_funcs.py +215 -0
- chia/cmds/signer.py +296 -0
- chia/cmds/sim.py +225 -0
- chia/cmds/sim_funcs.py +509 -0
- chia/cmds/start.py +24 -0
- chia/cmds/start_funcs.py +109 -0
- chia/cmds/stop.py +62 -0
- chia/cmds/units.py +9 -0
- chia/cmds/wallet.py +1901 -0
- chia/cmds/wallet_funcs.py +1874 -0
- chia/consensus/__init__.py +0 -0
- chia/consensus/block_body_validation.py +562 -0
- chia/consensus/block_creation.py +546 -0
- chia/consensus/block_header_validation.py +1059 -0
- chia/consensus/block_record.py +31 -0
- chia/consensus/block_rewards.py +53 -0
- chia/consensus/blockchain.py +1087 -0
- chia/consensus/blockchain_interface.py +56 -0
- chia/consensus/coinbase.py +30 -0
- chia/consensus/condition_costs.py +9 -0
- chia/consensus/constants.py +49 -0
- chia/consensus/cost_calculator.py +15 -0
- chia/consensus/default_constants.py +89 -0
- chia/consensus/deficit.py +55 -0
- chia/consensus/difficulty_adjustment.py +412 -0
- chia/consensus/find_fork_point.py +111 -0
- chia/consensus/full_block_to_block_record.py +167 -0
- chia/consensus/get_block_challenge.py +106 -0
- chia/consensus/get_block_generator.py +27 -0
- chia/consensus/make_sub_epoch_summary.py +210 -0
- chia/consensus/multiprocess_validation.py +268 -0
- chia/consensus/pos_quality.py +19 -0
- chia/consensus/pot_iterations.py +67 -0
- chia/consensus/puzzles/__init__.py +0 -0
- chia/consensus/puzzles/chialisp_deserialisation.clsp +69 -0
- chia/consensus/puzzles/chialisp_deserialisation.clsp.hex +1 -0
- chia/consensus/puzzles/rom_bootstrap_generator.clsp +37 -0
- chia/consensus/puzzles/rom_bootstrap_generator.clsp.hex +1 -0
- chia/consensus/vdf_info_computation.py +156 -0
- chia/daemon/__init__.py +0 -0
- chia/daemon/client.py +252 -0
- chia/daemon/keychain_proxy.py +502 -0
- chia/daemon/keychain_server.py +365 -0
- chia/daemon/server.py +1606 -0
- chia/daemon/windows_signal.py +56 -0
- chia/data_layer/__init__.py +0 -0
- chia/data_layer/data_layer.py +1291 -0
- chia/data_layer/data_layer_api.py +33 -0
- chia/data_layer/data_layer_errors.py +50 -0
- chia/data_layer/data_layer_server.py +170 -0
- chia/data_layer/data_layer_util.py +985 -0
- chia/data_layer/data_layer_wallet.py +1311 -0
- chia/data_layer/data_store.py +2267 -0
- chia/data_layer/dl_wallet_store.py +407 -0
- chia/data_layer/download_data.py +389 -0
- chia/data_layer/puzzles/__init__.py +0 -0
- chia/data_layer/puzzles/graftroot_dl_offers.clsp +100 -0
- chia/data_layer/puzzles/graftroot_dl_offers.clsp.hex +1 -0
- chia/data_layer/s3_plugin_config.yml +33 -0
- chia/data_layer/s3_plugin_service.py +468 -0
- chia/data_layer/util/__init__.py +0 -0
- chia/data_layer/util/benchmark.py +107 -0
- chia/data_layer/util/plugin.py +40 -0
- chia/farmer/__init__.py +0 -0
- chia/farmer/farmer.py +923 -0
- chia/farmer/farmer_api.py +820 -0
- chia/full_node/__init__.py +0 -0
- chia/full_node/bitcoin_fee_estimator.py +85 -0
- chia/full_node/block_height_map.py +271 -0
- chia/full_node/block_store.py +576 -0
- chia/full_node/bundle_tools.py +19 -0
- chia/full_node/coin_store.py +647 -0
- chia/full_node/fee_estimate.py +54 -0
- chia/full_node/fee_estimate_store.py +24 -0
- chia/full_node/fee_estimation.py +92 -0
- chia/full_node/fee_estimator.py +90 -0
- chia/full_node/fee_estimator_constants.py +38 -0
- chia/full_node/fee_estimator_interface.py +42 -0
- chia/full_node/fee_history.py +25 -0
- chia/full_node/fee_tracker.py +564 -0
- chia/full_node/full_node.py +3327 -0
- chia/full_node/full_node_api.py +2025 -0
- chia/full_node/full_node_store.py +1033 -0
- chia/full_node/hint_management.py +56 -0
- chia/full_node/hint_store.py +93 -0
- chia/full_node/mempool.py +589 -0
- chia/full_node/mempool_check_conditions.py +146 -0
- chia/full_node/mempool_manager.py +853 -0
- chia/full_node/pending_tx_cache.py +112 -0
- chia/full_node/puzzles/__init__.py +0 -0
- chia/full_node/puzzles/block_program_zero.clsp +14 -0
- chia/full_node/puzzles/block_program_zero.clsp.hex +1 -0
- chia/full_node/puzzles/decompress_coin_spend_entry.clsp +5 -0
- chia/full_node/puzzles/decompress_coin_spend_entry.clsp.hex +1 -0
- chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp +7 -0
- chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp.hex +1 -0
- chia/full_node/puzzles/decompress_puzzle.clsp +6 -0
- chia/full_node/puzzles/decompress_puzzle.clsp.hex +1 -0
- chia/full_node/signage_point.py +16 -0
- chia/full_node/subscriptions.py +247 -0
- chia/full_node/sync_store.py +146 -0
- chia/full_node/tx_processing_queue.py +78 -0
- chia/full_node/util/__init__.py +0 -0
- chia/full_node/weight_proof.py +1720 -0
- chia/harvester/__init__.py +0 -0
- chia/harvester/harvester.py +272 -0
- chia/harvester/harvester_api.py +380 -0
- chia/introducer/__init__.py +0 -0
- chia/introducer/introducer.py +122 -0
- chia/introducer/introducer_api.py +70 -0
- chia/legacy/__init__.py +0 -0
- chia/legacy/keyring.py +155 -0
- chia/plot_sync/__init__.py +0 -0
- chia/plot_sync/delta.py +61 -0
- chia/plot_sync/exceptions.py +56 -0
- chia/plot_sync/receiver.py +386 -0
- chia/plot_sync/sender.py +340 -0
- chia/plot_sync/util.py +43 -0
- chia/plotters/__init__.py +0 -0
- chia/plotters/bladebit.py +388 -0
- chia/plotters/chiapos.py +63 -0
- chia/plotters/madmax.py +224 -0
- chia/plotters/plotters.py +577 -0
- chia/plotters/plotters_util.py +133 -0
- chia/plotting/__init__.py +0 -0
- chia/plotting/cache.py +213 -0
- chia/plotting/check_plots.py +283 -0
- chia/plotting/create_plots.py +278 -0
- chia/plotting/manager.py +436 -0
- chia/plotting/util.py +336 -0
- chia/pools/__init__.py +0 -0
- chia/pools/pool_config.py +110 -0
- chia/pools/pool_puzzles.py +459 -0
- chia/pools/pool_wallet.py +933 -0
- chia/pools/pool_wallet_info.py +118 -0
- chia/pools/puzzles/__init__.py +0 -0
- chia/pools/puzzles/pool_member_innerpuz.clsp +70 -0
- chia/pools/puzzles/pool_member_innerpuz.clsp.hex +1 -0
- chia/pools/puzzles/pool_waitingroom_innerpuz.clsp +69 -0
- chia/pools/puzzles/pool_waitingroom_innerpuz.clsp.hex +1 -0
- chia/protocols/__init__.py +0 -0
- chia/protocols/farmer_protocol.py +102 -0
- chia/protocols/full_node_protocol.py +219 -0
- chia/protocols/harvester_protocol.py +216 -0
- chia/protocols/introducer_protocol.py +25 -0
- chia/protocols/pool_protocol.py +177 -0
- chia/protocols/protocol_message_types.py +139 -0
- chia/protocols/protocol_state_machine.py +87 -0
- chia/protocols/protocol_timing.py +8 -0
- chia/protocols/shared_protocol.py +86 -0
- chia/protocols/timelord_protocol.py +93 -0
- chia/protocols/wallet_protocol.py +401 -0
- chia/py.typed +0 -0
- chia/rpc/__init__.py +0 -0
- chia/rpc/crawler_rpc_api.py +80 -0
- chia/rpc/data_layer_rpc_api.py +644 -0
- chia/rpc/data_layer_rpc_client.py +188 -0
- chia/rpc/data_layer_rpc_util.py +58 -0
- chia/rpc/farmer_rpc_api.py +365 -0
- chia/rpc/farmer_rpc_client.py +86 -0
- chia/rpc/full_node_rpc_api.py +959 -0
- chia/rpc/full_node_rpc_client.py +292 -0
- chia/rpc/harvester_rpc_api.py +141 -0
- chia/rpc/harvester_rpc_client.py +54 -0
- chia/rpc/rpc_client.py +164 -0
- chia/rpc/rpc_server.py +521 -0
- chia/rpc/timelord_rpc_api.py +32 -0
- chia/rpc/util.py +93 -0
- chia/rpc/wallet_request_types.py +904 -0
- chia/rpc/wallet_rpc_api.py +4943 -0
- chia/rpc/wallet_rpc_client.py +1814 -0
- chia/seeder/__init__.py +0 -0
- chia/seeder/crawl_store.py +425 -0
- chia/seeder/crawler.py +410 -0
- chia/seeder/crawler_api.py +135 -0
- chia/seeder/dns_server.py +593 -0
- chia/seeder/peer_record.py +146 -0
- chia/seeder/start_crawler.py +92 -0
- chia/server/__init__.py +0 -0
- chia/server/address_manager.py +658 -0
- chia/server/address_manager_store.py +237 -0
- chia/server/api_protocol.py +116 -0
- chia/server/capabilities.py +24 -0
- chia/server/chia_policy.py +346 -0
- chia/server/introducer_peers.py +76 -0
- chia/server/node_discovery.py +714 -0
- chia/server/outbound_message.py +33 -0
- chia/server/rate_limit_numbers.py +214 -0
- chia/server/rate_limits.py +153 -0
- chia/server/server.py +741 -0
- chia/server/signal_handlers.py +120 -0
- chia/server/ssl_context.py +32 -0
- chia/server/start_data_layer.py +151 -0
- chia/server/start_farmer.py +98 -0
- chia/server/start_full_node.py +112 -0
- chia/server/start_harvester.py +93 -0
- chia/server/start_introducer.py +81 -0
- chia/server/start_service.py +316 -0
- chia/server/start_timelord.py +89 -0
- chia/server/start_wallet.py +113 -0
- chia/server/upnp.py +118 -0
- chia/server/ws_connection.py +766 -0
- chia/simulator/__init__.py +0 -0
- chia/simulator/add_blocks_in_batches.py +54 -0
- chia/simulator/block_tools.py +2054 -0
- chia/simulator/full_node_simulator.py +794 -0
- chia/simulator/keyring.py +128 -0
- chia/simulator/setup_services.py +506 -0
- chia/simulator/simulator_constants.py +13 -0
- chia/simulator/simulator_full_node_rpc_api.py +99 -0
- chia/simulator/simulator_full_node_rpc_client.py +60 -0
- chia/simulator/simulator_protocol.py +29 -0
- chia/simulator/simulator_test_tools.py +164 -0
- chia/simulator/socket.py +24 -0
- chia/simulator/ssl_certs.py +114 -0
- chia/simulator/ssl_certs_1.py +697 -0
- chia/simulator/ssl_certs_10.py +697 -0
- chia/simulator/ssl_certs_2.py +697 -0
- chia/simulator/ssl_certs_3.py +697 -0
- chia/simulator/ssl_certs_4.py +697 -0
- chia/simulator/ssl_certs_5.py +697 -0
- chia/simulator/ssl_certs_6.py +697 -0
- chia/simulator/ssl_certs_7.py +697 -0
- chia/simulator/ssl_certs_8.py +697 -0
- chia/simulator/ssl_certs_9.py +697 -0
- chia/simulator/start_simulator.py +143 -0
- chia/simulator/wallet_tools.py +246 -0
- chia/ssl/__init__.py +0 -0
- chia/ssl/chia_ca.crt +19 -0
- chia/ssl/chia_ca.key +28 -0
- chia/ssl/create_ssl.py +249 -0
- chia/ssl/dst_root_ca.pem +20 -0
- chia/timelord/__init__.py +0 -0
- chia/timelord/iters_from_block.py +50 -0
- chia/timelord/timelord.py +1226 -0
- chia/timelord/timelord_api.py +138 -0
- chia/timelord/timelord_launcher.py +190 -0
- chia/timelord/timelord_state.py +244 -0
- chia/timelord/types.py +22 -0
- chia/types/__init__.py +0 -0
- chia/types/aliases.py +35 -0
- chia/types/block_protocol.py +20 -0
- chia/types/blockchain_format/__init__.py +0 -0
- chia/types/blockchain_format/classgroup.py +5 -0
- chia/types/blockchain_format/coin.py +28 -0
- chia/types/blockchain_format/foliage.py +8 -0
- chia/types/blockchain_format/pool_target.py +5 -0
- chia/types/blockchain_format/program.py +269 -0
- chia/types/blockchain_format/proof_of_space.py +135 -0
- chia/types/blockchain_format/reward_chain_block.py +6 -0
- chia/types/blockchain_format/serialized_program.py +5 -0
- chia/types/blockchain_format/sized_bytes.py +11 -0
- chia/types/blockchain_format/slots.py +9 -0
- chia/types/blockchain_format/sub_epoch_summary.py +5 -0
- chia/types/blockchain_format/tree_hash.py +72 -0
- chia/types/blockchain_format/vdf.py +86 -0
- chia/types/clvm_cost.py +13 -0
- chia/types/coin_record.py +43 -0
- chia/types/coin_spend.py +115 -0
- chia/types/condition_opcodes.py +73 -0
- chia/types/condition_with_args.py +16 -0
- chia/types/eligible_coin_spends.py +365 -0
- chia/types/end_of_slot_bundle.py +5 -0
- chia/types/fee_rate.py +38 -0
- chia/types/full_block.py +5 -0
- chia/types/generator_types.py +13 -0
- chia/types/header_block.py +5 -0
- chia/types/internal_mempool_item.py +18 -0
- chia/types/mempool_inclusion_status.py +9 -0
- chia/types/mempool_item.py +85 -0
- chia/types/mempool_submission_status.py +30 -0
- chia/types/mojos.py +7 -0
- chia/types/peer_info.py +64 -0
- chia/types/signing_mode.py +29 -0
- chia/types/spend_bundle.py +30 -0
- chia/types/spend_bundle_conditions.py +7 -0
- chia/types/transaction_queue_entry.py +55 -0
- chia/types/unfinished_block.py +5 -0
- chia/types/unfinished_header_block.py +37 -0
- chia/types/validation_state.py +14 -0
- chia/types/weight_proof.py +49 -0
- chia/util/__init__.py +0 -0
- chia/util/action_scope.py +168 -0
- chia/util/async_pool.py +226 -0
- chia/util/augmented_chain.py +134 -0
- chia/util/batches.py +42 -0
- chia/util/bech32m.py +126 -0
- chia/util/beta_metrics.py +119 -0
- chia/util/block_cache.py +56 -0
- chia/util/byte_types.py +12 -0
- chia/util/check_fork_next_block.py +33 -0
- chia/util/chia_logging.py +144 -0
- chia/util/chia_version.py +33 -0
- chia/util/collection.py +17 -0
- chia/util/condition_tools.py +201 -0
- chia/util/config.py +367 -0
- chia/util/cpu.py +22 -0
- chia/util/db_synchronous.py +23 -0
- chia/util/db_version.py +32 -0
- chia/util/db_wrapper.py +430 -0
- chia/util/default_root.py +27 -0
- chia/util/dump_keyring.py +93 -0
- chia/util/english.txt +2048 -0
- chia/util/errors.py +353 -0
- chia/util/file_keyring.py +469 -0
- chia/util/files.py +97 -0
- chia/util/full_block_utils.py +345 -0
- chia/util/generator_tools.py +72 -0
- chia/util/hash.py +31 -0
- chia/util/initial-config.yaml +694 -0
- chia/util/inline_executor.py +26 -0
- chia/util/ints.py +19 -0
- chia/util/ip_address.py +39 -0
- chia/util/json_util.py +37 -0
- chia/util/keychain.py +676 -0
- chia/util/keyring_wrapper.py +327 -0
- chia/util/limited_semaphore.py +41 -0
- chia/util/lock.py +49 -0
- chia/util/log_exceptions.py +32 -0
- chia/util/logging.py +36 -0
- chia/util/lru_cache.py +31 -0
- chia/util/math.py +20 -0
- chia/util/network.py +182 -0
- chia/util/paginator.py +48 -0
- chia/util/path.py +31 -0
- chia/util/permissions.py +20 -0
- chia/util/prev_transaction_block.py +21 -0
- chia/util/priority_mutex.py +95 -0
- chia/util/profiler.py +197 -0
- chia/util/recursive_replace.py +24 -0
- chia/util/safe_cancel_task.py +16 -0
- chia/util/service_groups.py +47 -0
- chia/util/setproctitle.py +22 -0
- chia/util/significant_bits.py +32 -0
- chia/util/ssl_check.py +213 -0
- chia/util/streamable.py +642 -0
- chia/util/task_referencer.py +59 -0
- chia/util/task_timing.py +382 -0
- chia/util/timing.py +67 -0
- chia/util/vdf_prover.py +30 -0
- chia/util/virtual_project_analysis.py +540 -0
- chia/util/ws_message.py +66 -0
- chia/wallet/__init__.py +0 -0
- chia/wallet/cat_wallet/__init__.py +0 -0
- chia/wallet/cat_wallet/cat_constants.py +75 -0
- chia/wallet/cat_wallet/cat_info.py +47 -0
- chia/wallet/cat_wallet/cat_outer_puzzle.py +120 -0
- chia/wallet/cat_wallet/cat_utils.py +164 -0
- chia/wallet/cat_wallet/cat_wallet.py +855 -0
- chia/wallet/cat_wallet/dao_cat_info.py +28 -0
- chia/wallet/cat_wallet/dao_cat_wallet.py +669 -0
- chia/wallet/cat_wallet/lineage_store.py +74 -0
- chia/wallet/cat_wallet/puzzles/__init__.py +0 -0
- chia/wallet/cat_wallet/puzzles/cat_truths.clib +31 -0
- chia/wallet/cat_wallet/puzzles/cat_v2.clsp +397 -0
- chia/wallet/cat_wallet/puzzles/cat_v2.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/delegated_tail.clsp +25 -0
- chia/wallet/cat_wallet/puzzles/delegated_tail.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp +15 -0
- chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp +26 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp +42 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp +24 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp.hex +1 -0
- chia/wallet/coin_selection.py +188 -0
- chia/wallet/conditions.py +1512 -0
- chia/wallet/dao_wallet/__init__.py +0 -0
- chia/wallet/dao_wallet/dao_info.py +61 -0
- chia/wallet/dao_wallet/dao_utils.py +811 -0
- chia/wallet/dao_wallet/dao_wallet.py +2119 -0
- chia/wallet/db_wallet/__init__.py +0 -0
- chia/wallet/db_wallet/db_wallet_puzzles.py +111 -0
- chia/wallet/derivation_record.py +30 -0
- chia/wallet/derive_keys.py +146 -0
- chia/wallet/did_wallet/__init__.py +0 -0
- chia/wallet/did_wallet/did_info.py +39 -0
- chia/wallet/did_wallet/did_wallet.py +1494 -0
- chia/wallet/did_wallet/did_wallet_puzzles.py +221 -0
- chia/wallet/did_wallet/puzzles/__init__.py +0 -0
- chia/wallet/did_wallet/puzzles/did_innerpuz.clsp +135 -0
- chia/wallet/did_wallet/puzzles/did_innerpuz.clsp.hex +1 -0
- chia/wallet/driver_protocol.py +26 -0
- chia/wallet/key_val_store.py +55 -0
- chia/wallet/lineage_proof.py +58 -0
- chia/wallet/nft_wallet/__init__.py +0 -0
- chia/wallet/nft_wallet/metadata_outer_puzzle.py +92 -0
- chia/wallet/nft_wallet/nft_info.py +120 -0
- chia/wallet/nft_wallet/nft_puzzles.py +305 -0
- chia/wallet/nft_wallet/nft_wallet.py +1687 -0
- chia/wallet/nft_wallet/ownership_outer_puzzle.py +101 -0
- chia/wallet/nft_wallet/puzzles/__init__.py +0 -0
- chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp +6 -0
- chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp +6 -0
- chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp +30 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp +28 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp +100 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp +78 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp +74 -0
- chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp.hex +1 -0
- chia/wallet/nft_wallet/singleton_outer_puzzle.py +101 -0
- chia/wallet/nft_wallet/transfer_program_puzzle.py +82 -0
- chia/wallet/nft_wallet/uncurry_nft.py +217 -0
- chia/wallet/notification_manager.py +117 -0
- chia/wallet/notification_store.py +178 -0
- chia/wallet/outer_puzzles.py +84 -0
- chia/wallet/payment.py +33 -0
- chia/wallet/puzzle_drivers.py +118 -0
- chia/wallet/puzzles/__init__.py +0 -0
- chia/wallet/puzzles/augmented_condition.clsp +13 -0
- chia/wallet/puzzles/augmented_condition.clsp.hex +1 -0
- chia/wallet/puzzles/clawback/__init__.py +0 -0
- chia/wallet/puzzles/clawback/drivers.py +188 -0
- chia/wallet/puzzles/clawback/metadata.py +38 -0
- chia/wallet/puzzles/clawback/puzzle_decorator.py +67 -0
- chia/wallet/puzzles/condition_codes.clib +77 -0
- chia/wallet/puzzles/curry-and-treehash.clib +102 -0
- chia/wallet/puzzles/curry.clib +135 -0
- chia/wallet/puzzles/curry_by_index.clib +16 -0
- chia/wallet/puzzles/dao_cat_eve.clsp +17 -0
- chia/wallet/puzzles/dao_cat_eve.clsp.hex +1 -0
- chia/wallet/puzzles/dao_cat_launcher.clsp +36 -0
- chia/wallet/puzzles/dao_cat_launcher.clsp.hex +1 -0
- chia/wallet/puzzles/dao_finished_state.clsp +35 -0
- chia/wallet/puzzles/dao_finished_state.clsp.hex +1 -0
- chia/wallet/puzzles/dao_finished_state.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_lockup.clsp +288 -0
- chia/wallet/puzzles/dao_lockup.clsp.hex +1 -0
- chia/wallet/puzzles/dao_lockup.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_proposal.clsp +377 -0
- chia/wallet/puzzles/dao_proposal.clsp.hex +1 -0
- chia/wallet/puzzles/dao_proposal.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_proposal_timer.clsp +78 -0
- chia/wallet/puzzles/dao_proposal_timer.clsp.hex +1 -0
- chia/wallet/puzzles/dao_proposal_timer.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_proposal_validator.clsp +87 -0
- chia/wallet/puzzles/dao_proposal_validator.clsp.hex +1 -0
- chia/wallet/puzzles/dao_proposal_validator.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp +240 -0
- chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex +1 -0
- chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_treasury.clsp +115 -0
- chia/wallet/puzzles/dao_treasury.clsp.hex +1 -0
- chia/wallet/puzzles/dao_update_proposal.clsp +44 -0
- chia/wallet/puzzles/dao_update_proposal.clsp.hex +1 -0
- chia/wallet/puzzles/deployed_puzzle_hashes.json +67 -0
- chia/wallet/puzzles/json.clib +25 -0
- chia/wallet/puzzles/load_clvm.py +161 -0
- chia/wallet/puzzles/merkle_utils.clib +18 -0
- chia/wallet/puzzles/notification.clsp +7 -0
- chia/wallet/puzzles/notification.clsp.hex +1 -0
- chia/wallet/puzzles/p2_1_of_n.clsp +22 -0
- chia/wallet/puzzles/p2_1_of_n.clsp.hex +1 -0
- chia/wallet/puzzles/p2_conditions.clsp +3 -0
- chia/wallet/puzzles/p2_conditions.clsp.hex +1 -0
- chia/wallet/puzzles/p2_conditions.py +26 -0
- chia/wallet/puzzles/p2_delegated_conditions.clsp +18 -0
- chia/wallet/puzzles/p2_delegated_conditions.clsp.hex +1 -0
- chia/wallet/puzzles/p2_delegated_conditions.py +21 -0
- chia/wallet/puzzles/p2_delegated_puzzle.clsp +19 -0
- chia/wallet/puzzles/p2_delegated_puzzle.clsp.hex +1 -0
- chia/wallet/puzzles/p2_delegated_puzzle.py +34 -0
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp +91 -0
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp.hex +1 -0
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +160 -0
- chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp +108 -0
- chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp.hex +1 -0
- chia/wallet/puzzles/p2_m_of_n_delegate_direct.py +21 -0
- chia/wallet/puzzles/p2_parent.clsp +19 -0
- chia/wallet/puzzles/p2_parent.clsp.hex +1 -0
- chia/wallet/puzzles/p2_puzzle_hash.clsp +18 -0
- chia/wallet/puzzles/p2_puzzle_hash.clsp.hex +1 -0
- chia/wallet/puzzles/p2_puzzle_hash.py +27 -0
- chia/wallet/puzzles/p2_singleton.clsp +30 -0
- chia/wallet/puzzles/p2_singleton.clsp.hex +1 -0
- chia/wallet/puzzles/p2_singleton_aggregator.clsp +81 -0
- chia/wallet/puzzles/p2_singleton_aggregator.clsp.hex +1 -0
- chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp +50 -0
- chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp.hex +1 -0
- chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp +47 -0
- chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp.hex +1 -0
- chia/wallet/puzzles/puzzle_utils.py +34 -0
- chia/wallet/puzzles/settlement_payments.clsp +49 -0
- chia/wallet/puzzles/settlement_payments.clsp.hex +1 -0
- chia/wallet/puzzles/sha256tree.clib +11 -0
- chia/wallet/puzzles/singleton_launcher.clsp +16 -0
- chia/wallet/puzzles/singleton_launcher.clsp.hex +1 -0
- chia/wallet/puzzles/singleton_top_layer.clsp +177 -0
- chia/wallet/puzzles/singleton_top_layer.clsp.hex +1 -0
- chia/wallet/puzzles/singleton_top_layer.py +296 -0
- chia/wallet/puzzles/singleton_top_layer_v1_1.clsp +107 -0
- chia/wallet/puzzles/singleton_top_layer_v1_1.clsp.hex +1 -0
- chia/wallet/puzzles/singleton_top_layer_v1_1.py +345 -0
- chia/wallet/puzzles/singleton_truths.clib +21 -0
- chia/wallet/puzzles/tails.py +348 -0
- chia/wallet/puzzles/utility_macros.clib +48 -0
- chia/wallet/signer_protocol.py +125 -0
- chia/wallet/singleton.py +106 -0
- chia/wallet/singleton_record.py +30 -0
- chia/wallet/trade_manager.py +1102 -0
- chia/wallet/trade_record.py +67 -0
- chia/wallet/trading/__init__.py +0 -0
- chia/wallet/trading/offer.py +702 -0
- chia/wallet/trading/trade_status.py +13 -0
- chia/wallet/trading/trade_store.py +526 -0
- chia/wallet/transaction_record.py +158 -0
- chia/wallet/transaction_sorting.py +14 -0
- chia/wallet/uncurried_puzzle.py +17 -0
- chia/wallet/util/__init__.py +0 -0
- chia/wallet/util/address_type.py +55 -0
- chia/wallet/util/blind_signer_tl.py +164 -0
- chia/wallet/util/clvm_streamable.py +203 -0
- chia/wallet/util/compute_hints.py +66 -0
- chia/wallet/util/compute_memos.py +43 -0
- chia/wallet/util/curry_and_treehash.py +91 -0
- chia/wallet/util/debug_spend_bundle.py +232 -0
- chia/wallet/util/merkle_tree.py +100 -0
- chia/wallet/util/merkle_utils.py +102 -0
- chia/wallet/util/new_peak_queue.py +82 -0
- chia/wallet/util/notifications.py +12 -0
- chia/wallet/util/peer_request_cache.py +174 -0
- chia/wallet/util/pprint.py +39 -0
- chia/wallet/util/puzzle_compression.py +95 -0
- chia/wallet/util/puzzle_decorator.py +100 -0
- chia/wallet/util/puzzle_decorator_type.py +7 -0
- chia/wallet/util/query_filter.py +59 -0
- chia/wallet/util/transaction_type.py +23 -0
- chia/wallet/util/tx_config.py +158 -0
- chia/wallet/util/wallet_sync_utils.py +351 -0
- chia/wallet/util/wallet_types.py +72 -0
- chia/wallet/vc_wallet/__init__.py +0 -0
- chia/wallet/vc_wallet/cr_cat_drivers.py +664 -0
- chia/wallet/vc_wallet/cr_cat_wallet.py +877 -0
- chia/wallet/vc_wallet/cr_outer_puzzle.py +102 -0
- chia/wallet/vc_wallet/cr_puzzles/__init__.py +0 -0
- chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp +3 -0
- chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp.hex +1 -0
- chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp +304 -0
- chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp.hex +1 -0
- chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp +45 -0
- chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_drivers.py +838 -0
- chia/wallet/vc_wallet/vc_puzzles/__init__.py +0 -0
- chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp +30 -0
- chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp +75 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp +32 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp +80 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp +163 -0
- chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp +16 -0
- chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp +74 -0
- chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp +23 -0
- chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp +64 -0
- chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_store.py +263 -0
- chia/wallet/vc_wallet/vc_wallet.py +638 -0
- chia/wallet/wallet.py +698 -0
- chia/wallet/wallet_action_scope.py +96 -0
- chia/wallet/wallet_blockchain.py +244 -0
- chia/wallet/wallet_coin_record.py +72 -0
- chia/wallet/wallet_coin_store.py +351 -0
- chia/wallet/wallet_info.py +35 -0
- chia/wallet/wallet_interested_store.py +188 -0
- chia/wallet/wallet_nft_store.py +279 -0
- chia/wallet/wallet_node.py +1765 -0
- chia/wallet/wallet_node_api.py +207 -0
- chia/wallet/wallet_pool_store.py +119 -0
- chia/wallet/wallet_protocol.py +90 -0
- chia/wallet/wallet_puzzle_store.py +396 -0
- chia/wallet/wallet_retry_store.py +70 -0
- chia/wallet/wallet_singleton_store.py +259 -0
- chia/wallet/wallet_spend_bundle.py +25 -0
- chia/wallet/wallet_state_manager.py +2819 -0
- chia/wallet/wallet_transaction_store.py +496 -0
- chia/wallet/wallet_user_store.py +110 -0
- chia/wallet/wallet_weight_proof_handler.py +126 -0
- chia_blockchain-2.5.1rc1.dist-info/LICENSE +201 -0
- chia_blockchain-2.5.1rc1.dist-info/METADATA +156 -0
- chia_blockchain-2.5.1rc1.dist-info/RECORD +1042 -0
- chia_blockchain-2.5.1rc1.dist-info/WHEEL +4 -0
- chia_blockchain-2.5.1rc1.dist-info/entry_points.txt +17 -0
- mozilla-ca/cacert.pem +3611 -0
|
@@ -0,0 +1,1687 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import math
|
|
7
|
+
import time
|
|
8
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypeVar, cast
|
|
9
|
+
|
|
10
|
+
from chia_rs import AugSchemeMPL, G1Element, G2Element
|
|
11
|
+
from clvm.casts import int_from_bytes, int_to_bytes
|
|
12
|
+
from typing_extensions import Unpack
|
|
13
|
+
|
|
14
|
+
import chia.server.api_protocol
|
|
15
|
+
import chia.wallet.singleton
|
|
16
|
+
from chia.protocols.wallet_protocol import CoinState
|
|
17
|
+
from chia.server.ws_connection import WSChiaConnection
|
|
18
|
+
from chia.types.blockchain_format.coin import Coin
|
|
19
|
+
from chia.types.blockchain_format.program import Program
|
|
20
|
+
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
21
|
+
from chia.types.coin_spend import CoinSpend, compute_additions, make_spend
|
|
22
|
+
from chia.types.signing_mode import CHIP_0002_SIGN_MESSAGE_PREFIX, SigningMode
|
|
23
|
+
from chia.util.hash import std_hash
|
|
24
|
+
from chia.util.ints import uint16, uint32, uint64, uint128
|
|
25
|
+
from chia.wallet.conditions import (
|
|
26
|
+
AssertCoinAnnouncement,
|
|
27
|
+
AssertPuzzleAnnouncement,
|
|
28
|
+
Condition,
|
|
29
|
+
CreateCoinAnnouncement,
|
|
30
|
+
CreatePuzzleAnnouncement,
|
|
31
|
+
UnknownCondition,
|
|
32
|
+
parse_timelock_info,
|
|
33
|
+
)
|
|
34
|
+
from chia.wallet.derivation_record import DerivationRecord
|
|
35
|
+
from chia.wallet.did_wallet import did_wallet_puzzles
|
|
36
|
+
from chia.wallet.did_wallet.did_info import DIDInfo
|
|
37
|
+
from chia.wallet.lineage_proof import LineageProof
|
|
38
|
+
from chia.wallet.nft_wallet import nft_puzzles
|
|
39
|
+
from chia.wallet.nft_wallet.nft_info import NFTCoinInfo, NFTWalletInfo
|
|
40
|
+
from chia.wallet.nft_wallet.nft_puzzles import NFT_METADATA_UPDATER, create_ownership_layer_puzzle, get_metadata_and_phs
|
|
41
|
+
from chia.wallet.nft_wallet.uncurry_nft import NFTCoinData, UncurriedNFT
|
|
42
|
+
from chia.wallet.outer_puzzles import AssetType, construct_puzzle, match_puzzle, solve_puzzle
|
|
43
|
+
from chia.wallet.payment import Payment
|
|
44
|
+
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
|
|
45
|
+
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
|
|
46
|
+
DEFAULT_HIDDEN_PUZZLE_HASH,
|
|
47
|
+
calculate_synthetic_secret_key,
|
|
48
|
+
puzzle_for_pk,
|
|
49
|
+
)
|
|
50
|
+
from chia.wallet.trading.offer import OFFER_MOD, OFFER_MOD_HASH, NotarizedPayment, Offer
|
|
51
|
+
from chia.wallet.transaction_record import TransactionRecord
|
|
52
|
+
from chia.wallet.uncurried_puzzle import uncurry_puzzle
|
|
53
|
+
from chia.wallet.util.compute_memos import compute_memos
|
|
54
|
+
from chia.wallet.util.transaction_type import TransactionType
|
|
55
|
+
from chia.wallet.util.wallet_types import WalletType
|
|
56
|
+
from chia.wallet.wallet import Wallet
|
|
57
|
+
from chia.wallet.wallet_action_scope import WalletActionScope
|
|
58
|
+
from chia.wallet.wallet_coin_record import WalletCoinRecord
|
|
59
|
+
from chia.wallet.wallet_info import WalletInfo
|
|
60
|
+
from chia.wallet.wallet_nft_store import WalletNftStore
|
|
61
|
+
from chia.wallet.wallet_protocol import GSTOptionalArgs, WalletProtocol
|
|
62
|
+
from chia.wallet.wallet_spend_bundle import WalletSpendBundle
|
|
63
|
+
|
|
64
|
+
_T_NFTWallet = TypeVar("_T_NFTWallet", bound="NFTWallet")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class NFTWallet:
|
|
68
|
+
if TYPE_CHECKING:
|
|
69
|
+
_protocol_check: ClassVar[WalletProtocol[NFTCoinData]] = cast("NFTWallet", None)
|
|
70
|
+
|
|
71
|
+
wallet_state_manager: Any
|
|
72
|
+
log: logging.Logger
|
|
73
|
+
wallet_info: WalletInfo
|
|
74
|
+
nft_wallet_info: NFTWalletInfo
|
|
75
|
+
standard_wallet: Wallet
|
|
76
|
+
wallet_id: int
|
|
77
|
+
nft_store: WalletNftStore
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def did_id(self) -> Optional[bytes32]:
|
|
81
|
+
return self.nft_wallet_info.did_id
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
async def create_new_nft_wallet(
|
|
85
|
+
cls: type[_T_NFTWallet],
|
|
86
|
+
wallet_state_manager: Any,
|
|
87
|
+
wallet: Wallet,
|
|
88
|
+
did_id: Optional[bytes32] = None,
|
|
89
|
+
name: Optional[str] = None,
|
|
90
|
+
) -> _T_NFTWallet:
|
|
91
|
+
"""
|
|
92
|
+
This must be called under the wallet state manager lock
|
|
93
|
+
"""
|
|
94
|
+
self = cls()
|
|
95
|
+
self.standard_wallet = wallet
|
|
96
|
+
if name is None:
|
|
97
|
+
name = "NFT Wallet"
|
|
98
|
+
self.log = logging.getLogger(name if name else __name__)
|
|
99
|
+
self.wallet_state_manager = wallet_state_manager
|
|
100
|
+
self.nft_wallet_info = NFTWalletInfo(did_id)
|
|
101
|
+
info_as_string = json.dumps(self.nft_wallet_info.to_json_dict())
|
|
102
|
+
self.wallet_info = await wallet_state_manager.user_store.create_wallet(
|
|
103
|
+
name, uint32(WalletType.NFT.value), info_as_string
|
|
104
|
+
)
|
|
105
|
+
self.wallet_id = self.wallet_info.id
|
|
106
|
+
self.nft_store = wallet_state_manager.nft_store
|
|
107
|
+
self.log.debug("NFT wallet id: %r and standard wallet id: %r", self.wallet_id, self.standard_wallet.wallet_id)
|
|
108
|
+
|
|
109
|
+
await self.wallet_state_manager.add_new_wallet(self)
|
|
110
|
+
self.log.debug("Generated a new NFT wallet: %s", self.__dict__)
|
|
111
|
+
return self
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
async def create(
|
|
115
|
+
cls: type[_T_NFTWallet],
|
|
116
|
+
wallet_state_manager: Any,
|
|
117
|
+
wallet: Wallet,
|
|
118
|
+
wallet_info: WalletInfo,
|
|
119
|
+
name: Optional[str] = None,
|
|
120
|
+
) -> _T_NFTWallet:
|
|
121
|
+
self = cls()
|
|
122
|
+
self.log = logging.getLogger(name if name else __name__)
|
|
123
|
+
self.wallet_state_manager = wallet_state_manager
|
|
124
|
+
self.wallet_info = wallet_info
|
|
125
|
+
self.wallet_id = wallet_info.id
|
|
126
|
+
self.standard_wallet = wallet
|
|
127
|
+
self.wallet_info = wallet_info
|
|
128
|
+
self.nft_store = wallet_state_manager.nft_store
|
|
129
|
+
self.nft_wallet_info = NFTWalletInfo.from_json_dict(json.loads(wallet_info.data))
|
|
130
|
+
return self
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def type(cls) -> WalletType:
|
|
134
|
+
return WalletType.NFT
|
|
135
|
+
|
|
136
|
+
def id(self) -> uint32:
|
|
137
|
+
return self.wallet_info.id
|
|
138
|
+
|
|
139
|
+
def get_did(self) -> Optional[bytes32]:
|
|
140
|
+
return self.did_id
|
|
141
|
+
|
|
142
|
+
async def get_confirmed_balance(self, record_list: Optional[set[WalletCoinRecord]] = None) -> uint128:
|
|
143
|
+
"""The NFT wallet doesn't really have a balance."""
|
|
144
|
+
return uint128(0)
|
|
145
|
+
|
|
146
|
+
async def get_unconfirmed_balance(self, record_list: Optional[set[WalletCoinRecord]] = None) -> uint128:
|
|
147
|
+
"""The NFT wallet doesn't really have a balance."""
|
|
148
|
+
return uint128(0)
|
|
149
|
+
|
|
150
|
+
async def get_spendable_balance(self, unspent_records: Optional[set[WalletCoinRecord]] = None) -> uint128:
|
|
151
|
+
"""The NFT wallet doesn't really have a balance."""
|
|
152
|
+
return uint128(0)
|
|
153
|
+
|
|
154
|
+
async def get_pending_change_balance(self) -> uint64:
|
|
155
|
+
return uint64(0)
|
|
156
|
+
|
|
157
|
+
async def get_max_send_amount(self, records: Optional[set[WalletCoinRecord]] = None) -> uint128:
|
|
158
|
+
"""This is the confirmed balance, which we set to 0 as the NFT wallet doesn't have one."""
|
|
159
|
+
return uint128(0)
|
|
160
|
+
|
|
161
|
+
async def get_nft_coin_by_id(self, nft_coin_id: bytes32) -> NFTCoinInfo:
|
|
162
|
+
nft_coin = await self.nft_store.get_nft_by_coin_id(nft_coin_id)
|
|
163
|
+
if nft_coin is None:
|
|
164
|
+
raise KeyError(f"Couldn't find coin with id: {nft_coin_id}")
|
|
165
|
+
return nft_coin
|
|
166
|
+
|
|
167
|
+
async def coin_added(
|
|
168
|
+
self, coin: Coin, height: uint32, peer: WSChiaConnection, parent_coin_data: Optional[NFTCoinData]
|
|
169
|
+
) -> None:
|
|
170
|
+
"""Notification from wallet state manager that wallet has been received."""
|
|
171
|
+
self.log.info(f"NFT wallet %s has been notified that {coin} was added", self.get_name())
|
|
172
|
+
if await self.nft_store.exists(coin.name()):
|
|
173
|
+
# already added
|
|
174
|
+
return
|
|
175
|
+
assert isinstance(parent_coin_data, NFTCoinData), f"Invalid NFT coin data: {parent_coin_data}"
|
|
176
|
+
await self.puzzle_solution_received(coin, parent_coin_data, peer)
|
|
177
|
+
|
|
178
|
+
async def puzzle_solution_received(self, coin: Coin, data: NFTCoinData, peer: WSChiaConnection) -> None:
|
|
179
|
+
self.log.debug("Puzzle solution received to wallet: %s", self.wallet_info)
|
|
180
|
+
# At this point, the puzzle must be a NFT puzzle.
|
|
181
|
+
# This method will be called only when the wallet state manager uncurried this coin as a NFT puzzle.
|
|
182
|
+
|
|
183
|
+
uncurried_nft: UncurriedNFT = data.uncurried_nft
|
|
184
|
+
self.log.debug(
|
|
185
|
+
"found the info for NFT coin %s %s %s",
|
|
186
|
+
coin.name().hex(),
|
|
187
|
+
uncurried_nft.inner_puzzle,
|
|
188
|
+
uncurried_nft.singleton_struct,
|
|
189
|
+
)
|
|
190
|
+
singleton_id = uncurried_nft.singleton_launcher_id
|
|
191
|
+
parent_inner_puzhash = uncurried_nft.nft_state_layer.get_tree_hash()
|
|
192
|
+
metadata, p2_puzzle_hash = get_metadata_and_phs(uncurried_nft, data.parent_coin_spend.solution)
|
|
193
|
+
self.log.debug("Got back puzhash from solution: %s", p2_puzzle_hash)
|
|
194
|
+
self.log.debug("Got back updated metadata: %s", metadata)
|
|
195
|
+
derivation_record: Optional[
|
|
196
|
+
DerivationRecord
|
|
197
|
+
] = await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(p2_puzzle_hash)
|
|
198
|
+
self.log.debug("Record for %s is: %s", p2_puzzle_hash, derivation_record)
|
|
199
|
+
if derivation_record is None:
|
|
200
|
+
self.log.debug("Not our NFT, pointing to %s, skipping", p2_puzzle_hash)
|
|
201
|
+
return
|
|
202
|
+
p2_puzzle = puzzle_for_pk(derivation_record.pubkey)
|
|
203
|
+
launcher_coin_states: list[CoinState] = await self.wallet_state_manager.wallet_node.get_coin_state(
|
|
204
|
+
[singleton_id], peer=peer
|
|
205
|
+
)
|
|
206
|
+
assert (
|
|
207
|
+
launcher_coin_states is not None
|
|
208
|
+
and len(launcher_coin_states) == 1
|
|
209
|
+
and launcher_coin_states[0].spent_height is not None
|
|
210
|
+
)
|
|
211
|
+
mint_height: uint32 = uint32(launcher_coin_states[0].spent_height)
|
|
212
|
+
minter_did = None
|
|
213
|
+
if uncurried_nft.supports_did:
|
|
214
|
+
inner_puzzle = nft_puzzles.recurry_nft_puzzle(
|
|
215
|
+
uncurried_nft, data.parent_coin_spend.solution.to_program(), p2_puzzle
|
|
216
|
+
)
|
|
217
|
+
minter_did = await self.wallet_state_manager.get_minter_did(launcher_coin_states[0].coin, peer)
|
|
218
|
+
else:
|
|
219
|
+
inner_puzzle = p2_puzzle
|
|
220
|
+
child_puzzle: Program = nft_puzzles.create_full_puzzle(
|
|
221
|
+
singleton_id,
|
|
222
|
+
Program.to(metadata),
|
|
223
|
+
bytes32(uncurried_nft.metadata_updater_hash.as_atom()),
|
|
224
|
+
inner_puzzle,
|
|
225
|
+
)
|
|
226
|
+
self.log.debug(
|
|
227
|
+
"Created NFT full puzzle with inner: %s",
|
|
228
|
+
nft_puzzles.create_full_puzzle_with_nft_puzzle(singleton_id, uncurried_nft.inner_puzzle),
|
|
229
|
+
)
|
|
230
|
+
child_puzzle_hash = child_puzzle.get_tree_hash()
|
|
231
|
+
for new_coin in compute_additions(data.parent_coin_spend):
|
|
232
|
+
self.log.debug(
|
|
233
|
+
"Comparing addition: %s with %s, amount: %s ",
|
|
234
|
+
new_coin.puzzle_hash,
|
|
235
|
+
child_puzzle_hash,
|
|
236
|
+
new_coin.amount,
|
|
237
|
+
)
|
|
238
|
+
if new_coin.puzzle_hash == child_puzzle_hash:
|
|
239
|
+
child_coin = new_coin
|
|
240
|
+
break
|
|
241
|
+
else:
|
|
242
|
+
raise ValueError("Couldn't generate child puzzle for NFT")
|
|
243
|
+
|
|
244
|
+
self.log.info("Adding a new NFT to wallet: %s", child_coin)
|
|
245
|
+
# all is well, lets add NFT to our local db
|
|
246
|
+
parent_coin = data.parent_coin_state.coin
|
|
247
|
+
confirmed_height = (
|
|
248
|
+
None if data.parent_coin_state.spent_height is None else uint32(data.parent_coin_state.spent_height)
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if confirmed_height is None:
|
|
252
|
+
raise ValueError("Error finding parent")
|
|
253
|
+
|
|
254
|
+
await self.add_coin(
|
|
255
|
+
child_coin,
|
|
256
|
+
singleton_id,
|
|
257
|
+
child_puzzle,
|
|
258
|
+
LineageProof(parent_coin.parent_coin_info, parent_inner_puzhash, uint64(parent_coin.amount)),
|
|
259
|
+
mint_height,
|
|
260
|
+
minter_did,
|
|
261
|
+
confirmed_height,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
async def add_coin(
|
|
265
|
+
self,
|
|
266
|
+
coin: Coin,
|
|
267
|
+
nft_id: bytes32,
|
|
268
|
+
puzzle: Program,
|
|
269
|
+
lineage_proof: LineageProof,
|
|
270
|
+
mint_height: uint32,
|
|
271
|
+
minter_did: Optional[bytes32],
|
|
272
|
+
confirmed_height: uint32,
|
|
273
|
+
) -> None:
|
|
274
|
+
new_nft = NFTCoinInfo(nft_id, coin, lineage_proof, puzzle, mint_height, minter_did, confirmed_height)
|
|
275
|
+
await self.wallet_state_manager.nft_store.save_nft(self.id(), self.get_did(), new_nft)
|
|
276
|
+
await self.wallet_state_manager.add_interested_coin_ids([coin.name()])
|
|
277
|
+
self.wallet_state_manager.state_changed("nft_coin_added", self.wallet_info.id)
|
|
278
|
+
|
|
279
|
+
async def remove_coin(self, coin: Coin, height: uint32) -> None:
|
|
280
|
+
nft_coin_info = await self.nft_store.get_nft_by_coin_id(coin.name())
|
|
281
|
+
if nft_coin_info:
|
|
282
|
+
await self.nft_store.delete_nft_by_coin_id(coin.name(), height)
|
|
283
|
+
self.wallet_state_manager.state_changed("nft_coin_removed", self.wallet_info.id)
|
|
284
|
+
num = await self.get_nft_count()
|
|
285
|
+
if num == 0 and self.did_id is not None:
|
|
286
|
+
# Check if the wallet owns the DID
|
|
287
|
+
for did_wallet in await self.wallet_state_manager.get_all_wallet_info_entries(
|
|
288
|
+
wallet_type=WalletType.DECENTRALIZED_ID
|
|
289
|
+
):
|
|
290
|
+
did_wallet_info: DIDInfo = DIDInfo.from_json_dict(json.loads(did_wallet.data))
|
|
291
|
+
assert did_wallet_info.origin_coin is not None
|
|
292
|
+
if did_wallet_info.origin_coin.name() == self.did_id:
|
|
293
|
+
return
|
|
294
|
+
self.log.info(f"No NFT, deleting wallet {self.wallet_info.name} ...")
|
|
295
|
+
await self.wallet_state_manager.delete_wallet(self.wallet_info.id)
|
|
296
|
+
self.wallet_state_manager.wallets.pop(self.wallet_info.id)
|
|
297
|
+
else:
|
|
298
|
+
self.log.info("Tried removing NFT coin that doesn't exist: %s", coin.name())
|
|
299
|
+
|
|
300
|
+
async def get_did_approval_info(
|
|
301
|
+
self,
|
|
302
|
+
nft_ids: list[bytes32],
|
|
303
|
+
action_scope: WalletActionScope,
|
|
304
|
+
did_id: Optional[bytes32] = None,
|
|
305
|
+
) -> bytes32:
|
|
306
|
+
"""Get DID spend with announcement created we need to transfer NFT with did with current inner hash of DID
|
|
307
|
+
|
|
308
|
+
We also store `did_id` and then iterate to find the did wallet as we'd otherwise have to subscribe to
|
|
309
|
+
any changes to DID wallet and storing wallet_id is not guaranteed to be consistent on wallet crash/reset.
|
|
310
|
+
"""
|
|
311
|
+
if did_id is None:
|
|
312
|
+
did_id = self.did_id
|
|
313
|
+
did_inner_hash: bytes32
|
|
314
|
+
for _, wallet in self.wallet_state_manager.wallets.items():
|
|
315
|
+
self.log.debug("Checking wallet type %s", wallet.type())
|
|
316
|
+
if wallet.type() == WalletType.DECENTRALIZED_ID:
|
|
317
|
+
self.log.debug("Found a DID wallet, checking did: %r == %r", wallet.get_my_DID(), did_id)
|
|
318
|
+
if bytes32.fromhex(wallet.get_my_DID()) == did_id:
|
|
319
|
+
self.log.debug("Creating announcement from DID for nft_ids: %s", nft_ids)
|
|
320
|
+
await wallet.create_message_spend(
|
|
321
|
+
action_scope, extra_conditions=(CreatePuzzleAnnouncement(id) for id in nft_ids)
|
|
322
|
+
)
|
|
323
|
+
did_inner_hash = wallet.did_info.current_inner.get_tree_hash()
|
|
324
|
+
break
|
|
325
|
+
else:
|
|
326
|
+
raise ValueError(f"Missing DID Wallet for did_id: {did_id}")
|
|
327
|
+
return did_inner_hash
|
|
328
|
+
|
|
329
|
+
async def generate_new_nft(
|
|
330
|
+
self,
|
|
331
|
+
metadata: Program,
|
|
332
|
+
action_scope: WalletActionScope,
|
|
333
|
+
target_puzzle_hash: Optional[bytes32] = None,
|
|
334
|
+
royalty_puzzle_hash: Optional[bytes32] = None,
|
|
335
|
+
percentage: uint16 = uint16(0),
|
|
336
|
+
did_id: Optional[bytes] = None,
|
|
337
|
+
fee: uint64 = uint64(0),
|
|
338
|
+
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
339
|
+
) -> bytes32:
|
|
340
|
+
"""
|
|
341
|
+
This must be called under the wallet state manager lock
|
|
342
|
+
"""
|
|
343
|
+
if self.did_id is not None and did_id is None:
|
|
344
|
+
# For a DID enabled NFT wallet it cannot mint NFT0. Mint NFT1 instead.
|
|
345
|
+
did_id = self.did_id
|
|
346
|
+
amount = uint64(1)
|
|
347
|
+
# ensure percentage is uint16
|
|
348
|
+
try:
|
|
349
|
+
percentage = uint16(percentage)
|
|
350
|
+
except ValueError:
|
|
351
|
+
raise ValueError("Percentage must be lower than 655%")
|
|
352
|
+
coins = await self.standard_wallet.select_coins(uint64(amount + fee), action_scope)
|
|
353
|
+
if coins is None:
|
|
354
|
+
return None
|
|
355
|
+
origin = coins.copy().pop()
|
|
356
|
+
genesis_launcher_puz = nft_puzzles.LAUNCHER_PUZZLE
|
|
357
|
+
# nft_id == singleton_id == launcher_id == launcher_coin.name()
|
|
358
|
+
launcher_coin = Coin(origin.name(), nft_puzzles.LAUNCHER_PUZZLE_HASH, uint64(amount))
|
|
359
|
+
self.log.debug("Generating NFT with launcher coin %s and metadata: %s", launcher_coin, metadata)
|
|
360
|
+
|
|
361
|
+
p2_inner_puzzle = await self.standard_wallet.get_puzzle(new=not action_scope.config.tx_config.reuse_puzhash)
|
|
362
|
+
if not target_puzzle_hash:
|
|
363
|
+
target_puzzle_hash = p2_inner_puzzle.get_tree_hash()
|
|
364
|
+
self.log.debug("Attempt to generate a new NFT to %s", target_puzzle_hash.hex())
|
|
365
|
+
if did_id is not None:
|
|
366
|
+
self.log.debug("Creating provenant NFT")
|
|
367
|
+
# eve coin DID can be set to whatever so we keep it empty
|
|
368
|
+
# WARNING: wallets should always ignore DID value for eve coins as they can be set
|
|
369
|
+
# to any DID without approval
|
|
370
|
+
inner_puzzle = create_ownership_layer_puzzle(
|
|
371
|
+
launcher_coin.name(), b"", p2_inner_puzzle, percentage, royalty_puzzle_hash=royalty_puzzle_hash
|
|
372
|
+
)
|
|
373
|
+
self.log.debug("Got back ownership inner puzzle: %s", inner_puzzle)
|
|
374
|
+
else:
|
|
375
|
+
self.log.debug("Creating standard NFT")
|
|
376
|
+
inner_puzzle = p2_inner_puzzle
|
|
377
|
+
|
|
378
|
+
# singleton eve puzzle
|
|
379
|
+
eve_fullpuz = nft_puzzles.create_full_puzzle(
|
|
380
|
+
launcher_coin.name(), metadata, NFT_METADATA_UPDATER.get_tree_hash(), inner_puzzle
|
|
381
|
+
)
|
|
382
|
+
eve_fullpuz_hash = eve_fullpuz.get_tree_hash()
|
|
383
|
+
# launcher announcement
|
|
384
|
+
announcement_message = Program.to([eve_fullpuz_hash, amount, []]).get_tree_hash()
|
|
385
|
+
|
|
386
|
+
self.log.debug(
|
|
387
|
+
"Creating transaction for launcher: %s and other coins: %s (%s)", origin, coins, announcement_message
|
|
388
|
+
)
|
|
389
|
+
# store the launcher transaction in the wallet state
|
|
390
|
+
await self.standard_wallet.generate_signed_transaction(
|
|
391
|
+
uint64(amount),
|
|
392
|
+
nft_puzzles.LAUNCHER_PUZZLE_HASH,
|
|
393
|
+
action_scope,
|
|
394
|
+
fee,
|
|
395
|
+
coins,
|
|
396
|
+
None,
|
|
397
|
+
origin_id=origin.name(),
|
|
398
|
+
extra_conditions=(
|
|
399
|
+
*extra_conditions,
|
|
400
|
+
AssertCoinAnnouncement(asserted_id=launcher_coin.name(), asserted_msg=announcement_message),
|
|
401
|
+
),
|
|
402
|
+
)
|
|
403
|
+
genesis_launcher_solution = Program.to([eve_fullpuz_hash, amount, []])
|
|
404
|
+
|
|
405
|
+
# launcher spend to generate the singleton
|
|
406
|
+
launcher_cs = make_spend(launcher_coin, genesis_launcher_puz, genesis_launcher_solution)
|
|
407
|
+
launcher_sb = WalletSpendBundle([launcher_cs], AugSchemeMPL.aggregate([]))
|
|
408
|
+
|
|
409
|
+
eve_coin = Coin(launcher_coin.name(), eve_fullpuz_hash, uint64(amount))
|
|
410
|
+
|
|
411
|
+
async with action_scope.use() as interface:
|
|
412
|
+
interface.side_effects.extra_spends.append(launcher_sb)
|
|
413
|
+
|
|
414
|
+
# Create inner solution for eve spend
|
|
415
|
+
did_inner_hash = b""
|
|
416
|
+
if did_id is not None:
|
|
417
|
+
if did_id != b"":
|
|
418
|
+
did_inner_hash = await self.get_did_approval_info([launcher_coin.name()], action_scope)
|
|
419
|
+
nft_coin = NFTCoinInfo(
|
|
420
|
+
nft_id=launcher_coin.name(),
|
|
421
|
+
coin=eve_coin,
|
|
422
|
+
lineage_proof=LineageProof(parent_name=launcher_coin.parent_coin_info, amount=uint64(launcher_coin.amount)),
|
|
423
|
+
full_puzzle=eve_fullpuz,
|
|
424
|
+
mint_height=uint32(0),
|
|
425
|
+
minter_did=bytes32(did_id) if did_id is not None and did_id != b"" else None,
|
|
426
|
+
)
|
|
427
|
+
# Don't set fee, it is covered in the tx_record
|
|
428
|
+
await self.generate_signed_transaction(
|
|
429
|
+
[uint64(eve_coin.amount)],
|
|
430
|
+
[target_puzzle_hash],
|
|
431
|
+
action_scope,
|
|
432
|
+
nft_coin=nft_coin,
|
|
433
|
+
new_owner=did_id,
|
|
434
|
+
new_did_inner_hash=did_inner_hash,
|
|
435
|
+
memos=[[target_puzzle_hash]],
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
return launcher_coin.name()
|
|
439
|
+
|
|
440
|
+
async def update_metadata(
|
|
441
|
+
self,
|
|
442
|
+
nft_coin_info: NFTCoinInfo,
|
|
443
|
+
key: str,
|
|
444
|
+
uri: str,
|
|
445
|
+
action_scope: WalletActionScope,
|
|
446
|
+
fee: uint64 = uint64(0),
|
|
447
|
+
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
448
|
+
) -> None:
|
|
449
|
+
uncurried_nft = UncurriedNFT.uncurry(*nft_coin_info.full_puzzle.uncurry())
|
|
450
|
+
assert uncurried_nft is not None
|
|
451
|
+
puzzle_hash = uncurried_nft.p2_puzzle.get_tree_hash()
|
|
452
|
+
|
|
453
|
+
self.log.info(
|
|
454
|
+
"Attempting to add urls to NFT coin %s in the metadata: %s",
|
|
455
|
+
nft_coin_info.coin.name(),
|
|
456
|
+
uncurried_nft.metadata,
|
|
457
|
+
)
|
|
458
|
+
await self.generate_signed_transaction(
|
|
459
|
+
[uint64(nft_coin_info.coin.amount)],
|
|
460
|
+
[puzzle_hash],
|
|
461
|
+
action_scope,
|
|
462
|
+
fee,
|
|
463
|
+
{nft_coin_info.coin},
|
|
464
|
+
metadata_update=(key, uri),
|
|
465
|
+
extra_conditions=extra_conditions,
|
|
466
|
+
)
|
|
467
|
+
await self.update_coin_status(nft_coin_info.coin.name(), True)
|
|
468
|
+
self.wallet_state_manager.state_changed("nft_coin_updated", self.wallet_info.id)
|
|
469
|
+
|
|
470
|
+
async def get_current_nfts(self, start_index: int = 0, count: int = 50) -> list[NFTCoinInfo]:
|
|
471
|
+
return await self.nft_store.get_nft_list(wallet_id=self.id(), start_index=start_index, count=count)
|
|
472
|
+
|
|
473
|
+
async def get_nft_count(self) -> int:
|
|
474
|
+
return await self.nft_store.count(wallet_id=self.id())
|
|
475
|
+
|
|
476
|
+
async def is_empty(self) -> bool:
|
|
477
|
+
return await self.nft_store.is_empty(wallet_id=self.id())
|
|
478
|
+
|
|
479
|
+
async def update_coin_status(self, coin_id: bytes32, pending_transaction: bool) -> None:
|
|
480
|
+
await self.nft_store.update_pending_transaction(coin_id, pending_transaction)
|
|
481
|
+
|
|
482
|
+
async def save_info(self, nft_info: NFTWalletInfo) -> None:
|
|
483
|
+
self.nft_wallet_info = nft_info
|
|
484
|
+
current_info = self.wallet_info
|
|
485
|
+
data_str = json.dumps(nft_info.to_json_dict())
|
|
486
|
+
wallet_info = WalletInfo(current_info.id, current_info.name, current_info.type, data_str)
|
|
487
|
+
self.wallet_info = wallet_info
|
|
488
|
+
await self.wallet_state_manager.user_store.update_wallet(wallet_info)
|
|
489
|
+
|
|
490
|
+
async def convert_puzzle_hash(self, puzhash: bytes32) -> bytes32:
|
|
491
|
+
return puzhash
|
|
492
|
+
|
|
493
|
+
async def get_nft(self, launcher_id: bytes32) -> Optional[NFTCoinInfo]:
|
|
494
|
+
return await self.nft_store.get_nft_by_id(launcher_id)
|
|
495
|
+
|
|
496
|
+
async def get_puzzle_info(self, nft_id: bytes32) -> PuzzleInfo:
|
|
497
|
+
nft_coin: Optional[NFTCoinInfo] = await self.get_nft(nft_id)
|
|
498
|
+
if nft_coin is None:
|
|
499
|
+
raise ValueError("An asset ID was specified that this wallet doesn't track")
|
|
500
|
+
puzzle_info: Optional[PuzzleInfo] = match_puzzle(uncurry_puzzle(nft_coin.full_puzzle))
|
|
501
|
+
if puzzle_info is None:
|
|
502
|
+
raise ValueError("Internal Error: NFT wallet is tracking a non NFT coin")
|
|
503
|
+
else:
|
|
504
|
+
return puzzle_info
|
|
505
|
+
|
|
506
|
+
async def sign_message(self, message: str, nft: NFTCoinInfo, mode: SigningMode) -> tuple[G1Element, G2Element]:
|
|
507
|
+
uncurried_nft = UncurriedNFT.uncurry(*nft.full_puzzle.uncurry())
|
|
508
|
+
if uncurried_nft is not None:
|
|
509
|
+
p2_puzzle = uncurried_nft.p2_puzzle
|
|
510
|
+
puzzle_hash = p2_puzzle.get_tree_hash()
|
|
511
|
+
private = await self.wallet_state_manager.get_private_key(puzzle_hash)
|
|
512
|
+
synthetic_secret_key = calculate_synthetic_secret_key(private, DEFAULT_HIDDEN_PUZZLE_HASH)
|
|
513
|
+
synthetic_pk = synthetic_secret_key.get_g1()
|
|
514
|
+
if mode == SigningMode.CHIP_0002_HEX_INPUT:
|
|
515
|
+
hex_message: bytes = Program.to((CHIP_0002_SIGN_MESSAGE_PREFIX, bytes.fromhex(message))).get_tree_hash()
|
|
516
|
+
elif mode == SigningMode.BLS_MESSAGE_AUGMENTATION_UTF8_INPUT:
|
|
517
|
+
hex_message = bytes(message, "utf-8")
|
|
518
|
+
elif mode == SigningMode.BLS_MESSAGE_AUGMENTATION_HEX_INPUT:
|
|
519
|
+
hex_message = bytes.fromhex(message)
|
|
520
|
+
else:
|
|
521
|
+
hex_message = Program.to((CHIP_0002_SIGN_MESSAGE_PREFIX, message)).get_tree_hash()
|
|
522
|
+
return synthetic_pk, AugSchemeMPL.sign(synthetic_secret_key, hex_message)
|
|
523
|
+
else:
|
|
524
|
+
raise ValueError("Invalid NFT puzzle.")
|
|
525
|
+
|
|
526
|
+
async def get_coins_to_offer(
|
|
527
|
+
self,
|
|
528
|
+
nft_id: bytes32,
|
|
529
|
+
*args: Any,
|
|
530
|
+
**kwargs: Any,
|
|
531
|
+
) -> set[Coin]:
|
|
532
|
+
nft_coin: Optional[NFTCoinInfo] = await self.get_nft(nft_id)
|
|
533
|
+
if nft_coin is None:
|
|
534
|
+
raise ValueError("An asset ID was specified that this wallet doesn't track")
|
|
535
|
+
return {nft_coin.coin}
|
|
536
|
+
|
|
537
|
+
async def match_puzzle_info(self, puzzle_driver: PuzzleInfo) -> bool:
|
|
538
|
+
return (
|
|
539
|
+
AssetType(puzzle_driver.type()) == AssetType.SINGLETON
|
|
540
|
+
and puzzle_driver.also() is not None
|
|
541
|
+
and AssetType(puzzle_driver.also().type()) == AssetType.METADATA # type: ignore
|
|
542
|
+
and puzzle_driver.also().also() is None # type: ignore
|
|
543
|
+
and await self.get_nft(puzzle_driver["launcher_id"]) is not None
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
@classmethod
|
|
547
|
+
async def create_from_puzzle_info(
|
|
548
|
+
cls: Any,
|
|
549
|
+
wallet_state_manager: Any,
|
|
550
|
+
wallet: Wallet,
|
|
551
|
+
puzzle_driver: PuzzleInfo,
|
|
552
|
+
name: Optional[str] = None,
|
|
553
|
+
) -> Any:
|
|
554
|
+
# Off the bat we don't support multiple profile but when we do this will have to change
|
|
555
|
+
for wallet in wallet_state_manager.wallets.values():
|
|
556
|
+
if wallet.type() == WalletType.NFT.value:
|
|
557
|
+
return wallet
|
|
558
|
+
|
|
559
|
+
# TODO: These are not the arguments to this function yet but they will be
|
|
560
|
+
return await cls.create_new_nft_wallet(
|
|
561
|
+
wallet_state_manager,
|
|
562
|
+
wallet,
|
|
563
|
+
None,
|
|
564
|
+
name,
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
async def generate_signed_transaction(
|
|
568
|
+
self,
|
|
569
|
+
amounts: list[uint64],
|
|
570
|
+
puzzle_hashes: list[bytes32],
|
|
571
|
+
action_scope: WalletActionScope,
|
|
572
|
+
fee: uint64 = uint64(0),
|
|
573
|
+
coins: Optional[set[Coin]] = None,
|
|
574
|
+
memos: Optional[list[list[bytes]]] = None,
|
|
575
|
+
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
576
|
+
**kwargs: Unpack[GSTOptionalArgs],
|
|
577
|
+
) -> None:
|
|
578
|
+
nft_coin: Optional[NFTCoinInfo] = kwargs.get("nft_coin", None)
|
|
579
|
+
new_owner: Optional[bytes] = kwargs.get("new_owner", None)
|
|
580
|
+
new_did_inner_hash: Optional[bytes] = kwargs.get("new_did_inner_hash", None)
|
|
581
|
+
trade_prices_list: Optional[Program] = kwargs.get("trade_prices_list", None)
|
|
582
|
+
additional_bundles: list[WalletSpendBundle] = kwargs.get("additional_bundles", [])
|
|
583
|
+
metadata_update: Optional[tuple[str, str]] = kwargs.get("metadata_update", None)
|
|
584
|
+
if memos is None:
|
|
585
|
+
memos = [[] for _ in range(len(puzzle_hashes))]
|
|
586
|
+
|
|
587
|
+
if not (len(memos) == len(puzzle_hashes) == len(amounts)):
|
|
588
|
+
raise ValueError("Memos, puzzle_hashes, and amounts must have the same length")
|
|
589
|
+
|
|
590
|
+
payments = []
|
|
591
|
+
for amount, puzhash, memo_list in zip(amounts, puzzle_hashes, memos):
|
|
592
|
+
memos_with_hint: list[bytes] = [puzhash]
|
|
593
|
+
memos_with_hint.extend(memo_list)
|
|
594
|
+
payments.append(Payment(puzhash, amount, memos_with_hint))
|
|
595
|
+
|
|
596
|
+
payment_sum = sum(p.amount for p in payments)
|
|
597
|
+
unsigned_spend_bundle = await self.generate_unsigned_spendbundle(
|
|
598
|
+
payments,
|
|
599
|
+
action_scope,
|
|
600
|
+
fee,
|
|
601
|
+
coins=coins,
|
|
602
|
+
nft_coin=nft_coin,
|
|
603
|
+
new_owner=new_owner,
|
|
604
|
+
new_did_inner_hash=new_did_inner_hash,
|
|
605
|
+
trade_prices_list=trade_prices_list,
|
|
606
|
+
metadata_update=metadata_update,
|
|
607
|
+
extra_conditions=extra_conditions,
|
|
608
|
+
)
|
|
609
|
+
spend_bundle = WalletSpendBundle.aggregate([unsigned_spend_bundle, *additional_bundles])
|
|
610
|
+
|
|
611
|
+
async with action_scope.use() as interface:
|
|
612
|
+
other_tx_removals: set[Coin] = {
|
|
613
|
+
removal for tx in interface.side_effects.transactions for removal in tx.removals
|
|
614
|
+
}
|
|
615
|
+
other_tx_additions: set[Coin] = {
|
|
616
|
+
addition for tx in interface.side_effects.transactions for addition in tx.additions
|
|
617
|
+
}
|
|
618
|
+
tx = TransactionRecord(
|
|
619
|
+
confirmed_at_height=uint32(0),
|
|
620
|
+
created_at_time=uint64(int(time.time())),
|
|
621
|
+
to_puzzle_hash=puzzle_hashes[0],
|
|
622
|
+
amount=uint64(payment_sum),
|
|
623
|
+
fee_amount=fee,
|
|
624
|
+
confirmed=False,
|
|
625
|
+
sent=uint32(0),
|
|
626
|
+
spend_bundle=spend_bundle,
|
|
627
|
+
additions=list(set(spend_bundle.additions()) - other_tx_additions),
|
|
628
|
+
removals=list(set(spend_bundle.removals()) - other_tx_removals),
|
|
629
|
+
wallet_id=self.id(),
|
|
630
|
+
sent_to=[],
|
|
631
|
+
trade_id=None,
|
|
632
|
+
type=uint32(TransactionType.OUTGOING_TX.value),
|
|
633
|
+
name=spend_bundle.name(),
|
|
634
|
+
memos=list(compute_memos(spend_bundle).items()),
|
|
635
|
+
valid_times=parse_timelock_info(extra_conditions),
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
interface.side_effects.transactions.append(tx)
|
|
639
|
+
|
|
640
|
+
async def generate_unsigned_spendbundle(
|
|
641
|
+
self,
|
|
642
|
+
payments: list[Payment],
|
|
643
|
+
action_scope: WalletActionScope,
|
|
644
|
+
fee: uint64 = uint64(0),
|
|
645
|
+
coins: Optional[set[Coin]] = None,
|
|
646
|
+
new_owner: Optional[bytes] = None,
|
|
647
|
+
new_did_inner_hash: Optional[bytes] = None,
|
|
648
|
+
trade_prices_list: Optional[Program] = None,
|
|
649
|
+
metadata_update: Optional[tuple[str, str]] = None,
|
|
650
|
+
nft_coin: Optional[NFTCoinInfo] = None,
|
|
651
|
+
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
652
|
+
) -> WalletSpendBundle:
|
|
653
|
+
if nft_coin is None:
|
|
654
|
+
if coins is None or not len(coins) == 1:
|
|
655
|
+
# Make sure the user is specifying which specific NFT coin to use
|
|
656
|
+
raise ValueError("NFT spends require a single selected coin")
|
|
657
|
+
elif len(payments) > 1:
|
|
658
|
+
raise ValueError("NFTs can only be sent to one party")
|
|
659
|
+
nft_coin = await self.nft_store.get_nft_by_coin_id(coins.pop().name())
|
|
660
|
+
assert nft_coin
|
|
661
|
+
|
|
662
|
+
coin_name = nft_coin.coin.name()
|
|
663
|
+
if fee > 0:
|
|
664
|
+
await self.standard_wallet.create_tandem_xch_tx(
|
|
665
|
+
fee,
|
|
666
|
+
action_scope,
|
|
667
|
+
extra_conditions=(AssertCoinAnnouncement(asserted_id=coin_name, asserted_msg=coin_name),),
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
unft = UncurriedNFT.uncurry(*nft_coin.full_puzzle.uncurry())
|
|
671
|
+
assert unft is not None
|
|
672
|
+
if unft.supports_did:
|
|
673
|
+
if new_owner is None:
|
|
674
|
+
# If no new owner was specified and we're sending this to ourselves, let's not reset the DID
|
|
675
|
+
derivation_record: Optional[
|
|
676
|
+
DerivationRecord
|
|
677
|
+
] = await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(
|
|
678
|
+
payments[0].puzzle_hash
|
|
679
|
+
)
|
|
680
|
+
if derivation_record is not None:
|
|
681
|
+
new_owner = unft.owner_did
|
|
682
|
+
extra_conditions = (
|
|
683
|
+
*extra_conditions,
|
|
684
|
+
UnknownCondition(
|
|
685
|
+
opcode=Program.to(-10),
|
|
686
|
+
args=[
|
|
687
|
+
Program.to(new_owner),
|
|
688
|
+
Program.to(trade_prices_list),
|
|
689
|
+
Program.to(new_did_inner_hash),
|
|
690
|
+
],
|
|
691
|
+
),
|
|
692
|
+
)
|
|
693
|
+
if metadata_update is not None:
|
|
694
|
+
extra_conditions = (
|
|
695
|
+
*extra_conditions,
|
|
696
|
+
UnknownCondition(
|
|
697
|
+
opcode=Program.to(-24),
|
|
698
|
+
args=[
|
|
699
|
+
NFT_METADATA_UPDATER,
|
|
700
|
+
Program.to(metadata_update),
|
|
701
|
+
],
|
|
702
|
+
),
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
innersol: Program = self.standard_wallet.make_solution(
|
|
706
|
+
primaries=payments,
|
|
707
|
+
conditions=(*extra_conditions, CreateCoinAnnouncement(coin_name)) if fee > 0 else extra_conditions,
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
if unft.supports_did:
|
|
711
|
+
innersol = Program.to([innersol])
|
|
712
|
+
|
|
713
|
+
nft_layer_solution = Program.to([innersol])
|
|
714
|
+
assert isinstance(nft_coin.lineage_proof, LineageProof)
|
|
715
|
+
singleton_solution = Program.to([nft_coin.lineage_proof.to_program(), nft_coin.coin.amount, nft_layer_solution])
|
|
716
|
+
coin_spend = make_spend(nft_coin.coin, nft_coin.full_puzzle, singleton_solution)
|
|
717
|
+
|
|
718
|
+
nft_spend_bundle = WalletSpendBundle([coin_spend], G2Element())
|
|
719
|
+
|
|
720
|
+
return nft_spend_bundle
|
|
721
|
+
|
|
722
|
+
@staticmethod
|
|
723
|
+
def royalty_calculation(
|
|
724
|
+
royalty_assets_dict: dict[Any, tuple[Any, uint16]],
|
|
725
|
+
fungible_asset_dict: dict[Any, uint64],
|
|
726
|
+
) -> dict[Any, list[dict[str, Any]]]:
|
|
727
|
+
summary_dict: dict[Any, list[dict[str, Any]]] = {}
|
|
728
|
+
for id, royalty_info in royalty_assets_dict.items():
|
|
729
|
+
address, percentage = royalty_info
|
|
730
|
+
summary_dict[id] = []
|
|
731
|
+
for name, amount in fungible_asset_dict.items():
|
|
732
|
+
summary_dict[id].append(
|
|
733
|
+
{
|
|
734
|
+
"asset": name,
|
|
735
|
+
"address": address,
|
|
736
|
+
"amount": math.floor(math.floor(abs(amount) / len(royalty_assets_dict)) * (percentage / 10000)),
|
|
737
|
+
}
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
return summary_dict
|
|
741
|
+
|
|
742
|
+
@staticmethod
|
|
743
|
+
async def make_nft1_offer(
|
|
744
|
+
wallet_state_manager: Any,
|
|
745
|
+
offer_dict: dict[Optional[bytes32], int],
|
|
746
|
+
driver_dict: dict[bytes32, PuzzleInfo],
|
|
747
|
+
action_scope: WalletActionScope,
|
|
748
|
+
fee: uint64,
|
|
749
|
+
extra_conditions: tuple[Condition, ...],
|
|
750
|
+
) -> Offer:
|
|
751
|
+
# First, let's take note of all the royalty enabled NFTs
|
|
752
|
+
royalty_nft_asset_dict: dict[bytes32, int] = {}
|
|
753
|
+
for asset, amount in offer_dict.items():
|
|
754
|
+
if asset is not None and driver_dict[asset].check_type( # check if asset is an Royalty Enabled NFT
|
|
755
|
+
[
|
|
756
|
+
AssetType.SINGLETON.value,
|
|
757
|
+
AssetType.METADATA.value,
|
|
758
|
+
AssetType.OWNERSHIP.value,
|
|
759
|
+
]
|
|
760
|
+
):
|
|
761
|
+
driver_dict[asset].info["also"]["also"]["owner"] = "()"
|
|
762
|
+
royalty_nft_asset_dict[asset] = amount
|
|
763
|
+
|
|
764
|
+
# Then, all of the things that trigger royalties
|
|
765
|
+
fungible_asset_dict: dict[Optional[bytes32], int] = {}
|
|
766
|
+
for asset, amount in offer_dict.items():
|
|
767
|
+
if asset is None or driver_dict[asset].type() != AssetType.SINGLETON.value:
|
|
768
|
+
fungible_asset_dict[asset] = amount
|
|
769
|
+
|
|
770
|
+
# Let's gather some information about the royalties
|
|
771
|
+
offer_side_royalty_split: int = 0
|
|
772
|
+
request_side_royalty_split: int = 0
|
|
773
|
+
for asset, amount in royalty_nft_asset_dict.items(): # requested non fungible items
|
|
774
|
+
if amount > 0:
|
|
775
|
+
request_side_royalty_split += 1
|
|
776
|
+
elif amount < 0:
|
|
777
|
+
offer_side_royalty_split += 1
|
|
778
|
+
|
|
779
|
+
trade_prices: list[tuple[uint64, bytes32]] = []
|
|
780
|
+
for asset, amount in fungible_asset_dict.items(): # requested fungible items
|
|
781
|
+
if amount > 0 and offer_side_royalty_split > 0:
|
|
782
|
+
settlement_ph: bytes32 = (
|
|
783
|
+
OFFER_MOD_HASH if asset is None else construct_puzzle(driver_dict[asset], OFFER_MOD).get_tree_hash()
|
|
784
|
+
)
|
|
785
|
+
trade_prices.append((uint64(math.floor(amount / offer_side_royalty_split)), settlement_ph))
|
|
786
|
+
|
|
787
|
+
required_royalty_info: list[tuple[bytes32, bytes32, uint16]] = [] # [(launcher_id, address, percentage)]
|
|
788
|
+
offered_royalty_percentages: dict[bytes32, uint16] = {}
|
|
789
|
+
for asset, amount in royalty_nft_asset_dict.items(): # royalty enabled NFTs
|
|
790
|
+
transfer_info = driver_dict[asset].also().also() # type: ignore
|
|
791
|
+
assert isinstance(transfer_info, PuzzleInfo)
|
|
792
|
+
royalty_percentage_raw = transfer_info["transfer_program"]["royalty_percentage"]
|
|
793
|
+
assert royalty_percentage_raw is not None
|
|
794
|
+
# clvm encodes large ints as bytes
|
|
795
|
+
if isinstance(royalty_percentage_raw, bytes):
|
|
796
|
+
royalty_percentage = int_from_bytes(royalty_percentage_raw)
|
|
797
|
+
else:
|
|
798
|
+
royalty_percentage = int(royalty_percentage_raw)
|
|
799
|
+
if amount > 0:
|
|
800
|
+
required_royalty_info.append(
|
|
801
|
+
(
|
|
802
|
+
asset,
|
|
803
|
+
bytes32(transfer_info["transfer_program"]["royalty_address"]),
|
|
804
|
+
uint16(royalty_percentage),
|
|
805
|
+
)
|
|
806
|
+
)
|
|
807
|
+
else:
|
|
808
|
+
offered_royalty_percentages[asset] = uint16(royalty_percentage)
|
|
809
|
+
|
|
810
|
+
royalty_payments: dict[Optional[bytes32], list[tuple[bytes32, Payment]]] = {}
|
|
811
|
+
for asset, amount in fungible_asset_dict.items(): # offered fungible items
|
|
812
|
+
if amount < 0 and request_side_royalty_split > 0:
|
|
813
|
+
payment_list: list[tuple[bytes32, Payment]] = []
|
|
814
|
+
for launcher_id, address, percentage in required_royalty_info:
|
|
815
|
+
extra_royalty_amount = uint64(
|
|
816
|
+
math.floor(math.floor(abs(amount) / request_side_royalty_split) * (percentage / 10000))
|
|
817
|
+
)
|
|
818
|
+
if extra_royalty_amount == abs(amount):
|
|
819
|
+
raise ValueError("Amount offered and amount paid in royalties are equal")
|
|
820
|
+
payment_list.append((launcher_id, Payment(address, extra_royalty_amount, [address])))
|
|
821
|
+
royalty_payments[asset] = payment_list
|
|
822
|
+
|
|
823
|
+
# Generate the requested_payments to be notarized
|
|
824
|
+
p2_ph = await wallet_state_manager.main_wallet.get_puzzle_hash(
|
|
825
|
+
new=not action_scope.config.tx_config.reuse_puzhash
|
|
826
|
+
)
|
|
827
|
+
requested_payments: dict[Optional[bytes32], list[Payment]] = {}
|
|
828
|
+
for asset, amount in offer_dict.items():
|
|
829
|
+
if amount > 0:
|
|
830
|
+
requested_payments[asset] = [Payment(p2_ph, uint64(amount), [p2_ph] if asset is not None else [])]
|
|
831
|
+
|
|
832
|
+
# Find all the coins we're offering
|
|
833
|
+
offered_coins_by_asset: dict[Optional[bytes32], set[Coin]] = {}
|
|
834
|
+
all_offered_coins: set[Coin] = set()
|
|
835
|
+
for asset, amount in offer_dict.items():
|
|
836
|
+
if amount < 0:
|
|
837
|
+
if asset is None:
|
|
838
|
+
wallet = wallet_state_manager.main_wallet
|
|
839
|
+
else:
|
|
840
|
+
wallet = await wallet_state_manager.get_wallet_for_asset_id(asset.hex())
|
|
841
|
+
if asset in royalty_payments:
|
|
842
|
+
royalty_amount: int = sum(p.amount for _, p in royalty_payments[asset])
|
|
843
|
+
else:
|
|
844
|
+
royalty_amount = 0
|
|
845
|
+
if asset is None:
|
|
846
|
+
coin_amount_needed: int = abs(amount) + royalty_amount + fee
|
|
847
|
+
else:
|
|
848
|
+
coin_amount_needed = abs(amount) + royalty_amount
|
|
849
|
+
offered_coins: set[Coin] = await wallet.get_coins_to_offer(asset, coin_amount_needed, action_scope)
|
|
850
|
+
if len(offered_coins) == 0:
|
|
851
|
+
raise ValueError(f"Did not have asset ID {asset.hex() if asset is not None else 'XCH'} to offer")
|
|
852
|
+
offered_coins_by_asset[asset] = offered_coins
|
|
853
|
+
all_offered_coins.update(offered_coins)
|
|
854
|
+
|
|
855
|
+
# Notarize the payments and get the announcements for the bundle
|
|
856
|
+
notarized_payments: dict[Optional[bytes32], list[NotarizedPayment]] = Offer.notarize_payments(
|
|
857
|
+
requested_payments, list(all_offered_coins)
|
|
858
|
+
)
|
|
859
|
+
announcements_to_assert: list[AssertPuzzleAnnouncement] = Offer.calculate_announcements(
|
|
860
|
+
notarized_payments, driver_dict
|
|
861
|
+
)
|
|
862
|
+
for asset, payments in royalty_payments.items():
|
|
863
|
+
if asset is None: # xch offer
|
|
864
|
+
offer_puzzle = OFFER_MOD
|
|
865
|
+
royalty_ph = OFFER_MOD_HASH
|
|
866
|
+
else:
|
|
867
|
+
offer_puzzle = construct_puzzle(driver_dict[asset], OFFER_MOD)
|
|
868
|
+
royalty_ph = offer_puzzle.get_tree_hash()
|
|
869
|
+
announcements_to_assert.extend(
|
|
870
|
+
[
|
|
871
|
+
AssertPuzzleAnnouncement(
|
|
872
|
+
asserted_ph=royalty_ph,
|
|
873
|
+
asserted_msg=Program.to((launcher_id, [p.as_condition_args()])).get_tree_hash(),
|
|
874
|
+
)
|
|
875
|
+
for launcher_id, p in payments
|
|
876
|
+
if p.amount > 0
|
|
877
|
+
]
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
# Create all of the transactions
|
|
881
|
+
all_transactions: list[TransactionRecord] = []
|
|
882
|
+
additional_bundles: list[WalletSpendBundle] = []
|
|
883
|
+
# standard pays the fee if possible
|
|
884
|
+
fee_left_to_pay: uint64 = uint64(0) if None in offer_dict and offer_dict[None] < 0 else fee
|
|
885
|
+
|
|
886
|
+
for asset, amount in offer_dict.items():
|
|
887
|
+
if amount < 0:
|
|
888
|
+
if asset is None:
|
|
889
|
+
wallet = wallet_state_manager.main_wallet
|
|
890
|
+
else:
|
|
891
|
+
wallet = await wallet_state_manager.get_wallet_for_asset_id(asset.hex())
|
|
892
|
+
|
|
893
|
+
# First, sending all the coins to the OFFER_MOD
|
|
894
|
+
async with wallet_state_manager.new_action_scope(
|
|
895
|
+
action_scope.config.tx_config, push=False
|
|
896
|
+
) as inner_action_scope:
|
|
897
|
+
if wallet.type() == WalletType.STANDARD_WALLET:
|
|
898
|
+
payments = royalty_payments[asset] if asset in royalty_payments else []
|
|
899
|
+
payment_sum = sum(p.amount for _, p in payments)
|
|
900
|
+
await wallet.generate_signed_transaction(
|
|
901
|
+
abs(amount),
|
|
902
|
+
OFFER_MOD_HASH,
|
|
903
|
+
inner_action_scope,
|
|
904
|
+
primaries=[Payment(OFFER_MOD_HASH, uint64(payment_sum))] if payment_sum > 0 else [],
|
|
905
|
+
fee=fee,
|
|
906
|
+
coins=offered_coins_by_asset[asset],
|
|
907
|
+
extra_conditions=(*extra_conditions, *announcements_to_assert),
|
|
908
|
+
)
|
|
909
|
+
elif asset not in fungible_asset_dict:
|
|
910
|
+
assert asset is not None
|
|
911
|
+
await wallet.generate_signed_transaction(
|
|
912
|
+
[abs(amount)],
|
|
913
|
+
[OFFER_MOD_HASH],
|
|
914
|
+
inner_action_scope,
|
|
915
|
+
fee=fee_left_to_pay,
|
|
916
|
+
coins=offered_coins_by_asset[asset],
|
|
917
|
+
trade_prices_list=[
|
|
918
|
+
list(price)
|
|
919
|
+
for price in trade_prices
|
|
920
|
+
if math.floor(price[0] * (offered_royalty_percentages[asset] / 10000)) != 0
|
|
921
|
+
],
|
|
922
|
+
extra_conditions=(*extra_conditions, *announcements_to_assert),
|
|
923
|
+
)
|
|
924
|
+
else:
|
|
925
|
+
payments = royalty_payments[asset] if asset in royalty_payments else []
|
|
926
|
+
await wallet.generate_signed_transaction(
|
|
927
|
+
[abs(amount)] + ([sum(p.amount for _, p in payments)] if payments != [] else []),
|
|
928
|
+
[OFFER_MOD_HASH] + ([OFFER_MOD_HASH] if payments != [] else []),
|
|
929
|
+
inner_action_scope,
|
|
930
|
+
fee=fee_left_to_pay,
|
|
931
|
+
coins=offered_coins_by_asset[asset],
|
|
932
|
+
extra_conditions=(*extra_conditions, *announcements_to_assert),
|
|
933
|
+
)
|
|
934
|
+
all_transactions.extend(inner_action_scope.side_effects.transactions)
|
|
935
|
+
fee_left_to_pay = uint64(0)
|
|
936
|
+
extra_conditions = tuple()
|
|
937
|
+
|
|
938
|
+
# Then, adding in the spends for the royalty offer mod
|
|
939
|
+
if asset in fungible_asset_dict:
|
|
940
|
+
# Create a coin_spend for the royalty payout from OFFER MOD
|
|
941
|
+
|
|
942
|
+
# Skip it if we're paying 0 royalties
|
|
943
|
+
payments = royalty_payments[asset] if asset in royalty_payments else []
|
|
944
|
+
if sum(p.amount for _, p in payments) == 0:
|
|
945
|
+
continue
|
|
946
|
+
|
|
947
|
+
# We cannot create coins with the same puzzle hash and amount
|
|
948
|
+
# So if there's multiple NFTs with the same royalty puzhash/percentage, we must create multiple
|
|
949
|
+
# generations of offer coins
|
|
950
|
+
royalty_coin: Optional[Coin] = None
|
|
951
|
+
parent_spend: Optional[CoinSpend] = None
|
|
952
|
+
while True:
|
|
953
|
+
duplicate_payments: list[tuple[bytes32, Payment]] = []
|
|
954
|
+
deduped_payment_list: list[tuple[bytes32, Payment]] = []
|
|
955
|
+
for launcher_id, payment in payments:
|
|
956
|
+
if payment in [p for _, p in deduped_payment_list]:
|
|
957
|
+
duplicate_payments.append((launcher_id, payment))
|
|
958
|
+
else:
|
|
959
|
+
deduped_payment_list.append((launcher_id, payment))
|
|
960
|
+
|
|
961
|
+
# ((nft_launcher_id . ((ROYALTY_ADDRESS, royalty_amount, memos) ...)))
|
|
962
|
+
inner_royalty_sol = Program.to(
|
|
963
|
+
[
|
|
964
|
+
(launcher_id, [payment.as_condition_args()])
|
|
965
|
+
for launcher_id, payment in deduped_payment_list
|
|
966
|
+
]
|
|
967
|
+
)
|
|
968
|
+
if duplicate_payments != []:
|
|
969
|
+
inner_royalty_sol = Program.to(
|
|
970
|
+
(
|
|
971
|
+
None,
|
|
972
|
+
[
|
|
973
|
+
Payment(
|
|
974
|
+
OFFER_MOD_HASH,
|
|
975
|
+
uint64(sum(p.amount for _, p in duplicate_payments)),
|
|
976
|
+
).as_condition_args()
|
|
977
|
+
],
|
|
978
|
+
)
|
|
979
|
+
).cons(inner_royalty_sol)
|
|
980
|
+
|
|
981
|
+
if asset is None: # xch offer
|
|
982
|
+
offer_puzzle = OFFER_MOD
|
|
983
|
+
royalty_ph = OFFER_MOD_HASH
|
|
984
|
+
else:
|
|
985
|
+
offer_puzzle = construct_puzzle(driver_dict[asset], OFFER_MOD)
|
|
986
|
+
royalty_ph = offer_puzzle.get_tree_hash()
|
|
987
|
+
if royalty_coin is None:
|
|
988
|
+
for tx in inner_action_scope.side_effects.transactions:
|
|
989
|
+
if tx.spend_bundle is not None:
|
|
990
|
+
for coin in tx.spend_bundle.additions():
|
|
991
|
+
royalty_payment_amount: int = sum(p.amount for _, p in payments)
|
|
992
|
+
if coin.amount == royalty_payment_amount and coin.puzzle_hash == royalty_ph:
|
|
993
|
+
royalty_coin = coin
|
|
994
|
+
parent_spend = next(
|
|
995
|
+
cs
|
|
996
|
+
for cs in tx.spend_bundle.coin_spends
|
|
997
|
+
if cs.coin.name() == royalty_coin.parent_coin_info
|
|
998
|
+
)
|
|
999
|
+
break
|
|
1000
|
+
else:
|
|
1001
|
+
continue
|
|
1002
|
+
break
|
|
1003
|
+
assert royalty_coin is not None
|
|
1004
|
+
assert parent_spend is not None
|
|
1005
|
+
if asset is None: # If XCH
|
|
1006
|
+
royalty_sol = inner_royalty_sol
|
|
1007
|
+
else:
|
|
1008
|
+
# call our drivers to solve the puzzle
|
|
1009
|
+
royalty_coin_hex = (
|
|
1010
|
+
"0x"
|
|
1011
|
+
+ royalty_coin.parent_coin_info.hex()
|
|
1012
|
+
+ royalty_coin.puzzle_hash.hex()
|
|
1013
|
+
+ uint64(royalty_coin.amount).stream_to_bytes().hex()
|
|
1014
|
+
)
|
|
1015
|
+
parent_spend_hex: str = "0x" + bytes(parent_spend).hex()
|
|
1016
|
+
solver = Solver(
|
|
1017
|
+
{
|
|
1018
|
+
"coin": royalty_coin_hex,
|
|
1019
|
+
"parent_spend": parent_spend_hex,
|
|
1020
|
+
"siblings": "()",
|
|
1021
|
+
"sibling_spends": "()",
|
|
1022
|
+
"sibling_puzzles": "()",
|
|
1023
|
+
"sibling_solutions": "()",
|
|
1024
|
+
}
|
|
1025
|
+
)
|
|
1026
|
+
royalty_sol = solve_puzzle(driver_dict[asset], solver, OFFER_MOD, inner_royalty_sol)
|
|
1027
|
+
|
|
1028
|
+
new_coin_spend = make_spend(royalty_coin, offer_puzzle, royalty_sol)
|
|
1029
|
+
additional_bundles.append(WalletSpendBundle([new_coin_spend], G2Element()))
|
|
1030
|
+
|
|
1031
|
+
if duplicate_payments != []:
|
|
1032
|
+
payments = duplicate_payments
|
|
1033
|
+
royalty_coin = next(
|
|
1034
|
+
c for c in compute_additions(new_coin_spend) if c.puzzle_hash == royalty_ph
|
|
1035
|
+
)
|
|
1036
|
+
parent_spend = new_coin_spend
|
|
1037
|
+
continue
|
|
1038
|
+
else:
|
|
1039
|
+
break
|
|
1040
|
+
|
|
1041
|
+
# Finally, assemble the tx records properly
|
|
1042
|
+
txs_bundle = WalletSpendBundle.aggregate(
|
|
1043
|
+
[tx.spend_bundle for tx in all_transactions if tx.spend_bundle is not None]
|
|
1044
|
+
)
|
|
1045
|
+
aggregate_bundle = WalletSpendBundle.aggregate([txs_bundle, *additional_bundles])
|
|
1046
|
+
offer = Offer(notarized_payments, aggregate_bundle, driver_dict)
|
|
1047
|
+
async with action_scope.use() as interface:
|
|
1048
|
+
interface.side_effects.transactions.extend(all_transactions)
|
|
1049
|
+
|
|
1050
|
+
return offer
|
|
1051
|
+
|
|
1052
|
+
async def set_bulk_nft_did(
|
|
1053
|
+
self,
|
|
1054
|
+
nft_list: list[NFTCoinInfo],
|
|
1055
|
+
did_id: bytes,
|
|
1056
|
+
action_scope: WalletActionScope,
|
|
1057
|
+
fee: uint64 = uint64(0),
|
|
1058
|
+
announcement_ids: list[bytes32] = [],
|
|
1059
|
+
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
1060
|
+
) -> None:
|
|
1061
|
+
self.log.debug("Setting NFT DID with parameters: nft=%s did=%s", nft_list, did_id)
|
|
1062
|
+
nft_ids = []
|
|
1063
|
+
first = True
|
|
1064
|
+
for nft_coin_info in nft_list:
|
|
1065
|
+
nft_ids.append(nft_coin_info.nft_id)
|
|
1066
|
+
if did_id != b"" and len(announcement_ids) > 0:
|
|
1067
|
+
await self.get_did_approval_info(announcement_ids, action_scope, bytes32(did_id))
|
|
1068
|
+
|
|
1069
|
+
for _, wallet in self.wallet_state_manager.wallets.items():
|
|
1070
|
+
if wallet.type() == WalletType.DECENTRALIZED_ID:
|
|
1071
|
+
if bytes32.fromhex(wallet.get_my_DID()) == did_id:
|
|
1072
|
+
did_inner_hash = wallet.did_info.current_inner.get_tree_hash()
|
|
1073
|
+
break
|
|
1074
|
+
else:
|
|
1075
|
+
raise ValueError(f"No DID wallet with id: {did_id.hex()}")
|
|
1076
|
+
|
|
1077
|
+
for nft_coin_info in nft_list:
|
|
1078
|
+
unft = UncurriedNFT.uncurry(*nft_coin_info.full_puzzle.uncurry())
|
|
1079
|
+
assert unft is not None
|
|
1080
|
+
puzzle_hashes_to_sign = [unft.p2_puzzle.get_tree_hash()]
|
|
1081
|
+
if not first:
|
|
1082
|
+
fee = uint64(0)
|
|
1083
|
+
extra_conditions = tuple()
|
|
1084
|
+
await self.generate_signed_transaction(
|
|
1085
|
+
[uint64(nft_coin_info.coin.amount)],
|
|
1086
|
+
puzzle_hashes_to_sign,
|
|
1087
|
+
action_scope,
|
|
1088
|
+
fee,
|
|
1089
|
+
{nft_coin_info.coin},
|
|
1090
|
+
new_owner=did_id,
|
|
1091
|
+
new_did_inner_hash=did_inner_hash,
|
|
1092
|
+
extra_conditions=extra_conditions,
|
|
1093
|
+
)
|
|
1094
|
+
first = False
|
|
1095
|
+
|
|
1096
|
+
async def bulk_transfer_nft(
|
|
1097
|
+
self,
|
|
1098
|
+
nft_list: list[NFTCoinInfo],
|
|
1099
|
+
puzzle_hash: bytes32,
|
|
1100
|
+
action_scope: WalletActionScope,
|
|
1101
|
+
fee: uint64 = uint64(0),
|
|
1102
|
+
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
1103
|
+
) -> None:
|
|
1104
|
+
self.log.debug("Transfer NFTs %s to %s", nft_list, puzzle_hash.hex())
|
|
1105
|
+
first = True
|
|
1106
|
+
|
|
1107
|
+
for nft_coin_info in nft_list:
|
|
1108
|
+
if not first:
|
|
1109
|
+
fee = uint64(0)
|
|
1110
|
+
extra_conditions = tuple()
|
|
1111
|
+
await self.generate_signed_transaction(
|
|
1112
|
+
[uint64(nft_coin_info.coin.amount)],
|
|
1113
|
+
[puzzle_hash],
|
|
1114
|
+
action_scope,
|
|
1115
|
+
coins={nft_coin_info.coin},
|
|
1116
|
+
fee=fee,
|
|
1117
|
+
new_owner=b"",
|
|
1118
|
+
new_did_inner_hash=b"",
|
|
1119
|
+
extra_conditions=extra_conditions,
|
|
1120
|
+
)
|
|
1121
|
+
first = False
|
|
1122
|
+
|
|
1123
|
+
async def set_nft_did(
|
|
1124
|
+
self,
|
|
1125
|
+
nft_coin_info: NFTCoinInfo,
|
|
1126
|
+
did_id: bytes,
|
|
1127
|
+
action_scope: WalletActionScope,
|
|
1128
|
+
fee: uint64 = uint64(0),
|
|
1129
|
+
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
1130
|
+
) -> None:
|
|
1131
|
+
self.log.debug("Setting NFT DID with parameters: nft=%s did=%s", nft_coin_info, did_id)
|
|
1132
|
+
unft = UncurriedNFT.uncurry(*nft_coin_info.full_puzzle.uncurry())
|
|
1133
|
+
assert unft is not None
|
|
1134
|
+
nft_id = unft.singleton_launcher_id
|
|
1135
|
+
puzzle_hashes_to_sign = [unft.p2_puzzle.get_tree_hash()]
|
|
1136
|
+
did_inner_hash = b""
|
|
1137
|
+
if did_id != b"":
|
|
1138
|
+
did_inner_hash = await self.get_did_approval_info([nft_id], action_scope, bytes32(did_id))
|
|
1139
|
+
|
|
1140
|
+
await self.generate_signed_transaction(
|
|
1141
|
+
[uint64(nft_coin_info.coin.amount)],
|
|
1142
|
+
puzzle_hashes_to_sign,
|
|
1143
|
+
action_scope,
|
|
1144
|
+
fee,
|
|
1145
|
+
{nft_coin_info.coin},
|
|
1146
|
+
new_owner=did_id,
|
|
1147
|
+
new_did_inner_hash=did_inner_hash,
|
|
1148
|
+
extra_conditions=extra_conditions,
|
|
1149
|
+
)
|
|
1150
|
+
|
|
1151
|
+
await self.update_coin_status(nft_coin_info.coin.name(), True)
|
|
1152
|
+
self.wallet_state_manager.state_changed("nft_coin_did_set", self.wallet_info.id)
|
|
1153
|
+
|
|
1154
|
+
async def mint_from_did(
|
|
1155
|
+
self,
|
|
1156
|
+
metadata_list: list[dict[str, Any]],
|
|
1157
|
+
action_scope: WalletActionScope,
|
|
1158
|
+
target_list: Optional[list[bytes32]] = [],
|
|
1159
|
+
mint_number_start: Optional[int] = 1,
|
|
1160
|
+
mint_total: Optional[int] = None,
|
|
1161
|
+
xch_coins: Optional[set[Coin]] = None,
|
|
1162
|
+
xch_change_ph: Optional[bytes32] = None,
|
|
1163
|
+
new_innerpuzhash: Optional[bytes32] = None,
|
|
1164
|
+
new_p2_puzhash: Optional[bytes32] = None,
|
|
1165
|
+
did_coin: Optional[Coin] = None,
|
|
1166
|
+
did_lineage_parent: Optional[bytes32] = None,
|
|
1167
|
+
fee: Optional[uint64] = uint64(0),
|
|
1168
|
+
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
1169
|
+
) -> None:
|
|
1170
|
+
"""
|
|
1171
|
+
Minting NFTs from the DID linked wallet, also used for bulk minting NFTs.
|
|
1172
|
+
- The DID is spent along with an intermediate launcher puzzle which
|
|
1173
|
+
generates a set of ephemeral coins with unique IDs by currying in the
|
|
1174
|
+
mint_number and mint_total for each NFT being minted. These
|
|
1175
|
+
intermediate coins then create the launcher coins for the list of NFTs
|
|
1176
|
+
- The launcher coins are then spent along with the created eve spend
|
|
1177
|
+
and an xch spend that funds the transactions and pays fees.
|
|
1178
|
+
- There is also an option to pass in a list of target puzzlehashes. If
|
|
1179
|
+
provided this method will create an additional transaction transfering
|
|
1180
|
+
the minted NFTs to the row-matched target.
|
|
1181
|
+
:param metadata_list: A list of dicts containing the metadata for each NFT to be minted
|
|
1182
|
+
:param target_list: [Optional] a list of targets for transfering minted NFTs (aka airdrop)
|
|
1183
|
+
:param mint_number_start: [Optional] The starting point for mint number used in intermediate launcher
|
|
1184
|
+
puzzle. Default: 1
|
|
1185
|
+
:param mint_total: [Optional] The total number of NFTs being minted
|
|
1186
|
+
:param xch_coins: [Optional] For use with bulk minting to provide the coin used for funding the minting spend.
|
|
1187
|
+
This coin can be one that will be created in the future
|
|
1188
|
+
:param xch_change_ph: [Optional] For use with bulk minting, so we can specify the puzzle hash that the change
|
|
1189
|
+
from the funding transaction goes to.
|
|
1190
|
+
:param new_innerpuzhash: [Optional] The new inner puzzle hash for the DID once it is spent. For bulk minting we
|
|
1191
|
+
generally don't provide this as the default behaviour is to re-use the existing inner puzzle hash
|
|
1192
|
+
:param new_p2_puzhash: [Optional] The new p2 puzzle hash for the DID once it is spent. For bulk minting we
|
|
1193
|
+
generally don't provide this as the default behaviour is to re-use the existing inner puzzle hash
|
|
1194
|
+
:param did_coin: [Optional] The did coin to use for minting. Required for bulk minting when the DID coin will
|
|
1195
|
+
be created in the future
|
|
1196
|
+
:param did_lineage_parent: [Optional] The parent coin to use for the lineage proof in the DID spend. Needed
|
|
1197
|
+
for bulk minting when the coin will be created in the future
|
|
1198
|
+
:param fee: A fee amount, taken out of the xch spend.
|
|
1199
|
+
"""
|
|
1200
|
+
# get DID Wallet
|
|
1201
|
+
for wallet in self.wallet_state_manager.wallets.values():
|
|
1202
|
+
if wallet.type() == WalletType.DECENTRALIZED_ID:
|
|
1203
|
+
if self.get_did() == bytes32.from_hexstr(wallet.get_my_DID()):
|
|
1204
|
+
did_wallet = wallet
|
|
1205
|
+
break
|
|
1206
|
+
else:
|
|
1207
|
+
raise ValueError("There is no DID associated with this NFT wallet")
|
|
1208
|
+
|
|
1209
|
+
assert did_wallet.did_info.current_inner is not None
|
|
1210
|
+
assert did_wallet.did_info.origin_coin is not None
|
|
1211
|
+
|
|
1212
|
+
# Ensure we have an mint_total value
|
|
1213
|
+
if mint_total is None:
|
|
1214
|
+
mint_total = len(metadata_list)
|
|
1215
|
+
assert isinstance(mint_number_start, int)
|
|
1216
|
+
assert len(metadata_list) <= mint_total + 1 - mint_number_start
|
|
1217
|
+
|
|
1218
|
+
# Ensure we have a did coin and its next inner puzzle hash
|
|
1219
|
+
if did_coin is None:
|
|
1220
|
+
did_coin = await did_wallet.get_coin()
|
|
1221
|
+
innerpuz: Program = did_wallet.did_info.current_inner
|
|
1222
|
+
if new_innerpuzhash is None:
|
|
1223
|
+
new_innerpuzhash = innerpuz.get_tree_hash()
|
|
1224
|
+
uncurried_did = did_wallet_puzzles.uncurry_innerpuz(innerpuz)
|
|
1225
|
+
assert uncurried_did is not None
|
|
1226
|
+
p2_puzzle = uncurried_did[0]
|
|
1227
|
+
new_p2_puzhash = p2_puzzle.get_tree_hash()
|
|
1228
|
+
assert new_p2_puzhash is not None
|
|
1229
|
+
# make the primaries for the DID spend
|
|
1230
|
+
primaries = [Payment(new_innerpuzhash, uint64(did_coin.amount), [bytes(new_p2_puzhash)])]
|
|
1231
|
+
|
|
1232
|
+
# Ensure we have an xch coin of high enough amount
|
|
1233
|
+
assert isinstance(fee, uint64)
|
|
1234
|
+
total_amount = len(metadata_list) + fee
|
|
1235
|
+
if xch_coins is None:
|
|
1236
|
+
xch_coins = await self.standard_wallet.select_coins(uint64(total_amount), action_scope)
|
|
1237
|
+
assert len(xch_coins) > 0
|
|
1238
|
+
|
|
1239
|
+
# set the chunk size for the spend bundle we're going to create
|
|
1240
|
+
chunk_size = len(metadata_list)
|
|
1241
|
+
|
|
1242
|
+
# Because bulk minting may not mint all the NFTs in one bundle, we
|
|
1243
|
+
# calculate the edition numbers that will be used in the intermediate
|
|
1244
|
+
# puzzle based on the starting edition number given, and the size of the
|
|
1245
|
+
# chunk going into this spend bundle
|
|
1246
|
+
mint_number_end = mint_number_start + chunk_size
|
|
1247
|
+
|
|
1248
|
+
# Empty set to load with the announcements we will assert from DID to
|
|
1249
|
+
# match the announcements from the intermediate launcher puzzle
|
|
1250
|
+
did_announcements: set[Any] = set()
|
|
1251
|
+
puzzle_assertions: set[Any] = set()
|
|
1252
|
+
amount = uint64(1)
|
|
1253
|
+
intermediate_coin_spends = []
|
|
1254
|
+
launcher_spends = []
|
|
1255
|
+
launcher_ids = []
|
|
1256
|
+
p2_inner_puzzle = await self.standard_wallet.get_new_puzzle()
|
|
1257
|
+
p2_inner_ph = p2_inner_puzzle.get_tree_hash()
|
|
1258
|
+
|
|
1259
|
+
# Loop to create each intermediate coin, launcher, eve and (optional) transfer spends
|
|
1260
|
+
for mint_number in range(mint_number_start, mint_number_end):
|
|
1261
|
+
# Create the puzzle, solution and coin spend for the intermediate launcher
|
|
1262
|
+
intermediate_launcher_puz = did_wallet_puzzles.INTERMEDIATE_LAUNCHER_MOD.curry(
|
|
1263
|
+
chia.wallet.singleton.SINGLETON_LAUNCHER_PUZZLE_HASH, mint_number, mint_total
|
|
1264
|
+
)
|
|
1265
|
+
intermediate_launcher_ph = intermediate_launcher_puz.get_tree_hash()
|
|
1266
|
+
primaries.append(Payment(intermediate_launcher_ph, uint64(0), [intermediate_launcher_ph]))
|
|
1267
|
+
intermediate_launcher_sol = Program.to([])
|
|
1268
|
+
intermediate_launcher_coin = Coin(did_coin.name(), intermediate_launcher_ph, uint64(0))
|
|
1269
|
+
intermediate_launcher_coin_spend = make_spend(
|
|
1270
|
+
intermediate_launcher_coin, intermediate_launcher_puz, intermediate_launcher_sol
|
|
1271
|
+
)
|
|
1272
|
+
intermediate_coin_spends.append(intermediate_launcher_coin_spend)
|
|
1273
|
+
|
|
1274
|
+
# create an ASSERT_COIN_ANNOUNCEMENT for the DID spend. The
|
|
1275
|
+
# intermediate launcher coin issues a CREATE_COIN_ANNOUNCEMENT of
|
|
1276
|
+
# the mint_number and mint_total for the launcher coin it creates
|
|
1277
|
+
intermediate_announcement_message = std_hash(int_to_bytes(mint_number) + int_to_bytes(mint_total))
|
|
1278
|
+
did_announcements.add(std_hash(intermediate_launcher_coin.name() + intermediate_announcement_message))
|
|
1279
|
+
|
|
1280
|
+
# Create the launcher coin, and add its id to a list to be asserted in the DID spend
|
|
1281
|
+
launcher_coin = Coin(
|
|
1282
|
+
intermediate_launcher_coin.name(), chia.wallet.singleton.SINGLETON_LAUNCHER_PUZZLE_HASH, amount
|
|
1283
|
+
)
|
|
1284
|
+
launcher_ids.append(launcher_coin.name())
|
|
1285
|
+
|
|
1286
|
+
# Grab the metadata from metadata_list. The index for metadata_list
|
|
1287
|
+
# needs to be offset by mint_number_start
|
|
1288
|
+
metadata = metadata_list[mint_number - mint_number_start]
|
|
1289
|
+
|
|
1290
|
+
# Create the inner and full puzzles for the eve spend
|
|
1291
|
+
inner_puzzle = create_ownership_layer_puzzle(
|
|
1292
|
+
launcher_coin.name(),
|
|
1293
|
+
b"",
|
|
1294
|
+
p2_inner_puzzle,
|
|
1295
|
+
metadata["royalty_pc"],
|
|
1296
|
+
royalty_puzzle_hash=metadata["royalty_ph"],
|
|
1297
|
+
)
|
|
1298
|
+
eve_fullpuz = nft_puzzles.create_full_puzzle(
|
|
1299
|
+
launcher_coin.name(), metadata["program"], NFT_METADATA_UPDATER.get_tree_hash(), inner_puzzle
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
# Annnouncements for eve spend. These are asserted by the DID spend
|
|
1303
|
+
announcement_message = Program.to([eve_fullpuz.get_tree_hash(), amount, []]).get_tree_hash()
|
|
1304
|
+
did_announcements.add(std_hash(launcher_coin.name() + announcement_message))
|
|
1305
|
+
|
|
1306
|
+
genesis_launcher_solution = Program.to([eve_fullpuz.get_tree_hash(), amount, []])
|
|
1307
|
+
|
|
1308
|
+
launcher_cs = make_spend(
|
|
1309
|
+
launcher_coin, chia.wallet.singleton.SINGLETON_LAUNCHER_PUZZLE, genesis_launcher_solution
|
|
1310
|
+
)
|
|
1311
|
+
launcher_spends.append(launcher_cs)
|
|
1312
|
+
|
|
1313
|
+
eve_coin = Coin(launcher_coin.name(), eve_fullpuz.get_tree_hash(), uint64(amount))
|
|
1314
|
+
|
|
1315
|
+
# To make the eve transaction we need to construct the NFTCoinInfo
|
|
1316
|
+
# for the NFT (which doesn't exist yet)
|
|
1317
|
+
nft_coin = NFTCoinInfo(
|
|
1318
|
+
nft_id=launcher_coin.name(),
|
|
1319
|
+
coin=eve_coin,
|
|
1320
|
+
lineage_proof=LineageProof(
|
|
1321
|
+
parent_name=launcher_coin.parent_coin_info, amount=uint64(launcher_coin.amount)
|
|
1322
|
+
),
|
|
1323
|
+
full_puzzle=eve_fullpuz,
|
|
1324
|
+
mint_height=uint32(0),
|
|
1325
|
+
)
|
|
1326
|
+
|
|
1327
|
+
# Create the eve transaction setting the DID owner, and applying
|
|
1328
|
+
# the announcements from announcement_set to match the launcher
|
|
1329
|
+
# coin annnouncement
|
|
1330
|
+
if target_list:
|
|
1331
|
+
target_ph = target_list[mint_number - mint_number_start]
|
|
1332
|
+
else:
|
|
1333
|
+
target_ph = p2_inner_ph
|
|
1334
|
+
async with self.wallet_state_manager.new_action_scope(
|
|
1335
|
+
action_scope.config.tx_config, push=False
|
|
1336
|
+
) as inner_action_scope:
|
|
1337
|
+
await self.generate_signed_transaction(
|
|
1338
|
+
[uint64(eve_coin.amount)],
|
|
1339
|
+
[target_ph],
|
|
1340
|
+
inner_action_scope,
|
|
1341
|
+
nft_coin=nft_coin,
|
|
1342
|
+
new_owner=b"",
|
|
1343
|
+
new_did_inner_hash=b"",
|
|
1344
|
+
additional_bundles=[],
|
|
1345
|
+
memos=[[target_ph]],
|
|
1346
|
+
)
|
|
1347
|
+
|
|
1348
|
+
async with action_scope.use() as interface:
|
|
1349
|
+
interface.side_effects.transactions.extend(inner_action_scope.side_effects.transactions)
|
|
1350
|
+
|
|
1351
|
+
eve_sb = next(
|
|
1352
|
+
tx.spend_bundle for tx in inner_action_scope.side_effects.transactions if tx.spend_bundle is not None
|
|
1353
|
+
)
|
|
1354
|
+
# Extract Puzzle Announcement from eve spend
|
|
1355
|
+
eve_sol = eve_sb.coin_spends[0].solution.to_program()
|
|
1356
|
+
conds = eve_fullpuz.run(eve_sol)
|
|
1357
|
+
eve_puzzle_announcement = next(x for x in conds.as_python() if int_from_bytes(x[0]) == 62)[1]
|
|
1358
|
+
assertion = std_hash(eve_fullpuz.get_tree_hash() + eve_puzzle_announcement)
|
|
1359
|
+
puzzle_assertions.add(assertion)
|
|
1360
|
+
|
|
1361
|
+
# We've now created all the intermediate, launcher, eve and transfer spends.
|
|
1362
|
+
# Create the xch spend to fund the minting.
|
|
1363
|
+
spend_value = sum(coin.amount for coin in xch_coins)
|
|
1364
|
+
change: uint64 = uint64(spend_value - total_amount)
|
|
1365
|
+
if xch_change_ph is None:
|
|
1366
|
+
xch_change_ph = await self.standard_wallet.get_new_puzzlehash()
|
|
1367
|
+
xch_payment = Payment(xch_change_ph, change, [xch_change_ph])
|
|
1368
|
+
|
|
1369
|
+
xch_coins_iter = iter(xch_coins)
|
|
1370
|
+
xch_coin = next(xch_coins_iter)
|
|
1371
|
+
|
|
1372
|
+
message_list: list[bytes32] = [c.name() for c in xch_coins]
|
|
1373
|
+
message_list.append(Coin(xch_coin.name(), xch_payment.puzzle_hash, xch_payment.amount).name())
|
|
1374
|
+
message: bytes32 = std_hash(b"".join(message_list))
|
|
1375
|
+
|
|
1376
|
+
xch_extra_conditions: tuple[Condition, ...] = (
|
|
1377
|
+
AssertCoinAnnouncement(asserted_id=did_coin.name(), asserted_msg=message),
|
|
1378
|
+
)
|
|
1379
|
+
if len(xch_coins) > 1:
|
|
1380
|
+
xch_extra_conditions += (CreateCoinAnnouncement(message),)
|
|
1381
|
+
|
|
1382
|
+
solution: Program = self.standard_wallet.make_solution(
|
|
1383
|
+
primaries=[xch_payment],
|
|
1384
|
+
fee=fee,
|
|
1385
|
+
conditions=xch_extra_conditions,
|
|
1386
|
+
)
|
|
1387
|
+
primary_announcement_hash = AssertCoinAnnouncement(asserted_id=xch_coin.name(), asserted_msg=message).msg_calc
|
|
1388
|
+
# connect this coin assertion to the DID announcement
|
|
1389
|
+
did_coin_announcement = CreateCoinAnnouncement(message)
|
|
1390
|
+
puzzle = await self.standard_wallet.puzzle_for_puzzle_hash(xch_coin.puzzle_hash)
|
|
1391
|
+
xch_spends = [make_spend(xch_coin, puzzle, solution)]
|
|
1392
|
+
|
|
1393
|
+
for xch_coin in xch_coins_iter:
|
|
1394
|
+
puzzle = await self.standard_wallet.puzzle_for_puzzle_hash(xch_coin.puzzle_hash)
|
|
1395
|
+
solution = self.standard_wallet.make_solution(
|
|
1396
|
+
primaries=[], conditions=(AssertCoinAnnouncement(primary_announcement_hash),)
|
|
1397
|
+
)
|
|
1398
|
+
xch_spends.append(make_spend(xch_coin, puzzle, solution))
|
|
1399
|
+
xch_spend = WalletSpendBundle(xch_spends, G2Element())
|
|
1400
|
+
|
|
1401
|
+
# Create the DID spend using the announcements collected when making the intermediate launcher coins
|
|
1402
|
+
did_p2_solution = self.standard_wallet.make_solution(
|
|
1403
|
+
primaries=primaries,
|
|
1404
|
+
conditions=(
|
|
1405
|
+
*extra_conditions,
|
|
1406
|
+
did_coin_announcement,
|
|
1407
|
+
*(AssertCoinAnnouncement(ann) for ann in did_announcements),
|
|
1408
|
+
*(AssertPuzzleAnnouncement(ann) for ann in puzzle_assertions),
|
|
1409
|
+
),
|
|
1410
|
+
)
|
|
1411
|
+
did_inner_sol: Program = Program.to([1, did_p2_solution])
|
|
1412
|
+
did_full_puzzle: Program = chia.wallet.singleton.create_singleton_puzzle(
|
|
1413
|
+
innerpuz,
|
|
1414
|
+
did_wallet.did_info.origin_coin.name(),
|
|
1415
|
+
)
|
|
1416
|
+
# The DID lineage parent won't not exist if we're bulk minting from a future DID coin
|
|
1417
|
+
if did_lineage_parent:
|
|
1418
|
+
did_parent_info: Optional[LineageProof] = LineageProof(
|
|
1419
|
+
parent_name=did_lineage_parent,
|
|
1420
|
+
inner_puzzle_hash=innerpuz.get_tree_hash(),
|
|
1421
|
+
amount=uint64(did_coin.amount),
|
|
1422
|
+
)
|
|
1423
|
+
else:
|
|
1424
|
+
did_parent_info = did_wallet.get_parent_for_coin(did_coin)
|
|
1425
|
+
assert did_parent_info is not None
|
|
1426
|
+
|
|
1427
|
+
did_full_sol = Program.to(
|
|
1428
|
+
[
|
|
1429
|
+
[
|
|
1430
|
+
did_parent_info.parent_name,
|
|
1431
|
+
did_parent_info.inner_puzzle_hash,
|
|
1432
|
+
did_parent_info.amount,
|
|
1433
|
+
],
|
|
1434
|
+
did_coin.amount,
|
|
1435
|
+
did_inner_sol,
|
|
1436
|
+
]
|
|
1437
|
+
)
|
|
1438
|
+
did_spend = make_spend(did_coin, did_full_puzzle, did_full_sol)
|
|
1439
|
+
|
|
1440
|
+
# Collect up all the coin spends and sign them
|
|
1441
|
+
list_of_coinspends = [did_spend, *intermediate_coin_spends, *launcher_spends, *xch_spend.coin_spends]
|
|
1442
|
+
unsigned_spend_bundle = WalletSpendBundle(list_of_coinspends, G2Element())
|
|
1443
|
+
|
|
1444
|
+
# Aggregate everything into a single spend bundle
|
|
1445
|
+
async with action_scope.use() as interface:
|
|
1446
|
+
# This should not be looked to for best practice. I think many of the spends generated above could call
|
|
1447
|
+
# wallet methods that generate transactions and prevent most of the need for this. Refactoring this function
|
|
1448
|
+
# is out of scope so for now we're using this hack.
|
|
1449
|
+
if interface.side_effects.transactions[0].spend_bundle is None:
|
|
1450
|
+
new_spend = unsigned_spend_bundle
|
|
1451
|
+
else:
|
|
1452
|
+
new_spend = WalletSpendBundle.aggregate(
|
|
1453
|
+
[interface.side_effects.transactions[0].spend_bundle, unsigned_spend_bundle]
|
|
1454
|
+
)
|
|
1455
|
+
interface.side_effects.transactions[0] = dataclasses.replace(
|
|
1456
|
+
interface.side_effects.transactions[0], spend_bundle=new_spend, name=new_spend.name()
|
|
1457
|
+
)
|
|
1458
|
+
|
|
1459
|
+
async def mint_from_xch(
|
|
1460
|
+
self,
|
|
1461
|
+
metadata_list: list[dict[str, Any]],
|
|
1462
|
+
action_scope: WalletActionScope,
|
|
1463
|
+
target_list: Optional[list[bytes32]] = [],
|
|
1464
|
+
mint_number_start: Optional[int] = 1,
|
|
1465
|
+
mint_total: Optional[int] = None,
|
|
1466
|
+
xch_coins: Optional[set[Coin]] = None,
|
|
1467
|
+
xch_change_ph: Optional[bytes32] = None,
|
|
1468
|
+
fee: Optional[uint64] = uint64(0),
|
|
1469
|
+
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
1470
|
+
) -> None:
|
|
1471
|
+
"""
|
|
1472
|
+
Minting NFTs from a single XCH spend using intermediate launcher puzzle
|
|
1473
|
+
:param metadata_list: A list of dicts containing the metadata for each NFT to be minted
|
|
1474
|
+
:param target_list: [Optional] a list of targets for transfering minted NFTs (aka airdrop)
|
|
1475
|
+
:param mint_number_start: [Optional] The starting point for mint number used in intermediate launcher
|
|
1476
|
+
puzzle. Default: 1
|
|
1477
|
+
:param mint_total: [Optional] The total number of NFTs being minted
|
|
1478
|
+
:param xch_coins: [Optional] For use with bulk minting to provide the coin used for funding the minting spend.
|
|
1479
|
+
This coin can be one that will be created in the future
|
|
1480
|
+
:param xch_change_ph: [Optional] For use with bulk minting, so we can specify the puzzle hash that the change
|
|
1481
|
+
from the funding transaction goes to.
|
|
1482
|
+
:param fee: A fee amount, taken out of the xch spend.
|
|
1483
|
+
"""
|
|
1484
|
+
|
|
1485
|
+
# Ensure we have an mint_total value
|
|
1486
|
+
if mint_total is None:
|
|
1487
|
+
mint_total = len(metadata_list)
|
|
1488
|
+
assert isinstance(mint_number_start, int)
|
|
1489
|
+
assert len(metadata_list) <= mint_total + 1 - mint_number_start
|
|
1490
|
+
|
|
1491
|
+
# Ensure we have an xch coin of high enough amount
|
|
1492
|
+
assert isinstance(fee, uint64)
|
|
1493
|
+
total_amount = len(metadata_list) + fee
|
|
1494
|
+
if xch_coins is None:
|
|
1495
|
+
xch_coins = await self.standard_wallet.select_coins(uint64(total_amount), action_scope)
|
|
1496
|
+
assert len(xch_coins) > 0
|
|
1497
|
+
|
|
1498
|
+
funding_coin = xch_coins.copy().pop()
|
|
1499
|
+
|
|
1500
|
+
# set the chunk size for the spend bundle we're going to create
|
|
1501
|
+
chunk_size = len(metadata_list)
|
|
1502
|
+
|
|
1503
|
+
# Because bulk minting may not mint all the NFTs in one bundle, we
|
|
1504
|
+
# calculate the edition numbers that will be used in the intermediate
|
|
1505
|
+
# puzzle based on the starting edition number given, and the size of the
|
|
1506
|
+
# chunk going into this spend bundle
|
|
1507
|
+
mint_number_end = mint_number_start + chunk_size
|
|
1508
|
+
|
|
1509
|
+
# Empty set to load with the announcements we will assert from XCH to
|
|
1510
|
+
# match the announcements from the intermediate launcher puzzle
|
|
1511
|
+
coin_announcements: set[bytes32] = set()
|
|
1512
|
+
puzzle_assertions: set[bytes32] = set()
|
|
1513
|
+
primaries = []
|
|
1514
|
+
amount = uint64(1)
|
|
1515
|
+
intermediate_coin_spends = []
|
|
1516
|
+
launcher_spends = []
|
|
1517
|
+
launcher_ids = []
|
|
1518
|
+
p2_inner_puzzle = await self.standard_wallet.get_new_puzzle()
|
|
1519
|
+
p2_inner_ph = p2_inner_puzzle.get_tree_hash()
|
|
1520
|
+
|
|
1521
|
+
# Loop to create each intermediate coin, launcher, eve and (optional) transfer spends
|
|
1522
|
+
for mint_number in range(mint_number_start, mint_number_end):
|
|
1523
|
+
# Create the puzzle, solution and coin spend for the intermediate launcher
|
|
1524
|
+
intermediate_launcher_puz = nft_puzzles.INTERMEDIATE_LAUNCHER_MOD.curry(
|
|
1525
|
+
nft_puzzles.LAUNCHER_PUZZLE_HASH, mint_number, mint_total
|
|
1526
|
+
)
|
|
1527
|
+
intermediate_launcher_ph = intermediate_launcher_puz.get_tree_hash()
|
|
1528
|
+
primaries.append(Payment(intermediate_launcher_ph, uint64(1), [intermediate_launcher_ph]))
|
|
1529
|
+
intermediate_launcher_sol = Program.to([])
|
|
1530
|
+
intermediate_launcher_coin = Coin(funding_coin.name(), intermediate_launcher_ph, uint64(1))
|
|
1531
|
+
intermediate_launcher_coin_spend = make_spend(
|
|
1532
|
+
intermediate_launcher_coin, intermediate_launcher_puz, intermediate_launcher_sol
|
|
1533
|
+
)
|
|
1534
|
+
intermediate_coin_spends.append(intermediate_launcher_coin_spend)
|
|
1535
|
+
|
|
1536
|
+
# create an ASSERT_COIN_ANNOUNCEMENT for the XCH spend. The
|
|
1537
|
+
# intermediate launcher coin issues a CREATE_COIN_ANNOUNCEMENT of
|
|
1538
|
+
# the mint_number and mint_total for the launcher coin it creates
|
|
1539
|
+
intermediate_announcement_message = std_hash(int_to_bytes(mint_number) + int_to_bytes(mint_total))
|
|
1540
|
+
coin_announcements.add(std_hash(intermediate_launcher_coin.name() + intermediate_announcement_message))
|
|
1541
|
+
|
|
1542
|
+
# Create the launcher coin, and add its id to a list to be asserted in the XCH spend
|
|
1543
|
+
launcher_coin = Coin(intermediate_launcher_coin.name(), nft_puzzles.LAUNCHER_PUZZLE_HASH, amount)
|
|
1544
|
+
launcher_ids.append(launcher_coin.name())
|
|
1545
|
+
|
|
1546
|
+
# Grab the metadata from metadata_list. The index for metadata_list
|
|
1547
|
+
# needs to be offset by mint_number_start, and since
|
|
1548
|
+
# mint_number starts at 1 not 0, we also subtract 1.
|
|
1549
|
+
metadata = metadata_list[mint_number - mint_number_start]
|
|
1550
|
+
|
|
1551
|
+
# Create the inner and full puzzles for the eve spend
|
|
1552
|
+
inner_puzzle = create_ownership_layer_puzzle(
|
|
1553
|
+
launcher_coin.name(),
|
|
1554
|
+
b"",
|
|
1555
|
+
p2_inner_puzzle,
|
|
1556
|
+
metadata["royalty_pc"],
|
|
1557
|
+
royalty_puzzle_hash=metadata["royalty_ph"],
|
|
1558
|
+
)
|
|
1559
|
+
eve_fullpuz = nft_puzzles.create_full_puzzle(
|
|
1560
|
+
launcher_coin.name(), metadata["program"], NFT_METADATA_UPDATER.get_tree_hash(), inner_puzzle
|
|
1561
|
+
)
|
|
1562
|
+
|
|
1563
|
+
# Annnouncements for eve spend. These are asserted by the xch spend
|
|
1564
|
+
announcement_message = Program.to([eve_fullpuz.get_tree_hash(), amount, []]).get_tree_hash()
|
|
1565
|
+
coin_announcements.add(std_hash(launcher_coin.name() + announcement_message))
|
|
1566
|
+
|
|
1567
|
+
genesis_launcher_solution = Program.to([eve_fullpuz.get_tree_hash(), amount, []])
|
|
1568
|
+
|
|
1569
|
+
launcher_cs = make_spend(launcher_coin, nft_puzzles.LAUNCHER_PUZZLE, genesis_launcher_solution)
|
|
1570
|
+
launcher_spends.append(launcher_cs)
|
|
1571
|
+
|
|
1572
|
+
eve_coin = Coin(launcher_coin.name(), eve_fullpuz.get_tree_hash(), uint64(amount))
|
|
1573
|
+
|
|
1574
|
+
# To make the eve transaction we need to construct the NFTCoinInfo
|
|
1575
|
+
# for the NFT (which doesn't exist yet)
|
|
1576
|
+
nft_coin = NFTCoinInfo(
|
|
1577
|
+
nft_id=launcher_coin.name(),
|
|
1578
|
+
coin=eve_coin,
|
|
1579
|
+
lineage_proof=LineageProof(
|
|
1580
|
+
parent_name=launcher_coin.parent_coin_info, amount=uint64(launcher_coin.amount)
|
|
1581
|
+
),
|
|
1582
|
+
full_puzzle=eve_fullpuz,
|
|
1583
|
+
mint_height=uint32(0),
|
|
1584
|
+
)
|
|
1585
|
+
|
|
1586
|
+
# Create the eve transaction with targets if present
|
|
1587
|
+
if target_list:
|
|
1588
|
+
target_ph = target_list[mint_number - mint_number_start]
|
|
1589
|
+
else:
|
|
1590
|
+
target_ph = p2_inner_ph
|
|
1591
|
+
async with self.wallet_state_manager.new_action_scope(
|
|
1592
|
+
action_scope.config.tx_config, push=False
|
|
1593
|
+
) as inner_action_scope:
|
|
1594
|
+
await self.generate_signed_transaction(
|
|
1595
|
+
[uint64(eve_coin.amount)],
|
|
1596
|
+
[target_ph],
|
|
1597
|
+
inner_action_scope,
|
|
1598
|
+
nft_coin=nft_coin,
|
|
1599
|
+
new_owner=b"",
|
|
1600
|
+
new_did_inner_hash=b"",
|
|
1601
|
+
additional_bundles=[],
|
|
1602
|
+
memos=[[target_ph]],
|
|
1603
|
+
)
|
|
1604
|
+
|
|
1605
|
+
async with action_scope.use() as interface:
|
|
1606
|
+
interface.side_effects.transactions.extend(inner_action_scope.side_effects.transactions)
|
|
1607
|
+
|
|
1608
|
+
eve_sb = next(
|
|
1609
|
+
tx.spend_bundle for tx in inner_action_scope.side_effects.transactions if tx.spend_bundle is not None
|
|
1610
|
+
)
|
|
1611
|
+
# Extract Puzzle Announcement from eve spend
|
|
1612
|
+
eve_sol = eve_sb.coin_spends[0].solution.to_program()
|
|
1613
|
+
conds = eve_fullpuz.run(eve_sol)
|
|
1614
|
+
eve_puzzle_announcement = next(x for x in conds.as_python() if int_from_bytes(x[0]) == 62)[1]
|
|
1615
|
+
assertion = std_hash(eve_fullpuz.get_tree_hash() + eve_puzzle_announcement)
|
|
1616
|
+
puzzle_assertions.add(assertion)
|
|
1617
|
+
|
|
1618
|
+
# We've now created all the intermediate, launcher, eve and transfer spends.
|
|
1619
|
+
# Create the xch spend to fund the minting.
|
|
1620
|
+
spend_value = sum(coin.amount for coin in xch_coins)
|
|
1621
|
+
change: uint64 = uint64(spend_value - total_amount)
|
|
1622
|
+
xch_spends = []
|
|
1623
|
+
if xch_change_ph is None:
|
|
1624
|
+
xch_change_ph = await self.standard_wallet.get_new_puzzlehash()
|
|
1625
|
+
xch_payment = Payment(xch_change_ph, change, [xch_change_ph])
|
|
1626
|
+
|
|
1627
|
+
first = True
|
|
1628
|
+
for xch_coin in xch_coins:
|
|
1629
|
+
puzzle: Program = await self.standard_wallet.puzzle_for_puzzle_hash(xch_coin.puzzle_hash)
|
|
1630
|
+
if first:
|
|
1631
|
+
message_list: list[bytes32] = [c.name() for c in xch_coins]
|
|
1632
|
+
message_list.append(Coin(xch_coin.name(), xch_payment.puzzle_hash, xch_payment.amount).name())
|
|
1633
|
+
message: bytes32 = std_hash(b"".join(message_list))
|
|
1634
|
+
|
|
1635
|
+
if len(xch_coins) > 1:
|
|
1636
|
+
extra_conditions += (CreateCoinAnnouncement(message),)
|
|
1637
|
+
extra_conditions += tuple(AssertCoinAnnouncement(ann) for ann in coin_announcements)
|
|
1638
|
+
extra_conditions += tuple(AssertPuzzleAnnouncement(ann) for ann in puzzle_assertions)
|
|
1639
|
+
|
|
1640
|
+
solution: Program = self.standard_wallet.make_solution(
|
|
1641
|
+
primaries=[xch_payment, *primaries],
|
|
1642
|
+
fee=fee,
|
|
1643
|
+
conditions=extra_conditions,
|
|
1644
|
+
)
|
|
1645
|
+
primary_announcement = AssertCoinAnnouncement(asserted_id=xch_coin.name(), asserted_msg=message)
|
|
1646
|
+
first = False
|
|
1647
|
+
else:
|
|
1648
|
+
solution = self.standard_wallet.make_solution(primaries=[], conditions=(primary_announcement,))
|
|
1649
|
+
xch_spends.append(make_spend(xch_coin, puzzle, solution))
|
|
1650
|
+
|
|
1651
|
+
# Collect up all the coin spends and sign them
|
|
1652
|
+
list_of_coinspends = intermediate_coin_spends + launcher_spends + xch_spends
|
|
1653
|
+
unsigned_spend_bundle = WalletSpendBundle(list_of_coinspends, G2Element())
|
|
1654
|
+
|
|
1655
|
+
# Aggregate everything into a single spend bundle
|
|
1656
|
+
async with action_scope.use() as interface:
|
|
1657
|
+
# This should not be looked to for best practice. I think many of the spends generated above could call
|
|
1658
|
+
# wallet methods that generate transactions and prevent most of the need for this. Refactoring this function
|
|
1659
|
+
# is out of scope so for now we're using this hack.
|
|
1660
|
+
if interface.side_effects.transactions[0].spend_bundle is None:
|
|
1661
|
+
new_spend = unsigned_spend_bundle
|
|
1662
|
+
else:
|
|
1663
|
+
new_spend = WalletSpendBundle.aggregate(
|
|
1664
|
+
[interface.side_effects.transactions[0].spend_bundle, unsigned_spend_bundle]
|
|
1665
|
+
)
|
|
1666
|
+
interface.side_effects.transactions[0] = dataclasses.replace(
|
|
1667
|
+
interface.side_effects.transactions[0], spend_bundle=new_spend, name=new_spend.name()
|
|
1668
|
+
)
|
|
1669
|
+
|
|
1670
|
+
async def select_coins(
|
|
1671
|
+
self,
|
|
1672
|
+
amount: uint64,
|
|
1673
|
+
action_scope: WalletActionScope,
|
|
1674
|
+
) -> set[Coin]:
|
|
1675
|
+
raise RuntimeError("NFTWallet does not support select_coins()")
|
|
1676
|
+
|
|
1677
|
+
def require_derivation_paths(self) -> bool:
|
|
1678
|
+
return False
|
|
1679
|
+
|
|
1680
|
+
def puzzle_hash_for_pk(self, pubkey: G1Element) -> bytes32:
|
|
1681
|
+
raise RuntimeError("NFTWallet does not support puzzle_hash_for_pk")
|
|
1682
|
+
|
|
1683
|
+
def get_name(self) -> str:
|
|
1684
|
+
return self.wallet_info.name
|
|
1685
|
+
|
|
1686
|
+
async def match_hinted_coin(self, coin: Coin, hint: bytes32) -> bool:
|
|
1687
|
+
return False
|