[CHIA-3608] Port take_offer to @marshal (#20061)

* Port `take_offer` to `@marshal`

* `TransactionEndpointRequest` misses
This commit is contained in:
Matt Hauff
2025-11-06 09:35:24 -07:00
committed by GitHub
parent 8c50242410
commit 74ec8a44d4
7 changed files with 81 additions and 73 deletions
+11 -9
View File
@@ -75,6 +75,7 @@ from chia.wallet.wallet_request_types import (
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 @@ def test_take_offer(capsys: object, get_test_cli_clients: tuple[TestRpcClients,
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(),
),
+11 -1
View File
@@ -160,6 +160,7 @@ from chia.wallet.wallet_request_types import (
SetWalletResyncOnStartup,
SpendClawbackCoins,
SplitCoins,
TakeOffer,
VerifySignature,
VerifySignatureResponse,
)
@@ -1647,7 +1648,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)
+3 -4
View File
@@ -88,6 +88,7 @@ from chia.wallet.wallet_request_types import (
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}")
+22 -19
View File
@@ -81,6 +81,7 @@ from chia.wallet.wallet_request_types import (
DLUpdateRoot,
LauncherRootPair,
LogIn,
TakeOffer,
)
from chia.wallet.wallet_rpc_client import WalletRpcClient
@@ -1241,22 +1242,26 @@ class DataLayer:
)
)
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
},
}
)
# Excluding wallet from transaction since failures in the wallet may occur
# after the transaction is submitted to the chain. If we roll back data we
@@ -1264,9 +1269,7 @@ class DataLayer:
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,
+11
View File
@@ -1930,6 +1930,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
+16 -24
View File
@@ -70,7 +70,7 @@ from chia.wallet.nft_wallet.nft_wallet import NFTWallet
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
@@ -283,6 +283,8 @@ from chia.wallet.wallet_request_types import (
StrayCAT,
SubmitTransactions,
SubmitTransactionsResponse,
TakeOffer,
TakeOfferResponse,
TransactionRecordWithMetadata,
VCAddProofs,
VCGet,
@@ -2322,44 +2324,34 @@ class WalletRpcApi:
)
@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
+7 -16
View File
@@ -192,6 +192,7 @@ from chia.wallet.wallet_request_types import (
SplitCoinsResponse,
SubmitTransactions,
SubmitTransactionsResponse,
TakeOffer,
TakeOfferResponse,
VCAddProofs,
VCGet,
@@ -690,26 +691,16 @@ class WalletRpcClient(RpcClient):
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})