diff --git a/chia/_tests/cmds/wallet/test_wallet.py b/chia/_tests/cmds/wallet/test_wallet.py index d0e97cfc57ff..981dfea8dbd6 100644 --- a/chia/_tests/cmds/wallet/test_wallet.py +++ b/chia/_tests/cmds/wallet/test_wallet.py @@ -75,6 +75,7 @@ SendTransactionResponse, SpendClawbackCoins, SpendClawbackCoinsResponse, + TakeOffer, TakeOfferResponse, TransactionRecordWithMetadata, WalletInfoResponse, @@ -1055,18 +1056,19 @@ def test_take_offer(capsys: object, get_test_cli_clients: tuple[TestRpcClients, class TakeOfferRpcClient(TestWalletRpcClient): async def take_offer( self, - offer: Offer, + request: TakeOffer, tx_config: TXConfig, - solver: Optional[dict[str, Any]] = None, - fee: uint64 = uint64(0), - push: bool = True, + extra_conditions: tuple[Condition, ...] = tuple(), timelock_info: ConditionValidTimes = ConditionValidTimes(), ) -> TakeOfferResponse: - self.add_to_log("take_offer", (offer, tx_config, solver, fee, push, timelock_info)) + self.add_to_log( + "take_offer", + (request.parsed_offer, tx_config, request.solver, request.fee, request.push, timelock_info), + ) return TakeOfferResponse( [STD_UTX], [STD_TX], - offer, + request.parsed_offer, TradeRecord( confirmed_at_index=uint32(0), accepted_at_time=uint64(123456789), @@ -1074,10 +1076,10 @@ async def take_offer( is_my_offer=False, sent=uint32(1), sent_to=[("aaaaa", uint8(1), None)], - offer=bytes(offer), + offer=bytes(request.parsed_offer), taken_offer=None, - coins_of_interest=offer.get_involved_coins(), - trade_id=offer.name(), + coins_of_interest=request.parsed_offer.get_involved_coins(), + trade_id=request.parsed_offer.name(), status=uint32(TradeStatus.PENDING_ACCEPT.value), valid_times=ConditionValidTimes(), ), diff --git a/chia/_tests/wallet/rpc/test_wallet_rpc.py b/chia/_tests/wallet/rpc/test_wallet_rpc.py index 25110c79ac79..459060da0d84 100644 --- a/chia/_tests/wallet/rpc/test_wallet_rpc.py +++ b/chia/_tests/wallet/rpc/test_wallet_rpc.py @@ -159,6 +159,7 @@ SetWalletResyncOnStartup, SpendClawbackCoins, SplitCoins, + TakeOffer, VerifySignature, VerifySignatureResponse, ) @@ -1607,7 +1608,16 @@ async def test_offer_endpoints(wallet_environments: WalletTestFramework, wallet_ assert offer_count.my_offers_count == 1 assert offer_count.taken_offers_count == 0 - trade_record = (await env_2.rpc_client.take_offer(offer, wallet_environments.tx_config, fee=uint64(1))).trade_record + trade_record = ( + await env_2.rpc_client.take_offer( + TakeOffer( + offer=offer.to_bech32(), + fee=uint64(1), + push=True, + ), + wallet_environments.tx_config, + ) + ).trade_record assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CONFIRM await env_1.rpc_client.cancel_offer(offer.name(), wallet_environments.tx_config, secure=False) diff --git a/chia/cmds/wallet_funcs.py b/chia/cmds/wallet_funcs.py index 3f9172704e72..1b2ae3d91dad 100644 --- a/chia/cmds/wallet_funcs.py +++ b/chia/cmds/wallet_funcs.py @@ -88,6 +88,7 @@ SignMessageByID, SignMessageByIDResponse, SpendClawbackCoins, + TakeOffer, VCAddProofs, VCGet, VCGetList, @@ -892,11 +893,9 @@ async def take_offer( print() cli_confirm("Would you like to take this offer? (y/n): ") res = await wallet_client.take_offer( - offer, - fee=fee, - tx_config=CMDTXConfigLoader().to_tx_config(units["chia"], config, fingerprint), - push=push, + TakeOffer(offer=offer.to_bech32(), fee=fee, push=push), timelock_info=condition_valid_times, + tx_config=CMDTXConfigLoader().to_tx_config(units["chia"], config, fingerprint), ) if push: print(f"Accepted offer with ID {res.trade_record.trade_id}") diff --git a/chia/data_layer/data_layer.py b/chia/data_layer/data_layer.py index 4a7e14e023f9..03d636da4adb 100644 --- a/chia/data_layer/data_layer.py +++ b/chia/data_layer/data_layer.py @@ -83,6 +83,7 @@ DLUpdateRoot, LauncherRootPair, LogIn, + TakeOffer, ) from chia.wallet.wallet_rpc_client import WalletRpcClient @@ -1231,22 +1232,26 @@ async def take_offer( ) ) - solver: dict[str, Any] = { - "proofs_of_inclusion": proofs_of_inclusion, - **{ - "0x" + our_offer_store.store_id.hex(): { - "new_root": "0x" + root.hex(), - "dependencies": [ - { - "launcher_id": "0x" + their_offer_store.store_id.hex(), - "values_to_prove": ["0x" + entry.node_hash.hex() for entry in their_offer_store.proofs], - } - for their_offer_store in maker - ], - } - for our_offer_store in taker - }, - } + solver = Solver( + { + "proofs_of_inclusion": proofs_of_inclusion, + **{ + "0x" + our_offer_store.store_id.hex(): { + "new_root": "0x" + root.hex(), + "dependencies": [ + { + "launcher_id": "0x" + their_offer_store.store_id.hex(), + "values_to_prove": [ + "0x" + entry.node_hash.hex() for entry in their_offer_store.proofs + ], + } + for their_offer_store in maker + ], + } + for our_offer_store in taker + }, + } + ) await self.data_store.clean_node_table() @@ -1256,9 +1261,7 @@ async def take_offer( trade_record = ( await self.wallet_rpc.take_offer( - offer=offer, - solver=solver, - fee=fee, + TakeOffer(offer=offer.to_bech32(), solver=solver, fee=fee, push=True), # TODO: probably shouldn't be default but due to peculiarities in the RPC, we're using a stop gap. # This is not a change in behavior, the default was already implicit. tx_config=DEFAULT_TX_CONFIG, diff --git a/chia/wallet/wallet_request_types.py b/chia/wallet/wallet_request_types.py index a8b7b6212fb0..3e97b206704b 100644 --- a/chia/wallet/wallet_request_types.py +++ b/chia/wallet/wallet_request_types.py @@ -1938,6 +1938,17 @@ class CreateOfferForIDsResponse(_OfferEndpointResponse): pass +@streamable +@dataclass(frozen=True) +class TakeOffer(TransactionEndpointRequest): + offer: str = field(default_factory=default_raise) + solver: Optional[Solver] = None + + @cached_property + def parsed_offer(self) -> Offer: + return Offer.from_bech32(self.offer) + + @streamable @dataclass(frozen=True) class TakeOfferResponse(_OfferEndpointResponse): # Inheriting for de-dup sake diff --git a/chia/wallet/wallet_rpc_api.py b/chia/wallet/wallet_rpc_api.py index bb3b25647368..7294360e318c 100644 --- a/chia/wallet/wallet_rpc_api.py +++ b/chia/wallet/wallet_rpc_api.py @@ -71,7 +71,7 @@ from chia.wallet.nft_wallet.uncurry_nft import UncurriedNFT from chia.wallet.notification_store import Notification from chia.wallet.outer_puzzles import AssetType -from chia.wallet.puzzle_drivers import PuzzleInfo, Solver +from chia.wallet.puzzle_drivers import PuzzleInfo from chia.wallet.puzzles import p2_delegated_conditions from chia.wallet.puzzles.clawback.metadata import AutoClaimSettings, ClawbackMetadata from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_hash_for_synthetic_public_key @@ -284,6 +284,8 @@ StrayCAT, SubmitTransactions, SubmitTransactionsResponse, + TakeOffer, + TakeOfferResponse, TransactionRecordWithMetadata, VCAddProofs, VCGet, @@ -2324,44 +2326,34 @@ async def check_offer_validity(self, request: CheckOfferValidity) -> CheckOfferV ) @tx_endpoint(push=True) + @marshal async def take_offer( self, - request: dict[str, Any], + request: TakeOffer, action_scope: WalletActionScope, extra_conditions: tuple[Condition, ...] = tuple(), - ) -> EndpointResult: - offer_hex: str = request["offer"] - - offer = Offer.from_bech32(offer_hex) - fee: uint64 = uint64(request.get("fee", 0)) - maybe_marshalled_solver: Optional[dict[str, Any]] = request.get("solver") - solver: Optional[Solver] - if maybe_marshalled_solver is None: - solver = None - else: - solver = Solver(info=maybe_marshalled_solver) - + ) -> TakeOfferResponse: peer = self.service.get_full_node_peer() trade_record = await self.service.wallet_state_manager.trade_manager.respond_to_offer( - offer, + request.parsed_offer, peer, action_scope, - fee=fee, - solver=solver, + fee=request.fee, + solver=request.solver, extra_conditions=extra_conditions, ) async with action_scope.use() as interface: interface.side_effects.signing_responses.append( - SigningResponse(bytes(offer._bundle.aggregated_signature), trade_record.trade_id) + SigningResponse(bytes(request.parsed_offer._bundle.aggregated_signature), trade_record.trade_id) ) - return { - "trade_record": trade_record.to_json_dict_convenience(), - "offer": Offer.from_bytes(trade_record.offer).to_bech32(), - "transactions": None, # tx_endpoint wrapper will take care of this - "signing_responses": None, # tx_endpoint wrapper will take care of this - } + return TakeOfferResponse( + [], # tx_endpoint will fill in this default value + [], # tx_endpoint will fill in this default value + Offer.from_bytes(trade_record.offer), + trade_record, + ) async def get_offer(self, request: dict[str, Any]) -> EndpointResult: trade_mgr = self.service.wallet_state_manager.trade_manager diff --git a/chia/wallet/wallet_rpc_client.py b/chia/wallet/wallet_rpc_client.py index f4a42150e12e..6825ebc64332 100644 --- a/chia/wallet/wallet_rpc_client.py +++ b/chia/wallet/wallet_rpc_client.py @@ -192,6 +192,7 @@ SplitCoinsResponse, SubmitTransactions, SubmitTransactionsResponse, + TakeOffer, TakeOfferResponse, VCAddProofs, VCGet, @@ -690,26 +691,16 @@ async def check_offer_validity(self, request: CheckOfferValidity) -> CheckOfferV async def take_offer( self, - offer: Offer, + request: TakeOffer, tx_config: TXConfig, - solver: Optional[dict[str, Any]] = None, - fee: int = 0, extra_conditions: tuple[Condition, ...] = tuple(), timelock_info: ConditionValidTimes = ConditionValidTimes(), - push: bool = True, ) -> TakeOfferResponse: - req = { - "offer": offer.to_bech32(), - "fee": fee, - "extra_conditions": conditions_to_json_dicts(extra_conditions), - "push": push, - **tx_config.to_json_dict(), - **timelock_info.to_json_dict(), - } - if solver is not None: - req["solver"] = solver - res = await self.fetch("take_offer", req) - return json_deserialize_with_clvm_streamable(res, TakeOfferResponse) + return TakeOfferResponse.from_json_dict( + await self.fetch( + "take_offer", request.json_serialize_for_transport(tx_config, extra_conditions, timelock_info) + ) + ) async def get_offer(self, trade_id: bytes32, file_contents: bool = False) -> TradeRecord: res = await self.fetch("get_offer", {"trade_id": trade_id.hex(), "file_contents": file_contents})