From 1b4972d297a7de292f1504a33408605b86b06278 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 14 Aug 2025 16:51:27 -0700 Subject: [PATCH 1/3] Port `send_transaction` --- chia/_tests/pools/test_pool_rpc.py | 9 +- .../wallet/cat_wallet/test_cat_wallet.py | 9 +- chia/_tests/wallet/rpc/test_wallet_rpc.py | 91 +++++++++++++------ chia/cmds/wallet_funcs.py | 34 ++++--- chia/wallet/wallet_request_types.py | 39 +++++++- chia/wallet/wallet_rpc_api.py | 42 ++++----- chia/wallet/wallet_rpc_client.py | 30 ++---- 7 files changed, 157 insertions(+), 97 deletions(-) diff --git a/chia/_tests/pools/test_pool_rpc.py b/chia/_tests/pools/test_pool_rpc.py index 08883d41b947..60279052427c 100644 --- a/chia/_tests/pools/test_pool_rpc.py +++ b/chia/_tests/pools/test_pool_rpc.py @@ -48,6 +48,7 @@ PWJoinPool, PWSelfPool, PWStatus, + SendTransaction, ) from chia.wallet.wallet_rpc_client import WalletRpcClient from chia.wallet.wallet_service import WalletService @@ -590,7 +591,13 @@ async def test_absorb_self( tr: TransactionRecord = ( await client.send_transaction( - 1, uint64(100), encode_puzzle_hash(status.p2_singleton_puzzle_hash, "txch"), DEFAULT_TX_CONFIG + SendTransaction( + wallet_id=uint32(1), + amount=uint64(100), + address=encode_puzzle_hash(status.p2_singleton_puzzle_hash, "txch"), + push=True, + ), + DEFAULT_TX_CONFIG, ) ).transaction diff --git a/chia/_tests/wallet/cat_wallet/test_cat_wallet.py b/chia/_tests/wallet/cat_wallet/test_cat_wallet.py index 802a06b015dd..4fc1d16baf41 100644 --- a/chia/_tests/wallet/cat_wallet/test_cat_wallet.py +++ b/chia/_tests/wallet/cat_wallet/test_cat_wallet.py @@ -44,7 +44,7 @@ from chia.wallet.wallet_info import WalletInfo from chia.wallet.wallet_interested_store import WalletInterestedStore from chia.wallet.wallet_node import WalletNode -from chia.wallet.wallet_request_types import GetTransactionMemo, PushTX +from chia.wallet.wallet_request_types import GetTransactionMemo, PushTX, SendTransaction from chia.wallet.wallet_state_manager import WalletStateManager @@ -1429,7 +1429,12 @@ async def test_cat_change_detection(wallet_environments: WalletTestFramework, wa cat_amount_0 = uint64(100) cat_amount_1 = uint64(5) - tx = (await env.rpc_client.send_transaction(1, cat_amount_0, addr, wallet_environments.tx_config)).transaction + tx = ( + await env.rpc_client.send_transaction( + SendTransaction(wallet_id=uint32(1), amount=cat_amount_0, address=addr, push=True), + wallet_environments.tx_config, + ) + ).transaction spend_bundle = tx.spend_bundle assert spend_bundle is not None diff --git a/chia/_tests/wallet/rpc/test_wallet_rpc.py b/chia/_tests/wallet/rpc/test_wallet_rpc.py index d714fc47a03b..ef33f13467f0 100644 --- a/chia/_tests/wallet/rpc/test_wallet_rpc.py +++ b/chia/_tests/wallet/rpc/test_wallet_rpc.py @@ -105,6 +105,7 @@ from chia.wallet.wallet_request_types import ( AddKey, CheckDeleteKey, + ClawbackPuzzleDecoratorOverride, CombineCoins, DefaultCAT, DeleteKey, @@ -136,6 +137,7 @@ PushTransactions, PushTX, RoyaltyAsset, + SendTransaction, SetWalletResyncOnStartup, SplitCoins, VerifySignature, @@ -378,24 +380,25 @@ async def test_send_transaction(wallet_rpc_environment: WalletRpcTestEnvironment addr = encode_puzzle_hash(await action_scope.get_puzzle_hash(wallet_2.wallet_state_manager), "txch") tx_amount = uint64(15600000) with pytest.raises(ValueError): - await client.send_transaction(1, uint64(100000000000000001), addr, DEFAULT_TX_CONFIG) + await client.send_transaction( + SendTransaction(wallet_id=uint32(1), amount=uint64(100000000000000001), address=addr, push=True), + DEFAULT_TX_CONFIG, + ) # Tests sending a basic transaction extra_conditions = (Remark(Program.to(("test", None))),) non_existent_coin = Coin(bytes32.zeros, bytes32.zeros, uint64(0)) tx_no_push = ( await client.send_transaction( - 1, - tx_amount, - addr, - memos=["this is a basic tx"], + SendTransaction( + wallet_id=uint32(1), amount=tx_amount, address=addr, memos=["this is a basic tx"], push=False + ), tx_config=DEFAULT_TX_CONFIG.override( excluded_coin_amounts=[uint64(250000000000)], excluded_coin_ids=[non_existent_coin.name()], reuse_puzhash=True, ), extra_conditions=extra_conditions, - push=False, ) ).transaction response = await client.fetch( @@ -837,12 +840,14 @@ async def test_spend_clawback_coins(wallet_rpc_environment: WalletRpcTestEnviron wallet_2_puzhash = await action_scope.get_puzzle_hash(wallet_2.wallet_state_manager) tx = ( await wallet_1_rpc.send_transaction( - wallet_id=1, - amount=uint64(500), - address=encode_puzzle_hash(wallet_2_puzhash, "txch"), + SendTransaction( + wallet_id=uint32(1), + amount=uint64(500), + address=encode_puzzle_hash(wallet_2_puzhash, "txch"), + puzzle_decorator=[ClawbackPuzzleDecoratorOverride(decorator="CLAWBACK", clawback_timelock=uint64(5))], + push=True, + ), tx_config=DEFAULT_TX_CONFIG, - fee=uint64(0), - puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}], ) ).transaction clawback_coin_id_1 = tx.additions[0].name() @@ -851,12 +856,14 @@ async def test_spend_clawback_coins(wallet_rpc_environment: WalletRpcTestEnviron await full_node_api.wait_for_wallet_synced(wallet_node=wallet_2_node, timeout=20) tx = ( await wallet_2_rpc.send_transaction( - wallet_id=1, - amount=uint64(500), - address=encode_puzzle_hash(wallet_1_puzhash, "txch"), + SendTransaction( + wallet_id=uint32(1), + amount=uint64(500), + address=encode_puzzle_hash(wallet_1_puzhash, "txch"), + puzzle_decorator=[ClawbackPuzzleDecoratorOverride(decorator="CLAWBACK", clawback_timelock=uint64(5))], + push=True, + ), tx_config=DEFAULT_TX_CONFIG, - fee=uint64(0), - puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}], ) ).transaction assert tx.spend_bundle is not None @@ -1019,7 +1026,8 @@ async def test_get_transactions(wallet_rpc_environment: WalletRpcTestEnvironment puzhash = await action_scope.get_puzzle_hash(wallet.wallet_state_manager) await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20) await client.send_transaction( - 1, uint64(1), encode_puzzle_hash(puzhash, "txch"), DEFAULT_TX_CONFIG + SendTransaction(wallet_id=uint32(1), amount=uint64(1), address=encode_puzzle_hash(puzhash, "txch"), push=True), + DEFAULT_TX_CONFIG, ) # Create a pending tx with pytest.raises(ValueError, match="There is no known sort foo"): @@ -1045,7 +1053,12 @@ async def test_get_transactions(wallet_rpc_environment: WalletRpcTestEnvironment async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: ph_by_addr = await action_scope.get_puzzle_hash(wallet.wallet_state_manager) await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20) - await client.send_transaction(1, uint64(1), encode_puzzle_hash(ph_by_addr, "txch"), DEFAULT_TX_CONFIG) + await client.send_transaction( + SendTransaction( + wallet_id=uint32(1), amount=uint64(1), address=encode_puzzle_hash(ph_by_addr, "txch"), push=True + ), + DEFAULT_TX_CONFIG, + ) await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20) tx_for_address = ( await client.get_transactions(GetTransactions(uint32(1), to_address=encode_puzzle_hash(ph_by_addr, "txch"))) @@ -1795,7 +1808,12 @@ async def test_get_coin_records_by_names(wallet_rpc_environment: WalletRpcTestEn await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20) # Spend half of it back to the same wallet get some spent coins in the wallet - tx = (await client.send_transaction(1, uint64(generated_funds / 2), address, DEFAULT_TX_CONFIG)).transaction + tx = ( + await client.send_transaction( + SendTransaction(wallet_id=uint32(1), amount=uint64(generated_funds / 2), address=address, push=True), + DEFAULT_TX_CONFIG, + ) + ).transaction assert tx.spend_bundle is not None await time_out_assert(20, tx_in_mempool, True, client, tx.name) await farm_transaction(full_node_api, wallet_node, tx.spend_bundle) @@ -2161,7 +2179,11 @@ async def test_key_and_address_endpoints(wallet_rpc_environment: WalletRpcTestEn addr = encode_puzzle_hash(ph, "txch") tx_amount = uint64(15600000) await env.full_node.api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20) - created_tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction + created_tx = ( + await client.send_transaction( + SendTransaction(wallet_id=uint32(1), amount=tx_amount, address=addr, push=True), DEFAULT_TX_CONFIG + ) + ).transaction await time_out_assert(20, tx_in_mempool, True, client, created_tx.name) assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 1 @@ -2224,7 +2246,10 @@ async def test_key_and_address_endpoints(wallet_rpc_environment: WalletRpcTestEn assert await get_unconfirmed_balance(client, int(wallets[0].id)) == 0 with pytest.raises(ValueError): - await client.send_transaction(wallets[0].id, uint64(100), addr, DEFAULT_TX_CONFIG) + await client.send_transaction( + SendTransaction(wallet_id=uint32(wallets[0].id), amount=uint64(100), address=addr, push=True), + DEFAULT_TX_CONFIG, + ) # Delete all keys await client.delete_all_keys() @@ -2250,7 +2275,11 @@ async def test_select_coins_rpc(wallet_rpc_environment: WalletRpcTestEnvironment for tx_amount in tx_amounts: funds -= tx_amount # create coins for tests - tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction + tx = ( + await client.send_transaction( + SendTransaction(wallet_id=uint32(1), amount=tx_amount, address=addr, push=True), DEFAULT_TX_CONFIG + ) + ).transaction spend_bundle = tx.spend_bundle assert spend_bundle is not None for coin in spend_bundle.additions(): @@ -2811,12 +2840,14 @@ async def test_set_wallet_resync_on_startup(wallet_rpc_environment: WalletRpcTes # Test Clawback resync tx = ( await wc.send_transaction( - wallet_id=1, - amount=uint64(500), - address=address, + SendTransaction( + wallet_id=uint32(1), + amount=uint64(500), + address=address, + puzzle_decorator=[ClawbackPuzzleDecoratorOverride(decorator="CLAWBACK", clawback_timelock=uint64(5))], + push=True, + ), tx_config=DEFAULT_TX_CONFIG, - fee=uint64(0), - puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}], ) ).transaction clawback_coin_id = tx.additions[0].name() @@ -2959,7 +2990,11 @@ async def test_cat_spend_run_tail(wallet_rpc_environment: WalletRpcTestEnvironme ) tx_amount = uint64(100) - tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction + tx = ( + await client.send_transaction( + SendTransaction(wallet_id=uint32(1), amount=uint64(500), address=addr, push=True), DEFAULT_TX_CONFIG + ) + ).transaction transaction_id = tx.name spend_bundle = tx.spend_bundle assert spend_bundle is not None diff --git a/chia/cmds/wallet_funcs.py b/chia/cmds/wallet_funcs.py index a4cf58ea5afa..3605fcd46ad2 100644 --- a/chia/cmds/wallet_funcs.py +++ b/chia/cmds/wallet_funcs.py @@ -45,6 +45,7 @@ from chia.wallet.wallet_coin_store import GetCoinRecords from chia.wallet.wallet_request_types import ( CATSpendResponse, + ClawbackPuzzleDecoratorOverride, DIDFindLostDID, DIDGetDID, DIDGetInfo, @@ -68,6 +69,7 @@ NFTSetNFTDID, NFTTransferNFT, RoyaltyAsset, + SendTransaction, SendTransactionResponse, VCAddProofs, VCGet, @@ -327,7 +329,7 @@ async def send( ) -> list[TransactionRecord]: async with get_wallet_client(root_path, wallet_rpc_port, fp) as (wallet_client, fingerprint, config): if memo is None: - memos = None + memos = [] else: memos = [memo] @@ -356,23 +358,29 @@ async def send( if typ == WalletType.STANDARD_WALLET: print("Submitting transaction...") res: Union[CATSpendResponse, SendTransactionResponse] = await wallet_client.send_transaction( - wallet_id, - final_amount, - address.original_address, - CMDTXConfigLoader( + SendTransaction( + wallet_id=uint32(wallet_id), + amount=final_amount, + address=address.original_address, + fee=fee, + memos=memos, + push=push, + puzzle_decorator=( + [ + ClawbackPuzzleDecoratorOverride( + PuzzleDecoratorType.CLAWBACK.name, clawback_timelock=uint64(clawback_time_lock) + ) + ] + if clawback_time_lock > 0 + else None + ), + ), + tx_config=CMDTXConfigLoader( min_coin_amount=min_coin_amount, max_coin_amount=max_coin_amount, excluded_coin_ids=list(excluded_coin_ids), reuse_puzhash=reuse_puzhash, ).to_tx_config(mojo_per_unit, config, fingerprint), - fee, - memos, - puzzle_decorator_override=( - [{"decorator": PuzzleDecoratorType.CLAWBACK.name, "clawback_timelock": clawback_time_lock}] - if clawback_time_lock > 0 - else None - ), - push=push, timelock_info=condition_valid_times, ) elif typ in {WalletType.CAT, WalletType.CRCAT, WalletType.RCAT}: diff --git a/chia/wallet/wallet_request_types.py b/chia/wallet/wallet_request_types.py index 10c56d1b78b8..5bda1559a1cb 100644 --- a/chia/wallet/wallet_request_types.py +++ b/chia/wallet/wallet_request_types.py @@ -30,6 +30,7 @@ from chia.wallet.transaction_record import TransactionRecord from chia.wallet.transaction_sorting import SortKey from chia.wallet.util.clvm_streamable import json_deserialize_with_clvm_streamable +from chia.wallet.util.puzzle_decorator_type import PuzzleDecoratorType from chia.wallet.util.query_filter import TransactionTypeFilter from chia.wallet.util.tx_config import TXConfig from chia.wallet.vc_wallet.vc_store import VCProofs, VCRecord @@ -1017,6 +1018,39 @@ class TransactionEndpointResponse(Streamable): transactions: list[TransactionRecord] +# utility for SendTransaction +@streamable +@dataclass(frozen=True) +class ClawbackPuzzleDecoratorOverride(Streamable): + decorator: str + clawback_timelock: uint64 + + def __post_init__(self) -> None: + if self.decorator != PuzzleDecoratorType.CLAWBACK.name: + raise ValueError("Invalid clawback puzzle decorator override specified") + super().__post_init__() + + +@streamable +@dataclass(frozen=True) +class SendTransaction(TransactionEndpointRequest): + wallet_id: uint32 = field(default_factory=default_raise) + amount: uint64 = field(default_factory=default_raise) + address: str = field(default_factory=default_raise) + memos: list[str] = field(default_factory=list) + # Technically this value was meant to support many types here + # However, only one is supported right now and there are no plans to extend + # So, as a slight hack, we'll specify that only Clawback is supported + puzzle_decorator: Optional[list[ClawbackPuzzleDecoratorOverride]] = None + + +@streamable +@dataclass(frozen=True) +class SendTransactionResponse(TransactionEndpointResponse): + transaction: TransactionRecord + transaction_id: bytes32 + + @streamable @dataclass(frozen=True) class PushTransactions(TransactionEndpointRequest): @@ -1452,11 +1486,6 @@ class VCRevokeResponse(TransactionEndpointResponse): # TODO: The section below needs corresponding request types # TODO: The section below should be added to the API (currently only for client) -@streamable -@dataclass(frozen=True) -class SendTransactionResponse(TransactionEndpointResponse): - transaction: TransactionRecord - transaction_id: bytes32 @streamable diff --git a/chia/wallet/wallet_rpc_api.py b/chia/wallet/wallet_rpc_api.py index a7955f7a1f4a..f471db9ff531 100644 --- a/chia/wallet/wallet_rpc_api.py +++ b/chia/wallet/wallet_rpc_api.py @@ -233,6 +233,8 @@ PWSelfPoolResponse, PWStatus, PWStatusResponse, + SendTransaction, + SendTransactionResponse, SetWalletResyncOnStartup, SplitCoins, SplitCoinsResponse, @@ -1580,51 +1582,39 @@ async def get_next_address(self, request: dict[str, Any]) -> EndpointResult: } @tx_endpoint(push=True) + @marshal async def send_transaction( self, - request: dict[str, Any], + request: SendTransaction, action_scope: WalletActionScope, extra_conditions: tuple[Condition, ...] = tuple(), - ) -> EndpointResult: + ) -> SendTransactionResponse: if await self.service.wallet_state_manager.synced() is False: raise ValueError("Wallet needs to be fully synced before sending transactions") - wallet_id = uint32(request["wallet_id"]) - wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=Wallet) + wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=Wallet) # TODO: Add support for multiple puzhash/amount/memo sets - if not isinstance(request["amount"], int) or not isinstance(request["fee"], int): - raise ValueError("An integer amount or fee is required (too many decimals)") - amount: uint64 = uint64(request["amount"]) - address = request["address"] selected_network = self.service.config["selected_network"] expected_prefix = self.service.config["network_overrides"]["config"][selected_network]["address_prefix"] - if address[0 : len(expected_prefix)] != expected_prefix: + if request.address[0 : len(expected_prefix)] != expected_prefix: raise ValueError("Unexpected Address Prefix") - puzzle_hash: bytes32 = decode_puzzle_hash(address) - - memos: list[bytes] = [] - if "memos" in request: - memos = [mem.encode("utf-8") for mem in request["memos"]] - - fee: uint64 = uint64(request.get("fee", 0)) await wallet.generate_signed_transaction( - [amount], - [puzzle_hash], + [request.amount], + [decode_puzzle_hash(request.address)], action_scope, - fee, - memos=[memos], - puzzle_decorator_override=request.get("puzzle_decorator", None), + request.fee, + memos=[[mem.encode("utf-8") for mem in request.memos]], + puzzle_decorator_override=[request.puzzle_decorator[0].to_json_dict()] + if request.puzzle_decorator is not None + else None, extra_conditions=extra_conditions, ) # Transaction may not have been included in the mempool yet. Use get_transaction to check. - return { - "transaction": None, # tx_endpoint wrapper will take care of this - "transactions": None, # tx_endpoint wrapper will take care of this - "transaction_id": None, # tx_endpoint wrapper will take care of this - } + # tx_endpoint will take care of the default values here + return SendTransactionResponse([], [], transaction=REPLACEABLE_TRANSACTION_RECORD, transaction_id=bytes32.zeros) async def send_transaction_multi(self, request: dict[str, Any]) -> EndpointResult: if await self.service.wallet_state_manager.synced() is False: diff --git a/chia/wallet/wallet_rpc_client.py b/chia/wallet/wallet_rpc_client.py index d884302523ed..29d51b8c24f5 100644 --- a/chia/wallet/wallet_rpc_client.py +++ b/chia/wallet/wallet_rpc_client.py @@ -150,6 +150,7 @@ PWSelfPoolResponse, PWStatus, PWStatusResponse, + SendTransaction, SendTransactionMultiResponse, SendTransactionResponse, SetWalletResyncOnStartup, @@ -295,32 +296,17 @@ async def get_next_address(self, wallet_id: int, new_address: bool) -> str: async def send_transaction( self, - wallet_id: int, - amount: uint64, - address: str, + request: SendTransaction, tx_config: TXConfig, - fee: uint64 = uint64(0), - memos: Optional[list[str]] = None, - puzzle_decorator_override: Optional[list[dict[str, Union[str, int, bool]]]] = None, extra_conditions: tuple[Condition, ...] = tuple(), timelock_info: ConditionValidTimes = ConditionValidTimes(), - push: bool = True, ) -> SendTransactionResponse: - request = { - "wallet_id": wallet_id, - "amount": amount, - "address": address, - "fee": fee, - "puzzle_decorator": puzzle_decorator_override, - "extra_conditions": conditions_to_json_dicts(extra_conditions), - "push": push, - **tx_config.to_json_dict(), - **timelock_info.to_json_dict(), - } - if memos is not None: - request["memos"] = memos - response = await self.fetch("send_transaction", request) - return json_deserialize_with_clvm_streamable(response, SendTransactionResponse) + return SendTransactionResponse.from_json_dict( + await self.fetch( + "send_transaction", + request.json_serialize_for_transport(tx_config, extra_conditions, timelock_info), + ) + ) async def send_transaction_multi( self, From 241dea728e24177ecf8e1cc4efeac21637e6351b Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 15 Aug 2025 09:30:39 -0700 Subject: [PATCH 2/3] Have I mentioned this framework is suboptimal? --- chia/_tests/cmds/wallet/test_wallet.py | 27 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/chia/_tests/cmds/wallet/test_wallet.py b/chia/_tests/cmds/wallet/test_wallet.py index f0a384196c8d..ce19f73fd3d3 100644 --- a/chia/_tests/cmds/wallet/test_wallet.py +++ b/chia/_tests/cmds/wallet/test_wallet.py @@ -29,7 +29,7 @@ from chia.types.blockchain_format.program import Program from chia.types.signing_mode import SigningMode from chia.util.bech32m import encode_puzzle_hash -from chia.wallet.conditions import ConditionValidTimes +from chia.wallet.conditions import Condition, ConditionValidTimes from chia.wallet.trade_record import TradeRecord from chia.wallet.trading.offer import Offer from chia.wallet.trading.trade_status import TradeStatus @@ -44,6 +44,7 @@ BalanceResponse, CancelOfferResponse, CATSpendResponse, + ClawbackPuzzleDecoratorOverride, CreateOfferForIDsResponse, FungibleAsset, GetHeightInfoResponse, @@ -58,6 +59,7 @@ NFTGetWalletDID, NFTGetWalletDIDResponse, RoyaltyAsset, + SendTransaction, SendTransactionResponse, TakeOfferResponse, TransactionRecordWithMetadata, @@ -327,19 +329,24 @@ def test_send(capsys: object, get_test_cli_clients: tuple[TestRpcClients, Path]) class SendWalletRpcClient(TestWalletRpcClient): async def send_transaction( self, - wallet_id: int, - amount: uint64, - address: str, + request: SendTransaction, tx_config: TXConfig, - fee: uint64 = uint64(0), - memos: Optional[list[str]] = None, - puzzle_decorator_override: Optional[list[dict[str, Union[str, int, bool]]]] = None, - push: bool = True, + extra_conditions: tuple[Condition, ...] = tuple(), timelock_info: ConditionValidTimes = ConditionValidTimes(), ) -> SendTransactionResponse: self.add_to_log( "send_transaction", - (wallet_id, amount, address, tx_config, fee, memos, puzzle_decorator_override, push, timelock_info), + ( + request.wallet_id, + request.amount, + request.address, + tx_config, + request.fee, + request.memos, + request.puzzle_decorator, + request.push, + timelock_info, + ), ) name = get_bytes32(2) tx_rec = TransactionRecord( @@ -457,7 +464,7 @@ async def cat_spend( ), 500000000000, ["0x6262626262626262626262626262626262626262626262626262626262626262"], - [{"decorator": "CLAWBACK", "clawback_timelock": 60}], + [ClawbackPuzzleDecoratorOverride(decorator="CLAWBACK", clawback_timelock=uint64(60))], True, test_condition_valid_times, ) From 5872b9c04d1e8de41aeaa82fd7e0f8df915c83d7 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 15 Aug 2025 09:48:44 -0700 Subject: [PATCH 3/3] Fix tests --- chia/_tests/wallet/rpc/test_wallet_rpc.py | 2 +- chia/wallet/wallet_request_types.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chia/_tests/wallet/rpc/test_wallet_rpc.py b/chia/_tests/wallet/rpc/test_wallet_rpc.py index ef33f13467f0..3a5fd16b8f8b 100644 --- a/chia/_tests/wallet/rpc/test_wallet_rpc.py +++ b/chia/_tests/wallet/rpc/test_wallet_rpc.py @@ -2992,7 +2992,7 @@ async def test_cat_spend_run_tail(wallet_rpc_environment: WalletRpcTestEnvironme tx = ( await client.send_transaction( - SendTransaction(wallet_id=uint32(1), amount=uint64(500), address=addr, push=True), DEFAULT_TX_CONFIG + SendTransaction(wallet_id=uint32(1), amount=tx_amount, address=addr, push=True), DEFAULT_TX_CONFIG ) ).transaction transaction_id = tx.name diff --git a/chia/wallet/wallet_request_types.py b/chia/wallet/wallet_request_types.py index 5bda1559a1cb..254e07bcb16e 100644 --- a/chia/wallet/wallet_request_types.py +++ b/chia/wallet/wallet_request_types.py @@ -15,7 +15,7 @@ from chia.types.blockchain_format.program import Program from chia.util.byte_types import hexstr_to_bytes from chia.util.streamable import Streamable, streamable -from chia.wallet.conditions import Condition, ConditionValidTimes +from chia.wallet.conditions import Condition, ConditionValidTimes, conditions_to_json_dicts from chia.wallet.nft_wallet.nft_info import NFTInfo from chia.wallet.notification_store import Notification from chia.wallet.signer_protocol import ( @@ -1006,7 +1006,7 @@ def json_serialize_for_transport( return { **tx_config.to_json_dict(), **timelock_info.to_json_dict(), - "extra_conditions": [condition.to_json_dict() for condition in extra_conditions], + "extra_conditions": conditions_to_json_dicts(extra_conditions), **self.to_json_dict(_avoid_ban=True), }