commit f2197ab754d4cd226d5b7e46216e59ac7b888dff
parent 9b40d20d7193adb5a435aab16ea9a3ec81fd869c
Author: triesap <tyson@radroots.org>
Date: Fri, 8 May 2026 17:11:50 +0000
trade: remove legacy order request model
- remove the legacy `RadrootsTradeOrder` publish payload from events
- route order-request encoding and projection through active order-request events
- require radrootsd SDK order-request calls to include the listing event pointer
- validate events, codec, trade, SDK tests, formatting, scans, and sdk validator
Diffstat:
11 files changed, 542 insertions(+), 334 deletions(-)
diff --git a/crates/events/src/trade.rs b/crates/events/src/trade.rs
@@ -401,23 +401,6 @@ pub enum RadrootsTradeOrderStatus {
#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct RadrootsTradeOrder {
- pub order_id: String,
- pub listing_addr: String,
- pub buyer_pubkey: String,
- pub seller_pubkey: String,
- pub items: Vec<RadrootsTradeOrderItem>,
- #[cfg_attr(
- feature = "ts-rs",
- ts(optional, type = "RadrootsCoreDiscountValue[] | null")
- )]
- pub discounts: Option<Vec<RadrootsCoreDiscountValue>>,
-}
-
-#[cfg_attr(feature = "ts-rs", derive(TS))]
-#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsTradeOrderRequested {
pub order_id: String,
pub listing_addr: String,
@@ -1087,7 +1070,7 @@ impl RadrootsTradeMessageType {
match kind {
KIND_TRADE_LISTING_VALIDATE_REQ => Some(Self::ListingValidateRequest),
KIND_TRADE_LISTING_VALIDATE_RES => Some(Self::ListingValidateResult),
- KIND_TRADE_ORDER_REQUEST => Some(Self::OrderRequest),
+ KIND_TRADE_ORDER_REQUEST => None,
KIND_TRADE_ORDER_RESPONSE => Some(Self::OrderResponse),
KIND_TRADE_ORDER_REVISION => Some(Self::OrderRevision),
KIND_TRADE_ORDER_REVISION_RESPONSE => None,
@@ -1745,7 +1728,7 @@ impl std::error::Error for RadrootsTradeEnvelopeError {}
pub enum RadrootsTradeMessagePayload {
ListingValidateRequest(RadrootsTradeListingValidateRequest),
ListingValidateResult(RadrootsTradeListingValidateResult),
- OrderRequest(RadrootsTradeOrder),
+ TradeOrderRequested(RadrootsTradeOrderRequested),
OrderResponse(RadrootsTradeOrderResponse),
OrderRevision(RadrootsTradeOrderRevision),
OrderRevisionAccept(RadrootsTradeOrderRevisionResponse),
@@ -1767,7 +1750,7 @@ impl RadrootsTradeMessagePayload {
match self {
Self::ListingValidateRequest(_) => RadrootsTradeMessageType::ListingValidateRequest,
Self::ListingValidateResult(_) => RadrootsTradeMessageType::ListingValidateResult,
- Self::OrderRequest(_) => RadrootsTradeMessageType::OrderRequest,
+ Self::TradeOrderRequested(_) => RadrootsTradeMessageType::OrderRequest,
Self::OrderResponse(_) => RadrootsTradeMessageType::OrderResponse,
Self::OrderRevision(_) => RadrootsTradeMessageType::OrderRevision,
Self::OrderRevisionAccept(_) => RadrootsTradeMessageType::OrderRevisionAccept,
@@ -1810,20 +1793,6 @@ mod tests {
)))
}
- fn sample_order() -> RadrootsTradeOrder {
- RadrootsTradeOrder {
- order_id: "order-1".into(),
- listing_addr: sample_listing_addr(),
- buyer_pubkey: "buyer".into(),
- seller_pubkey: "seller".into(),
- items: vec![RadrootsTradeOrderItem {
- bin_id: "bin-1".into(),
- bin_count: 2,
- }],
- discounts: Some(vec![sample_discount_value()]),
- }
- }
-
fn sample_active_order_request() -> RadrootsTradeOrderRequested {
RadrootsTradeOrderRequested {
order_id: "order-1".into(),
@@ -2033,7 +2002,7 @@ mod tests {
fn message_type_classifies_request_and_result_kinds() {
assert_eq!(
RadrootsTradeMessageType::from_kind(KIND_TRADE_LISTING_ORDER_REQ),
- Some(RadrootsTradeMessageType::OrderRequest)
+ None
);
assert_eq!(
RadrootsTradeMessageType::from_kind(KIND_TRADE_LISTING_ORDER_RES),
@@ -2882,7 +2851,7 @@ mod tests {
);
assert_eq!(
RadrootsTradeMessageType::from_kind(KIND_TRADE_ORDER_REQUEST),
- Some(RadrootsTradeMessageType::OrderRequest)
+ None
);
assert_eq!(
RadrootsTradeMessageType::from_kind(KIND_TRADE_ORDER_RESPONSE),
@@ -2938,10 +2907,10 @@ mod tests {
#[test]
fn envelope_requires_order_id_for_order_scoped_messages() {
let envelope = RadrootsTradeEnvelope::new(
- RadrootsTradeMessageType::OrderRequest,
+ RadrootsTradeMessageType::OrderResponse,
sample_listing_addr(),
None,
- RadrootsTradeMessagePayload::OrderRequest(sample_order()),
+ RadrootsTradeMessagePayload::OrderResponse(sample_order_response(true)),
);
assert_eq!(
envelope.validate().unwrap_err(),
@@ -2960,20 +2929,20 @@ mod tests {
assert_eq!(service_envelope.validate(), Ok(()));
let public_envelope = RadrootsTradeEnvelope::new(
- RadrootsTradeMessageType::OrderRequest,
+ RadrootsTradeMessageType::OrderResponse,
sample_listing_addr(),
Some("order-1".into()),
- RadrootsTradeMessagePayload::OrderRequest(sample_order()),
+ RadrootsTradeMessagePayload::OrderResponse(sample_order_response(true)),
);
assert_eq!(public_envelope.validate(), Ok(()));
let invalid_version = RadrootsTradeEnvelope {
version: RADROOTS_TRADE_ENVELOPE_VERSION + 1,
domain: RadrootsTradeDomain::TradeListing,
- message_type: RadrootsTradeMessageType::OrderRequest,
+ message_type: RadrootsTradeMessageType::OrderResponse,
order_id: Some("order-1".into()),
listing_addr: sample_listing_addr(),
- payload: RadrootsTradeMessagePayload::OrderRequest(sample_order()),
+ payload: RadrootsTradeMessagePayload::OrderResponse(sample_order_response(true)),
};
assert_eq!(
invalid_version.validate().unwrap_err(),
@@ -3038,7 +3007,7 @@ mod tests {
RadrootsTradeMessageType::ListingValidateResult,
),
(
- RadrootsTradeMessagePayload::OrderRequest(sample_order()),
+ RadrootsTradeMessagePayload::TradeOrderRequested(sample_active_order_request()),
RadrootsTradeMessageType::OrderRequest,
),
(
diff --git a/crates/events_codec/src/trade/decode.rs b/crates/events_codec/src/trade/decode.rs
@@ -833,31 +833,16 @@ mod tests {
RadrootsActiveTradeMessageType, RadrootsActiveTradePayloadError,
RadrootsTradeBuyerReceipt, RadrootsTradeEnvelope, RadrootsTradeFulfillmentUpdated,
RadrootsTradeInventoryCommitment, RadrootsTradeMessagePayload,
- RadrootsTradeMessageType, RadrootsTradeOrder, RadrootsTradeOrderCancelled,
- RadrootsTradeOrderDecision, RadrootsTradeOrderDecisionEvent,
- RadrootsTradeOrderEconomicItem, RadrootsTradeOrderEconomicLine,
- RadrootsTradeOrderEconomics, RadrootsTradeOrderItem, RadrootsTradeOrderRequested,
- RadrootsTradeOrderRevisionDecision, RadrootsTradeOrderRevisionDecisionEvent,
- RadrootsTradeOrderRevisionProposed, RadrootsTradePaymentMethod,
- RadrootsTradePaymentRecorded, RadrootsTradePricingBasis,
+ RadrootsTradeMessageType, RadrootsTradeOrderCancelled, RadrootsTradeOrderDecision,
+ RadrootsTradeOrderDecisionEvent, RadrootsTradeOrderEconomicItem,
+ RadrootsTradeOrderEconomicLine, RadrootsTradeOrderEconomics, RadrootsTradeOrderItem,
+ RadrootsTradeOrderRequested, RadrootsTradeOrderRevisionDecision,
+ RadrootsTradeOrderRevisionDecisionEvent, RadrootsTradeOrderRevisionProposed,
+ RadrootsTradePaymentMethod, RadrootsTradePaymentRecorded, RadrootsTradePricingBasis,
RadrootsTradeSettlementDecision, RadrootsTradeSettlementDecisionEvent,
},
};
- fn base_order() -> RadrootsTradeOrder {
- RadrootsTradeOrder {
- order_id: "order-1".into(),
- listing_addr: "30402:seller:AAAAAAAAAAAAAAAAAAAAAg".into(),
- buyer_pubkey: "buyer".into(),
- seller_pubkey: "seller".into(),
- items: vec![RadrootsTradeOrderItem {
- bin_id: "lb".into(),
- bin_count: 3,
- }],
- discounts: None,
- }
- }
-
fn active_order_request() -> RadrootsTradeOrderRequested {
RadrootsTradeOrderRequested {
order_id: "order-1".into(),
@@ -872,6 +857,15 @@ mod tests {
}
}
+ fn generic_order_response() -> RadrootsTradeMessagePayload {
+ RadrootsTradeMessagePayload::OrderResponse(
+ radroots_events::trade::RadrootsTradeOrderResponse {
+ accepted: true,
+ reason: None,
+ },
+ )
+ }
+
fn decimal(raw: &str) -> RadrootsCoreDecimal {
raw.parse().unwrap()
}
@@ -1050,19 +1044,16 @@ mod tests {
}
#[test]
- fn parse_order_request_roundtrip() {
- let payload = RadrootsTradeMessagePayload::OrderRequest(base_order());
+ fn parse_generic_order_response_roundtrip() {
+ let payload = generic_order_response();
let built = trade_envelope_event_build(
- "seller",
- RadrootsTradeMessageType::OrderRequest,
+ "buyer",
+ RadrootsTradeMessageType::OrderResponse,
"30402:seller:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1".into()),
- Some(&RadrootsNostrEventPtr {
- id: "listing-snapshot".into(),
- relays: None,
- }),
- None,
None,
+ Some("root"),
+ Some("prev"),
&payload,
)
.expect("build trade envelope");
@@ -1079,7 +1070,7 @@ mod tests {
trade_envelope_from_event(&event).expect("parse trade envelope");
assert_eq!(
envelope.message_type,
- RadrootsTradeMessageType::OrderRequest
+ RadrootsTradeMessageType::OrderResponse
);
assert_eq!(envelope.order_id.as_deref(), Some("order-1"));
}
@@ -1816,18 +1807,15 @@ mod tests {
#[test]
fn parse_rejects_listing_addr_mismatch() {
- let payload = RadrootsTradeMessagePayload::OrderRequest(base_order());
+ let payload = generic_order_response();
let built = trade_envelope_event_build(
- "seller",
- RadrootsTradeMessageType::OrderRequest,
+ "buyer",
+ RadrootsTradeMessageType::OrderResponse,
"30402:seller:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1".into()),
- Some(&RadrootsNostrEventPtr {
- id: "listing-snapshot".into(),
- relays: None,
- }),
- None,
None,
+ Some("root"),
+ Some("prev"),
&payload,
)
.expect("build trade envelope");
@@ -1849,18 +1837,23 @@ mod tests {
#[test]
fn parse_rejects_missing_public_snapshot_tag() {
- let payload = RadrootsTradeMessagePayload::OrderRequest(base_order());
+ let payload = RadrootsTradeMessagePayload::OrderRevision(
+ radroots_events::trade::RadrootsTradeOrderRevision {
+ revision_id: "rev-1".into(),
+ changes: Vec::new(),
+ },
+ );
let built = trade_envelope_event_build(
"seller",
- RadrootsTradeMessageType::OrderRequest,
+ RadrootsTradeMessageType::OrderRevision,
"30402:seller:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1".into()),
Some(&RadrootsNostrEventPtr {
id: "listing-snapshot".into(),
relays: None,
}),
- None,
- None,
+ Some("root"),
+ Some("prev"),
&payload,
)
.expect("build trade envelope");
diff --git a/crates/events_codec/src/trade/encode.rs b/crates/events_codec/src/trade/encode.rs
@@ -125,6 +125,9 @@ pub fn trade_envelope_event_build(
prev_event_id: Option<&str>,
payload: &RadrootsTradeMessagePayload,
) -> Result<WireEventParts, EventEncodeError> {
+ if message_type == RadrootsTradeMessageType::OrderRequest {
+ return Err(EventEncodeError::InvalidField("message_type"));
+ }
if payload.message_type() != message_type {
return Err(EventEncodeError::InvalidField("payload"));
}
diff --git a/crates/sdk/src/adapters/radrootsd.rs b/crates/sdk/src/adapters/radrootsd.rs
@@ -187,7 +187,8 @@ impl fmt::Debug for SdkRadrootsdListingPublishRequest {
#[derive(Clone, PartialEq, Eq, Serialize)]
pub(crate) struct SdkRadrootsdOrderRequestPublishRequest {
- pub order: trade::RadrootsTradeOrder,
+ pub order: trade::RadrootsTradeOrderRequested,
+ pub listing_event: RadrootsNostrEventPtr,
pub signer_session_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signer_authority: Option<SdkRadrootsdSignerAuthority>,
@@ -199,6 +200,7 @@ impl fmt::Debug for SdkRadrootsdOrderRequestPublishRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug = f.debug_struct("SdkRadrootsdOrderRequestPublishRequest");
debug.field("order", &self.order);
+ debug.field("listing_event", &self.listing_event);
debug.field("signer_session_id", &"<redacted>");
debug.field("signer_authority", &self.signer_authority);
debug.field("idempotency_key", &self.idempotency_key);
@@ -375,7 +377,7 @@ impl SdkRadrootsdPublicTradePublishRequest {
match &self.payload {
trade::RadrootsTradeMessagePayload::ListingValidateRequest(_) => None,
trade::RadrootsTradeMessagePayload::ListingValidateResult(_) => None,
- trade::RadrootsTradeMessagePayload::OrderRequest(_) => None,
+ trade::RadrootsTradeMessagePayload::TradeOrderRequested(_) => None,
trade::RadrootsTradeMessagePayload::OrderResponse(_) => {
Some(trade::RadrootsTradeMessageType::OrderResponse)
}
@@ -1263,7 +1265,7 @@ pub(crate) async fn publish_public_trade(
}
trade::RadrootsTradeMessagePayload::ListingValidateRequest(_)
| trade::RadrootsTradeMessagePayload::ListingValidateResult(_)
- | trade::RadrootsTradeMessagePayload::OrderRequest(_)
+ | trade::RadrootsTradeMessagePayload::TradeOrderRequested(_)
| trade::RadrootsTradeMessagePayload::DiscountDecline(_) => {
unreachable!("unsupported trade payload should be rejected by the curated client")
}
diff --git a/crates/sdk/src/client.rs b/crates/sdk/src/client.rs
@@ -2363,11 +2363,13 @@ impl<'a> TradeClient<'a> {
#[cfg(feature = "radrootsd-client")]
pub async fn publish_order_request_via_radrootsd(
&self,
- order: &trade::RadrootsTradeOrder,
+ order: &trade::RadrootsTradeOrderRequested,
+ listing_event: &RadrootsNostrEventPtr,
session: &SdkRadrootsdSignerSessionHandle,
) -> Result<SdkPublishReceipt, SdkPublishError> {
self.publish_order_request_via_radrootsd_with_options(
order,
+ listing_event,
&SdkRadrootsdOrderRequestPublishOptions::from_signer_session(session),
)
.await
@@ -2376,11 +2378,13 @@ impl<'a> TradeClient<'a> {
#[cfg(feature = "radrootsd-client")]
pub async fn publish_order_request_via_radrootsd_with_options(
&self,
- order: &trade::RadrootsTradeOrder,
+ order: &trade::RadrootsTradeOrderRequested,
+ listing_event: &RadrootsNostrEventPtr,
options: &SdkRadrootsdOrderRequestPublishOptions,
) -> Result<SdkPublishReceipt, SdkPublishError> {
let request = radrootsd::SdkRadrootsdOrderRequestPublishRequest {
order: order.clone(),
+ listing_event: listing_event.clone(),
signer_session_id: options.session().session_id().to_owned(),
signer_authority: options.signer_authority().cloned(),
idempotency_key: options.idempotency_key().map(str::to_owned),
diff --git a/crates/sdk/tests/radrootsd.rs b/crates/sdk/tests/radrootsd.rs
@@ -18,8 +18,9 @@ use radroots_sdk::listing::{
};
use radroots_sdk::trade::{
RadrootsTradeDiscountDecision, RadrootsTradeMessagePayload, RadrootsTradeMessageType,
- RadrootsTradeOrder, RadrootsTradeOrderItem, RadrootsTradeOrderResponse,
- RadrootsTradeOrderRevision, RadrootsTradeOrderRevisionResponse,
+ RadrootsTradeOrderEconomicItem, RadrootsTradeOrderEconomicLine, RadrootsTradeOrderEconomics,
+ RadrootsTradeOrderItem, RadrootsTradeOrderRequested, RadrootsTradeOrderResponse,
+ RadrootsTradeOrderRevision, RadrootsTradeOrderRevisionResponse, RadrootsTradePricingBasis,
};
use radroots_sdk::{
RadrootsNostrEvent, RadrootsNostrEventPtr, RadrootsProfile, RadrootsProfileType,
@@ -414,8 +415,44 @@ fn sample_farm() -> RadrootsFarm {
}
}
-fn sample_trade_order() -> RadrootsTradeOrder {
- RadrootsTradeOrder {
+fn sample_trade_order_economics() -> RadrootsTradeOrderEconomics {
+ RadrootsTradeOrderEconomics {
+ quote_id: "quote-1".to_owned(),
+ quote_version: 1,
+ pricing_basis: RadrootsTradePricingBasis::ListingEvent,
+ currency: RadrootsCoreCurrency::USD,
+ items: vec![RadrootsTradeOrderEconomicItem {
+ bin_id: "bin-1".to_owned(),
+ bin_count: 2,
+ quantity_amount: RadrootsCoreDecimal::from(1u32),
+ quantity_unit: RadrootsCoreUnit::MassG,
+ unit_price_amount: RadrootsCoreDecimal::from(20u32),
+ unit_price_currency: RadrootsCoreCurrency::USD,
+ line_subtotal: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(40u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ }],
+ discounts: Vec::<RadrootsTradeOrderEconomicLine>::new(),
+ adjustments: Vec::<RadrootsTradeOrderEconomicLine>::new(),
+ subtotal: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(40u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ discount_total: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(0u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ adjustment_total: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(0u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ total: RadrootsCoreMoney::new(RadrootsCoreDecimal::from(40u32), RadrootsCoreCurrency::USD),
+ }
+}
+
+fn sample_trade_order() -> RadrootsTradeOrderRequested {
+ RadrootsTradeOrderRequested {
order_id: "order-1".to_owned(),
listing_addr: format!("{KIND_LISTING}:seller:AAAAAAAAAAAAAAAAAAAAAg"),
buyer_pubkey: "buyer".to_owned(),
@@ -424,7 +461,7 @@ fn sample_trade_order() -> RadrootsTradeOrder {
bin_id: "bin-1".to_owned(),
bin_count: 2,
}],
- discounts: Some(Vec::new()),
+ economics: sample_trade_order_economics(),
}
}
@@ -1597,7 +1634,11 @@ async fn radrootsd_trade_order_request_publish_accepts_session_handle() -> TestR
let receipt = client
.trade()
- .publish_order_request_via_radrootsd_with_options(&sample_trade_order(), &options)
+ .publish_order_request_via_radrootsd_with_options(
+ &sample_trade_order(),
+ &listing_event_ptr_with_relays(Some("wss://radroots.org")),
+ &options,
+ )
.await?;
let request_json = request_rx.await?;
@@ -1609,6 +1650,14 @@ async fn radrootsd_trade_order_request_publish_accepts_session_handle() -> TestR
assert_eq!(request_json["params"]["idempotency_key"], "idem-order-1");
assert_eq!(request_json["params"]["order"]["order_id"], "order-1");
assert_eq!(
+ request_json["params"]["listing_event"]["id"],
+ "listing-event-1"
+ );
+ assert_eq!(
+ request_json["params"]["listing_event"]["relays"],
+ "wss://radroots.org"
+ );
+ assert_eq!(
request_json["params"]["signer_authority"]["provider_runtime_id"],
"runtime-1"
);
@@ -1695,7 +1744,7 @@ fn public_trade_request_validation_rejects_order_request_payload() {
sample_trade_order().listing_addr.clone(),
"order-1",
"buyer",
- RadrootsTradeMessagePayload::OrderRequest(sample_trade_order()),
+ RadrootsTradeMessagePayload::TradeOrderRequested(sample_trade_order()),
);
let error = request
@@ -1905,7 +1954,11 @@ async fn radrootsd_sdk_workflow_chains_session_listing_trade_and_bridge_job() ->
let trade_receipt = client
.trade()
- .publish_order_request_via_radrootsd(&sample_trade_order(), &handle)
+ .publish_order_request_via_radrootsd(
+ &sample_trade_order(),
+ &listing_event_ptr_with_relays(Some("wss://radroots.org")),
+ &handle,
+ )
.await?;
let trade_request = request_rx.recv().await.expect("trade publish request");
assert_eq!(trade_request["method"], "bridge.order.request");
@@ -1914,6 +1967,10 @@ async fn radrootsd_sdk_workflow_chains_session_listing_trade_and_bridge_job() ->
"session-workflow-1"
);
assert_eq!(trade_request["params"]["order"]["order_id"], "order-1");
+ assert_eq!(
+ trade_request["params"]["listing_event"]["id"],
+ "listing-event-1"
+ );
let trade_job = match &trade_receipt.transport_receipt {
SdkTransportReceipt::Radrootsd(receipt) => receipt.job(),
diff --git a/crates/trade/src/listing/contract.rs b/crates/trade/src/listing/contract.rs
@@ -21,6 +21,9 @@ pub(crate) use radroots_events::{
RadrootsTradeDiscountDecision as TradeDiscountDecision,
RadrootsTradeDiscountOffer as TradeDiscountOffer,
RadrootsTradeDiscountRequest as TradeDiscountRequest,
+ RadrootsTradeEconomicActor as TradeEconomicActor,
+ RadrootsTradeEconomicEffect as TradeEconomicEffect,
+ RadrootsTradeEconomicLineKind as TradeEconomicLineKind,
RadrootsTradeEnvelope as TradeListingEnvelope,
RadrootsTradeEnvelopeError as TradeListingEnvelopeError,
RadrootsTradeFulfillmentStatus as TradeFulfillmentStatus,
@@ -31,12 +34,17 @@ pub(crate) use radroots_events::{
RadrootsTradeListingValidateResult as TradeListingValidateResult,
RadrootsTradeListingValidationError as TradeListingValidationError,
RadrootsTradeMessagePayload as TradeListingMessagePayload,
- RadrootsTradeMessageType as TradeListingMessageType, RadrootsTradeOrder as TradeOrder,
- RadrootsTradeOrderChange as TradeOrderChange, RadrootsTradeOrderItem as TradeOrderItem,
+ RadrootsTradeMessageType as TradeListingMessageType,
+ RadrootsTradeOrderChange as TradeOrderChange,
+ RadrootsTradeOrderEconomicItem as TradeOrderEconomicItem,
+ RadrootsTradeOrderEconomicLine as TradeOrderEconomicLine,
+ RadrootsTradeOrderEconomics as TradeOrderEconomics,
+ RadrootsTradeOrderItem as TradeOrderItem, RadrootsTradeOrderRequested as TradeOrder,
RadrootsTradeOrderResponse as TradeOrderResponse,
RadrootsTradeOrderRevision as TradeOrderRevision,
RadrootsTradeOrderRevisionResponse as TradeOrderRevisionResponse,
- RadrootsTradeOrderStatus as TradeOrderStatus, RadrootsTradeQuestion as TradeQuestion,
+ RadrootsTradeOrderStatus as TradeOrderStatus,
+ RadrootsTradePricingBasis as TradePricingBasis, RadrootsTradeQuestion as TradeQuestion,
RadrootsTradeReceipt as TradeReceipt,
},
};
diff --git a/crates/trade/src/listing/dvm.rs b/crates/trade/src/listing/dvm.rs
@@ -636,7 +636,7 @@ pub struct TradeListingCancel {
pub enum TradeListingMessagePayload {
ListingValidateRequest(TradeListingValidateRequest),
ListingValidateResult(TradeListingValidateResult),
- OrderRequest(TradeOrder),
+ TradeOrderRequested(TradeOrder),
OrderResponse(TradeOrderResponse),
OrderRevision(TradeOrderRevision),
OrderRevisionAccept(TradeOrderRevisionResponse),
@@ -661,7 +661,9 @@ impl TradeListingMessagePayload {
TradeListingMessagePayload::ListingValidateResult(_) => {
TradeListingMessageType::ListingValidateResult
}
- TradeListingMessagePayload::OrderRequest(_) => TradeListingMessageType::OrderRequest,
+ TradeListingMessagePayload::TradeOrderRequested(_) => {
+ TradeListingMessageType::OrderRequest
+ }
TradeListingMessagePayload::OrderResponse(_) => TradeListingMessageType::OrderResponse,
TradeListingMessagePayload::OrderRevision(_) => TradeListingMessageType::OrderRevision,
TradeListingMessagePayload::OrderRevisionAccept(_) => {
@@ -706,7 +708,12 @@ mod tests {
use radroots_events_codec::error::EventEncodeError;
#[cfg(feature = "serde_json")]
- use crate::listing::order::{TradeOrder, TradeOrderItem};
+ use crate::listing::order::{
+ TradeOrder, TradeOrderEconomicItem, TradeOrderEconomicLine, TradeOrderEconomics,
+ TradeOrderItem, TradePricingBasis,
+ };
+ #[cfg(feature = "serde_json")]
+ use radroots_core::{RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreUnit};
#[test]
fn envelope_requires_listing_addr() {
@@ -935,17 +942,65 @@ mod tests {
}
#[cfg(feature = "serde_json")]
+ fn order_economics(items: &[TradeOrderItem]) -> TradeOrderEconomics {
+ let economic_items = items
+ .iter()
+ .map(|item| {
+ let line_subtotal =
+ RadrootsCoreDecimal::from(item.bin_count) * RadrootsCoreDecimal::from(5u32);
+ TradeOrderEconomicItem {
+ bin_id: item.bin_id.clone(),
+ bin_count: item.bin_count,
+ quantity_amount: RadrootsCoreDecimal::from(1u32),
+ quantity_unit: RadrootsCoreUnit::Each,
+ unit_price_amount: RadrootsCoreDecimal::from(5u32),
+ unit_price_currency: RadrootsCoreCurrency::USD,
+ line_subtotal: RadrootsCoreMoney::new(line_subtotal, RadrootsCoreCurrency::USD),
+ }
+ })
+ .collect::<Vec<_>>();
+ let subtotal = items
+ .iter()
+ .fold(RadrootsCoreDecimal::from(0u32), |total, item| {
+ total
+ + (RadrootsCoreDecimal::from(item.bin_count)
+ * RadrootsCoreDecimal::from(5u32))
+ });
+
+ TradeOrderEconomics {
+ quote_id: "quote-1".into(),
+ quote_version: 1,
+ pricing_basis: TradePricingBasis::ListingEvent,
+ currency: RadrootsCoreCurrency::USD,
+ items: economic_items,
+ discounts: Vec::<TradeOrderEconomicLine>::new(),
+ adjustments: Vec::<TradeOrderEconomicLine>::new(),
+ subtotal: RadrootsCoreMoney::new(subtotal, RadrootsCoreCurrency::USD),
+ discount_total: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(0u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ adjustment_total: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(0u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ total: RadrootsCoreMoney::new(subtotal, RadrootsCoreCurrency::USD),
+ }
+ }
+
+ #[cfg(feature = "serde_json")]
fn base_order() -> TradeOrder {
+ let items = vec![TradeOrderItem {
+ bin_id: "bin-1".into(),
+ bin_count: 2,
+ }];
TradeOrder {
order_id: "order-1".into(),
listing_addr: format!("{KIND_LISTING}:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg"),
buyer_pubkey: "buyer-pubkey".into(),
seller_pubkey: "seller-pubkey".into(),
- items: vec![TradeOrderItem {
- bin_id: "bin-1".into(),
- bin_count: 2,
- }],
- discounts: None,
+ economics: order_economics(&items),
+ items,
}
}
@@ -996,7 +1051,7 @@ mod tests {
#[test]
fn envelope_event_build_includes_order_and_snapshot_tags() {
let listing_addr = format!("{KIND_LISTING}:pubkey:AAAAAAAAAAAAAAAAAAAAAg");
- let payload = TradeListingMessagePayload::OrderRequest(base_order());
+ let payload = TradeListingMessagePayload::TradeOrderRequested(base_order());
let built = super::trade_listing_envelope_event_build(
"pubkey",
TradeListingMessageType::OrderRequest,
@@ -1054,7 +1109,7 @@ mod tests {
#[test]
fn envelope_event_build_requires_snapshot_for_order_request() {
let listing_addr = format!("{KIND_LISTING}:pubkey:AAAAAAAAAAAAAAAAAAAAAg");
- let payload = TradeListingMessagePayload::OrderRequest(base_order());
+ let payload = TradeListingMessagePayload::TradeOrderRequested(base_order());
let err = super::trade_listing_envelope_event_build(
"pubkey",
TradeListingMessageType::OrderRequest,
@@ -1097,7 +1152,7 @@ mod tests {
#[cfg(feature = "serde_json")]
#[test]
fn envelope_from_event_parses_canonical_order_request() {
- let payload = TradeListingMessagePayload::OrderRequest(base_order());
+ let payload = TradeListingMessagePayload::TradeOrderRequested(base_order());
let event = base_event(
"buyer-pubkey",
"seller-pubkey",
@@ -1117,7 +1172,7 @@ mod tests {
#[cfg(feature = "serde_json")]
#[test]
fn envelope_from_event_rejects_kind_mismatch() {
- let payload = TradeListingMessagePayload::OrderRequest(base_order());
+ let payload = TradeListingMessagePayload::TradeOrderRequested(base_order());
let mut event = base_event(
"buyer-pubkey",
"seller-pubkey",
@@ -1142,7 +1197,7 @@ mod tests {
#[cfg(feature = "serde_json")]
#[test]
fn envelope_from_event_rejects_listing_addr_tag_mismatch() {
- let payload = TradeListingMessagePayload::OrderRequest(base_order());
+ let payload = TradeListingMessagePayload::TradeOrderRequested(base_order());
let mut event = base_event(
"buyer-pubkey",
"seller-pubkey",
@@ -1161,7 +1216,7 @@ mod tests {
#[cfg(feature = "serde_json")]
#[test]
fn envelope_from_event_rejects_order_id_tag_mismatch() {
- let payload = TradeListingMessagePayload::OrderRequest(base_order());
+ let payload = TradeListingMessagePayload::TradeOrderRequested(base_order());
let mut event = base_event(
"buyer-pubkey",
"seller-pubkey",
diff --git a/crates/trade/src/listing/overlay.rs b/crates/trade/src/listing/overlay.rs
@@ -513,8 +513,9 @@ mod tests {
};
use crate::listing::{
order::{
- TradeFulfillmentStatus, TradeFulfillmentUpdate, TradeOrder, TradeOrderItem,
- TradeOrderStatus, TradeReceipt,
+ TradeEconomicActor, TradeEconomicEffect, TradeEconomicLineKind, TradeFulfillmentStatus,
+ TradeFulfillmentUpdate, TradeOrder, TradeOrderEconomicItem, TradeOrderEconomicLine,
+ TradeOrderEconomics, TradeOrderItem, TradeOrderStatus, TradePricingBasis, TradeReceipt,
},
projection::{
RadrootsTradeListingSort, RadrootsTradeListingSortField, RadrootsTradeOrderQuery,
@@ -523,8 +524,8 @@ mod tests {
},
};
use radroots_core::{
- RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCorePercent,
- RadrootsCoreQuantity, RadrootsCoreQuantityPrice, RadrootsCoreUnit,
+ RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity,
+ RadrootsCoreQuantityPrice, RadrootsCoreUnit,
};
use radroots_events::RadrootsNostrEventPtr;
use radroots_events::farm::RadrootsFarmRef;
@@ -589,7 +590,7 @@ mod tests {
None,
None,
),
- (TradeListingMessagePayload::OrderRequest(order), Some(order_id)) => {
+ (TradeListingMessagePayload::TradeOrderRequested(order), Some(order_id)) => {
let event_id = format!("{order_id}:request");
TEST_WORKFLOW_CHAINS.with(|chains| {
chains.borrow_mut().insert(
@@ -775,33 +776,90 @@ mod tests {
}
}
+ fn order_economics(items: &[TradeOrderItem], include_discount: bool) -> TradeOrderEconomics {
+ let mut subtotal = RadrootsCoreDecimal::from(0u32);
+ let economic_items = items
+ .iter()
+ .map(|item| {
+ let line_subtotal =
+ RadrootsCoreDecimal::from(item.bin_count) * RadrootsCoreDecimal::from(5u32);
+ subtotal = subtotal + line_subtotal;
+ TradeOrderEconomicItem {
+ bin_id: item.bin_id.clone(),
+ bin_count: item.bin_count,
+ quantity_amount: RadrootsCoreDecimal::from(1u32),
+ quantity_unit: RadrootsCoreUnit::Each,
+ unit_price_amount: RadrootsCoreDecimal::from(5u32),
+ unit_price_currency: RadrootsCoreCurrency::USD,
+ line_subtotal: RadrootsCoreMoney::new(line_subtotal, RadrootsCoreCurrency::USD),
+ }
+ })
+ .collect::<Vec<_>>();
+ let discounts = include_discount
+ .then(|| {
+ vec![TradeOrderEconomicLine {
+ id: "discount-1".into(),
+ kind: TradeEconomicLineKind::ListingDiscount,
+ actor: TradeEconomicActor::Seller,
+ effect: TradeEconomicEffect::Decrease,
+ amount: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(1u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ reason: "listing discount".into(),
+ }]
+ })
+ .unwrap_or_default();
+ let discount_total = if include_discount {
+ RadrootsCoreDecimal::from(1u32)
+ } else {
+ RadrootsCoreDecimal::from(0u32)
+ };
+ TradeOrderEconomics {
+ quote_id: "quote-1".into(),
+ quote_version: 1,
+ pricing_basis: TradePricingBasis::ListingEvent,
+ currency: RadrootsCoreCurrency::USD,
+ items: economic_items,
+ discounts,
+ adjustments: Vec::new(),
+ subtotal: RadrootsCoreMoney::new(subtotal, RadrootsCoreCurrency::USD),
+ discount_total: RadrootsCoreMoney::new(discount_total, RadrootsCoreCurrency::USD),
+ adjustment_total: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(0u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ total: RadrootsCoreMoney::new(subtotal - discount_total, RadrootsCoreCurrency::USD),
+ }
+ }
+
fn base_order() -> TradeOrder {
+ let items = vec![TradeOrderItem {
+ bin_id: "bin-1".into(),
+ bin_count: 2,
+ }];
TradeOrder {
order_id: "order-1".into(),
listing_addr: "30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg".into(),
buyer_pubkey: "buyer-pubkey".into(),
seller_pubkey: "seller-pubkey".into(),
- items: vec![TradeOrderItem {
- bin_id: "bin-1".into(),
- bin_count: 2,
- }],
- discounts: Some(vec![radroots_core::RadrootsCoreDiscountValue::Percent(
- RadrootsCorePercent::new(RadrootsCoreDecimal::from(10u32)),
- )]),
+ economics: order_economics(&items, true),
+ items,
}
}
fn alternate_order() -> TradeOrder {
+ let items = vec![TradeOrderItem {
+ bin_id: "bin-1".into(),
+ bin_count: 3,
+ }];
TradeOrder {
order_id: "order-2".into(),
listing_addr: "30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAw".into(),
buyer_pubkey: "buyer-pubkey-2".into(),
seller_pubkey: "seller-pubkey".into(),
- items: vec![TradeOrderItem {
- bin_id: "bin-1".into(),
- bin_count: 3,
- }],
- discounts: None,
+ economics: order_economics(&items, false),
+ items,
}
}
@@ -1043,7 +1101,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("first order");
index
@@ -1051,7 +1109,7 @@ mod tests {
"buyer-pubkey-2",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAw",
Some("order-2"),
- TradeListingMessagePayload::OrderRequest(alternate_order()),
+ TradeListingMessagePayload::TradeOrderRequested(alternate_order()),
))
.expect("second order");
index
diff --git a/crates/trade/src/listing/projection.rs b/crates/trade/src/listing/projection.rs
@@ -11,7 +11,7 @@ use radroots_core::{RadrootsCoreDecimal, RadrootsCoreDiscount, RadrootsCoreDisco
use radroots_events::{
RadrootsNostrEvent, RadrootsNostrEventPtr,
farm::RadrootsFarmRef,
- kinds::{KIND_LISTING, is_listing_kind},
+ kinds::{KIND_LISTING, KIND_TRADE_ORDER_REQUEST, is_listing_kind},
listing::{
RadrootsListing, RadrootsListingAvailability, RadrootsListingBin,
RadrootsListingDeliveryMethod, RadrootsListingImage, RadrootsListingLocation,
@@ -31,12 +31,16 @@ use crate::listing::{
},
model::RadrootsTradeListingTotal,
order::{
- TradeFulfillmentStatus, TradeOrder, TradeOrderChange, TradeOrderItem, TradeOrderStatus,
+ TradeFulfillmentStatus, TradeOrder, TradeOrderChange, TradeOrderEconomicLine,
+ TradeOrderItem, TradeOrderStatus,
},
price_ext::BinPricingExt,
};
#[cfg(feature = "serde_json")]
-use radroots_events_codec::trade::trade_event_context_from_tags;
+use radroots_events_codec::trade::{
+ RadrootsActiveTradeEnvelopeParseError, active_trade_order_request_from_event,
+ trade_event_context_from_tags,
+};
#[cfg_attr(feature = "ts-rs", derive(TS))]
#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))]
@@ -116,9 +120,9 @@ pub struct RadrootsTradeOrderWorkflowProjection {
pub items: Vec<TradeOrderItem>,
#[cfg_attr(
feature = "ts-rs",
- ts(optional, type = "RadrootsCoreDiscountValue[] | null")
+ ts(optional, type = "RadrootsTradeOrderEconomicLine[] | null")
)]
- pub requested_discounts: Option<Vec<RadrootsCoreDiscountValue>>,
+ pub requested_discounts: Option<Vec<TradeOrderEconomicLine>>,
pub status: TradeOrderStatus,
#[cfg_attr(feature = "ts-rs", ts(optional, type = "RadrootsNostrEventPtr | null"))]
pub listing_snapshot: Option<RadrootsNostrEventPtr>,
@@ -678,7 +682,8 @@ impl RadrootsTradeOrderWorkflowProjection {
buyer_pubkey: order.buyer_pubkey.clone(),
seller_pubkey: order.seller_pubkey.clone(),
items: order.items.clone(),
- requested_discounts: order.discounts.clone(),
+ requested_discounts: (!order.economics.discounts.is_empty())
+ .then(|| order.economics.discounts.clone()),
status: TradeOrderStatus::Requested,
listing_snapshot: Some(listing_snapshot),
root_event_id: message.event_id.clone(),
@@ -710,6 +715,24 @@ impl RadrootsTradeOrderWorkflowProjection {
impl RadrootsTradeOrderWorkflowMessage {
#[cfg(feature = "serde_json")]
pub fn from_event(event: &RadrootsNostrEvent) -> Result<Self, TradeListingEnvelopeParseError> {
+ if event.kind == KIND_TRADE_ORDER_REQUEST {
+ let envelope = active_trade_order_request_from_event(event)
+ .map_err(map_active_order_request_parse_error)?;
+ let context =
+ trade_event_context_from_tags(TradeListingMessageType::OrderRequest, &event.tags)?;
+ return Ok(Self {
+ event_id: event.id.clone(),
+ actor_pubkey: event.author.clone(),
+ counterparty_pubkey: context.counterparty_pubkey,
+ listing_addr: envelope.listing_addr,
+ order_id: Some(envelope.order_id),
+ listing_event: context.listing_event,
+ root_event_id: context.root_event_id,
+ prev_event_id: context.prev_event_id,
+ payload: TradeListingMessagePayload::TradeOrderRequested(envelope.payload),
+ });
+ }
+
let envelope = trade_listing_envelope_from_event::<TradeListingMessagePayload>(event)?;
trade_event_context_from_tags(envelope.message_type, &event.tags).map(|context| Self {
event_id: event.id.clone(),
@@ -732,7 +755,9 @@ impl RadrootsTradeOrderWorkflowMessage {
TradeListingMessagePayload::ListingValidateResult(_) => {
TradeListingMessageType::ListingValidateResult
}
- TradeListingMessagePayload::OrderRequest(_) => TradeListingMessageType::OrderRequest,
+ TradeListingMessagePayload::TradeOrderRequested(_) => {
+ TradeListingMessageType::OrderRequest
+ }
TradeListingMessagePayload::OrderResponse(_) => TradeListingMessageType::OrderResponse,
TradeListingMessagePayload::OrderRevision(_) => TradeListingMessageType::OrderRevision,
TradeListingMessagePayload::OrderRevisionAccept(_) => {
@@ -762,6 +787,41 @@ impl RadrootsTradeOrderWorkflowMessage {
}
}
+#[cfg(feature = "serde_json")]
+fn map_active_order_request_parse_error(
+ error: RadrootsActiveTradeEnvelopeParseError,
+) -> TradeListingEnvelopeParseError {
+ match error {
+ RadrootsActiveTradeEnvelopeParseError::InvalidKind(kind) => {
+ TradeListingEnvelopeParseError::InvalidKind(kind)
+ }
+ RadrootsActiveTradeEnvelopeParseError::MissingTag(tag) => {
+ TradeListingEnvelopeParseError::MissingTag(tag)
+ }
+ RadrootsActiveTradeEnvelopeParseError::InvalidTag(tag) => {
+ TradeListingEnvelopeParseError::InvalidTag(tag)
+ }
+ RadrootsActiveTradeEnvelopeParseError::ListingAddrTagMismatch => {
+ TradeListingEnvelopeParseError::ListingAddrTagMismatch
+ }
+ RadrootsActiveTradeEnvelopeParseError::OrderIdTagMismatch => {
+ TradeListingEnvelopeParseError::OrderIdTagMismatch
+ }
+ RadrootsActiveTradeEnvelopeParseError::InvalidListingAddr(error) => {
+ TradeListingEnvelopeParseError::InvalidListingAddr(error)
+ }
+ RadrootsActiveTradeEnvelopeParseError::InvalidJson
+ | RadrootsActiveTradeEnvelopeParseError::InvalidEnvelope(_)
+ | RadrootsActiveTradeEnvelopeParseError::InvalidPayload(_)
+ | RadrootsActiveTradeEnvelopeParseError::MessageTypeKindMismatch { .. }
+ | RadrootsActiveTradeEnvelopeParseError::PayloadBindingMismatch(_)
+ | RadrootsActiveTradeEnvelopeParseError::AuthorMismatch
+ | RadrootsActiveTradeEnvelopeParseError::CounterpartyTagMismatch => {
+ TradeListingEnvelopeParseError::InvalidJson
+ }
+ }
+}
+
impl RadrootsTradeReadIndex {
pub fn new() -> Self {
Self::default()
@@ -956,7 +1016,7 @@ impl RadrootsTradeReadIndex {
| TradeListingMessagePayload::ListingValidateResult(_) => Err(
RadrootsTradeProjectionError::NonOrderWorkflowMessage(message.message_type()),
),
- TradeListingMessagePayload::OrderRequest(order) => {
+ TradeListingMessagePayload::TradeOrderRequested(order) => {
self.apply_order_request(message, order)
}
TradeListingMessagePayload::OrderResponse(response) => {
@@ -1762,8 +1822,10 @@ mod tests {
},
order::{
TradeAnswer, TradeDiscountDecision, TradeDiscountOffer, TradeDiscountRequest,
- TradeFulfillmentStatus, TradeFulfillmentUpdate, TradeOrder, TradeOrderChange,
- TradeOrderItem, TradeOrderRevision, TradeOrderStatus, TradeQuestion, TradeReceipt,
+ TradeEconomicActor, TradeEconomicEffect, TradeEconomicLineKind, TradeFulfillmentStatus,
+ TradeFulfillmentUpdate, TradeOrder, TradeOrderChange, TradeOrderEconomicItem,
+ TradeOrderEconomicLine, TradeOrderEconomics, TradeOrderItem, TradeOrderRevision,
+ TradeOrderStatus, TradePricingBasis, TradeQuestion, TradeReceipt,
},
};
use radroots_core::{
@@ -1777,6 +1839,7 @@ mod tests {
RadrootsListingStatus,
};
use radroots_events::{RadrootsNostrEvent, RadrootsNostrEventPtr, kinds::KIND_LISTING};
+ use radroots_events_codec::trade::active_trade_order_request_event_build;
#[derive(Clone, Debug)]
struct TestWorkflowChain {
@@ -1833,7 +1896,7 @@ mod tests {
None,
None,
),
- (TradeListingMessagePayload::OrderRequest(order), Some(order_id)) => {
+ (TradeListingMessagePayload::TradeOrderRequested(order), Some(order_id)) => {
let event_id = format!("{order_id}:request");
TEST_WORKFLOW_CHAINS.with(|chains| {
chains.borrow_mut().insert(
@@ -1978,19 +2041,75 @@ mod tests {
}
}
+ fn order_economics(items: &[TradeOrderItem], include_discount: bool) -> TradeOrderEconomics {
+ let mut subtotal = RadrootsCoreDecimal::from(0u32);
+ let economic_items = items
+ .iter()
+ .map(|item| {
+ let line_subtotal =
+ RadrootsCoreDecimal::from(item.bin_count) * RadrootsCoreDecimal::from(5u32);
+ subtotal = subtotal + line_subtotal;
+ TradeOrderEconomicItem {
+ bin_id: item.bin_id.clone(),
+ bin_count: item.bin_count,
+ quantity_amount: RadrootsCoreDecimal::from(1u32),
+ quantity_unit: RadrootsCoreUnit::Each,
+ unit_price_amount: RadrootsCoreDecimal::from(5u32),
+ unit_price_currency: RadrootsCoreCurrency::USD,
+ line_subtotal: RadrootsCoreMoney::new(line_subtotal, RadrootsCoreCurrency::USD),
+ }
+ })
+ .collect::<Vec<_>>();
+ let discounts = include_discount
+ .then(|| {
+ vec![TradeOrderEconomicLine {
+ id: "discount-1".into(),
+ kind: TradeEconomicLineKind::ListingDiscount,
+ actor: TradeEconomicActor::Seller,
+ effect: TradeEconomicEffect::Decrease,
+ amount: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(1u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ reason: "listing discount".into(),
+ }]
+ })
+ .unwrap_or_default();
+ let discount_total = if include_discount {
+ RadrootsCoreDecimal::from(1u32)
+ } else {
+ RadrootsCoreDecimal::from(0u32)
+ };
+ TradeOrderEconomics {
+ quote_id: "quote-1".into(),
+ quote_version: 1,
+ pricing_basis: TradePricingBasis::ListingEvent,
+ currency: RadrootsCoreCurrency::USD,
+ items: economic_items,
+ discounts,
+ adjustments: Vec::new(),
+ subtotal: RadrootsCoreMoney::new(subtotal, RadrootsCoreCurrency::USD),
+ discount_total: RadrootsCoreMoney::new(discount_total, RadrootsCoreCurrency::USD),
+ adjustment_total: RadrootsCoreMoney::new(
+ RadrootsCoreDecimal::from(0u32),
+ RadrootsCoreCurrency::USD,
+ ),
+ total: RadrootsCoreMoney::new(subtotal - discount_total, RadrootsCoreCurrency::USD),
+ }
+ }
+
fn base_order() -> TradeOrder {
+ let items = vec![TradeOrderItem {
+ bin_id: "bin-1".into(),
+ bin_count: 2,
+ }];
TradeOrder {
order_id: "order-1".into(),
listing_addr: "30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg".into(),
buyer_pubkey: "buyer-pubkey".into(),
seller_pubkey: "seller-pubkey".into(),
- items: vec![TradeOrderItem {
- bin_id: "bin-1".into(),
- bin_count: 2,
- }],
- discounts: Some(vec![radroots_core::RadrootsCoreDiscountValue::Percent(
- RadrootsCorePercent::new(RadrootsCoreDecimal::from(10u32)),
- )]),
+ economics: order_economics(&items, true),
+ items,
}
}
@@ -2061,22 +2180,23 @@ mod tests {
}
fn alternate_order() -> TradeOrder {
+ let items = vec![
+ TradeOrderItem {
+ bin_id: "bin-1".into(),
+ bin_count: 3,
+ },
+ TradeOrderItem {
+ bin_id: "bin-2".into(),
+ bin_count: 1,
+ },
+ ];
TradeOrder {
order_id: "order-2".into(),
listing_addr: "30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAw".into(),
buyer_pubkey: "buyer-pubkey-2".into(),
seller_pubkey: "seller-pubkey".into(),
- items: vec![
- TradeOrderItem {
- bin_id: "bin-1".into(),
- bin_count: 3,
- },
- TradeOrderItem {
- bin_id: "bin-2".into(),
- bin_count: 1,
- },
- ],
- discounts: None,
+ economics: order_economics(&items, false),
+ items,
}
}
@@ -2123,17 +2243,30 @@ mod tests {
) -> RadrootsNostrEvent {
let (_, _, listing_event, root_event_id, prev_event_id) =
workflow_refs(actor_pubkey, listing_addr, order_id, payload);
- let built = trade_listing_envelope_event_build(
- recipient_pubkey,
- message_type,
- listing_addr.to_string(),
- order_id.map(str::to_string),
- listing_event.as_ref(),
- root_event_id.as_deref(),
- prev_event_id.as_deref(),
- payload,
- )
- .expect("trade workflow event");
+ let built = if message_type == crate::listing::dvm::TradeListingMessageType::OrderRequest {
+ let TradeListingMessagePayload::TradeOrderRequested(order) = payload else {
+ panic!("order-request workflow event requires active order payload")
+ };
+ active_trade_order_request_event_build(
+ listing_event
+ .as_ref()
+ .expect("order-request workflow event requires listing snapshot"),
+ order,
+ )
+ .expect("trade workflow event")
+ } else {
+ trade_listing_envelope_event_build(
+ recipient_pubkey,
+ message_type,
+ listing_addr.to_string(),
+ order_id.map(str::to_string),
+ listing_event.as_ref(),
+ root_event_id.as_deref(),
+ prev_event_id.as_deref(),
+ payload,
+ )
+ .expect("trade workflow event")
+ };
RadrootsNostrEvent {
id: "workflow-event-id".into(),
author: actor_pubkey.into(),
@@ -2200,7 +2333,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("order request");
assert_eq!(index.listings().len(), 1);
@@ -2481,7 +2614,7 @@ mod tests {
crate::listing::dvm::TradeListingMessageType::OrderRequest,
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- &TradeListingMessagePayload::OrderRequest(base_order()),
+ &TradeListingMessagePayload::TradeOrderRequested(base_order()),
);
let order = index
@@ -2506,7 +2639,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("order request");
let listing_after_request = index
@@ -2576,7 +2709,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("order request");
index
@@ -2625,7 +2758,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("order request");
index
@@ -2679,7 +2812,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("order request");
@@ -2745,7 +2878,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("order request");
@@ -2815,7 +2948,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("order request");
@@ -2880,7 +3013,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("order request");
assert_eq!(
@@ -2889,6 +3022,10 @@ mod tests {
"canonical helper should still create a requested order"
);
+ let missing_snapshot_items = vec![TradeOrderItem {
+ bin_id: "bin-1".into(),
+ bin_count: 1,
+ }];
let err = index
.apply_workflow_message(&RadrootsTradeOrderWorkflowMessage {
event_id: "missing-snapshot".into(),
@@ -2899,16 +3036,13 @@ mod tests {
listing_event: None,
root_event_id: None,
prev_event_id: None,
- payload: TradeListingMessagePayload::OrderRequest(TradeOrder {
+ payload: TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-2".into(),
listing_addr: "30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg".into(),
buyer_pubkey: "buyer-pubkey".into(),
seller_pubkey: "seller-pubkey".into(),
- items: vec![TradeOrderItem {
- bin_id: "bin-1".into(),
- bin_count: 1,
- }],
- discounts: None,
+ economics: order_economics(&missing_snapshot_items, false),
+ items: missing_snapshot_items,
}),
})
.expect_err("order request without snapshot should fail");
@@ -2924,7 +3058,7 @@ mod tests {
crate::listing::dvm::TradeListingMessageType::OrderRequest,
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- &TradeListingMessagePayload::OrderRequest(base_order()),
+ &TradeListingMessagePayload::TradeOrderRequested(base_order()),
);
event.tags[1][1] = "30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAw".into();
@@ -2954,7 +3088,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("open order");
index
@@ -2962,7 +3096,7 @@ mod tests {
"buyer-pubkey-2",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAw",
Some("order-2"),
- TradeListingMessagePayload::OrderRequest(alternate_order()),
+ TradeListingMessagePayload::TradeOrderRequested(alternate_order()),
))
.expect("second order request");
index
@@ -3149,7 +3283,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
);
let order_a = RadrootsTradeOrderWorkflowProjection::from_order_request(
&request_message,
@@ -3250,7 +3384,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("message-type-order-request"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "message-type-order-request".into(),
..base_order()
}),
@@ -3470,7 +3604,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("first order");
index
@@ -3478,7 +3612,7 @@ mod tests {
"buyer-pubkey-2",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAw",
Some("order-2"),
- TradeListingMessagePayload::OrderRequest(alternate_order()),
+ TradeListingMessagePayload::TradeOrderRequested(alternate_order()),
))
.expect("second order");
index
@@ -3587,7 +3721,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("order request");
@@ -3668,7 +3802,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("decline order request");
let declined = decline_index
@@ -3697,7 +3831,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-2"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-2".into(),
..base_order()
}),
@@ -3724,7 +3858,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-2"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-2".into(),
..base_order()
}),
@@ -3751,7 +3885,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-2"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-2".into(),
..base_order()
}),
@@ -3777,7 +3911,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-2"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-2".into(),
..base_order()
}),
@@ -3807,7 +3941,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("wrong-order-id"),
- TradeListingMessagePayload::OrderRequest(mismatched_order.clone()),
+ TradeListingMessagePayload::TradeOrderRequested(mismatched_order.clone()),
))
.expect_err("order id mismatch"),
RadrootsTradeProjectionError::OrderIdMismatch
@@ -3819,7 +3953,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-3"),
- TradeListingMessagePayload::OrderRequest(mismatched_order),
+ TradeListingMessagePayload::TradeOrderRequested(mismatched_order),
))
.expect_err("listing addr mismatch"),
RadrootsTradeProjectionError::ListingAddrMismatch
@@ -3836,7 +3970,7 @@ mod tests {
"buyer-pubkey-2",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(duplicate_order),
+ TradeListingMessagePayload::TradeOrderRequested(duplicate_order),
))
.expect_err("duplicate order identity mismatch"),
RadrootsTradeProjectionError::ListingAddrMismatch
@@ -3858,7 +3992,9 @@ mod tests {
)),
root_event_id: None,
prev_event_id: None,
- payload: TradeListingMessagePayload::OrderRequest(duplicate_listing_mismatch_order),
+ payload: TradeListingMessagePayload::TradeOrderRequested(
+ duplicate_listing_mismatch_order,
+ ),
};
assert_eq!(
index
@@ -3872,7 +4008,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-1"),
- TradeListingMessagePayload::OrderRequest(base_order()),
+ TradeListingMessagePayload::TradeOrderRequested(base_order()),
))
.expect("duplicate same order");
assert_eq!(duplicate_same.order_id, "order-1");
@@ -3888,7 +4024,7 @@ mod tests {
)),
root_event_id: None,
prev_event_id: None,
- payload: TradeListingMessagePayload::OrderRequest(TradeOrder {
+ payload: TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-1".into(),
seller_pubkey: "other-seller".into(),
..base_order()
@@ -3907,7 +4043,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-4"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-4".into(),
..base_order()
}),
@@ -3933,7 +4069,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-4"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-4".into(),
..base_order()
}),
@@ -3968,7 +4104,7 @@ mod tests {
"intruder",
listing_addr,
Some("order-request-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-request-actor".into(),
..base_order()
}),
@@ -3981,7 +4117,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-request-counterparty"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-request-counterparty".into(),
..base_order()
}),
@@ -4005,7 +4141,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-helper"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-helper".into(),
..base_order()
}),
@@ -4563,7 +4699,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-response-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-response-actor".into(),
..base_order()
}),
@@ -4590,7 +4726,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-revision-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-revision-actor".into(),
..base_order()
}),
@@ -4655,7 +4791,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-revision-accept-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-revision-accept-actor".into(),
..base_order()
}),
@@ -4682,7 +4818,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-revision-decline-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-revision-decline-actor".into(),
..base_order()
}),
@@ -4709,7 +4845,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-answer-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-answer-actor".into(),
..base_order()
}),
@@ -4745,7 +4881,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-discount-request-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-discount-request-actor".into(),
..base_order()
}),
@@ -4800,7 +4936,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-discount-offer-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-discount-offer-actor".into(),
..base_order()
}),
@@ -4855,7 +4991,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-discount-accept-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-discount-accept-actor".into(),
..base_order()
}),
@@ -4883,7 +5019,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-discount-decline-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-discount-decline-actor".into(),
..base_order()
}),
@@ -4909,7 +5045,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-fulfillment-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-fulfillment-actor".into(),
..base_order()
}),
@@ -4935,7 +5071,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-receipt-actor"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-receipt-actor".into(),
..base_order()
}),
@@ -4967,7 +5103,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-response-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-response-transition".into(),
..base_order()
}),
@@ -5002,7 +5138,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-revision-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-revision-transition".into(),
..base_order()
}),
@@ -5040,7 +5176,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-revision-accept-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-revision-accept-transition".into(),
..base_order()
}),
@@ -5075,7 +5211,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-revision-decline-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-revision-decline-transition".into(),
..base_order()
}),
@@ -5110,7 +5246,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-question-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-question-transition".into(),
..base_order()
}),
@@ -5144,7 +5280,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-answer-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-answer-transition".into(),
..base_order()
}),
@@ -5178,7 +5314,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-discount-offer-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-discount-offer-transition".into(),
..base_order()
}),
@@ -5215,7 +5351,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-discount-accept-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-discount-accept-transition".into(),
..base_order()
}),
@@ -5251,7 +5387,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-discount-decline-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-discount-decline-transition".into(),
..base_order()
}),
@@ -5285,7 +5421,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-cancel-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-cancel-transition".into(),
..base_order()
}),
@@ -5319,7 +5455,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-fulfillment-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-fulfillment-transition".into(),
..base_order()
}),
@@ -5348,7 +5484,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-receipt-transition"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-receipt-transition".into(),
..base_order()
}),
@@ -5387,7 +5523,7 @@ mod tests {
"buyer-pubkey",
listing_addr,
Some("order-revision-invalid-change"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-revision-invalid-change".into(),
..base_order()
}),
@@ -5424,7 +5560,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-question"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-question".into(),
..base_order()
}),
@@ -5462,7 +5598,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-revision-accept"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-revision-accept".into(),
..base_order()
}),
@@ -5510,7 +5646,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-revision-decline"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-revision-decline".into(),
..base_order()
}),
@@ -5553,7 +5689,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-discount"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-discount".into(),
..base_order()
}),
@@ -5609,7 +5745,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-discount-decline"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-discount-decline".into(),
..base_order()
}),
@@ -5650,7 +5786,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-seller-cancel"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-seller-cancel".into(),
..base_order()
}),
@@ -5677,7 +5813,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-preparing"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-preparing".into(),
..base_order()
}),
@@ -5715,7 +5851,7 @@ mod tests {
"buyer-pubkey",
"30402:seller-pubkey:AAAAAAAAAAAAAAAAAAAAAg",
Some("order-receipt"),
- TradeListingMessagePayload::OrderRequest(TradeOrder {
+ TradeListingMessagePayload::TradeOrderRequested(TradeOrder {
order_id: "order-receipt".into(),
..base_order()
}),
diff --git a/crates/trade/src/order.rs b/crates/trade/src/order.rs
@@ -10,10 +10,9 @@ use radroots_core::{RadrootsCoreCurrency, RadrootsCoreDecimal};
use radroots_events::kinds::KIND_LISTING;
use radroots_events::trade::{
RadrootsActiveTradeFulfillmentState, RadrootsTradeBuyerReceipt,
- RadrootsTradeFulfillmentUpdated, RadrootsTradeInventoryCommitment,
- RadrootsTradeOrder as TradeOrder, RadrootsTradeOrderCancelled, RadrootsTradeOrderDecision,
- RadrootsTradeOrderDecisionEvent, RadrootsTradeOrderEconomics, RadrootsTradeOrderItem,
- RadrootsTradeOrderRequested, RadrootsTradeOrderRevisionDecision,
+ RadrootsTradeFulfillmentUpdated, RadrootsTradeInventoryCommitment, RadrootsTradeOrderCancelled,
+ RadrootsTradeOrderDecision, RadrootsTradeOrderDecisionEvent, RadrootsTradeOrderEconomics,
+ RadrootsTradeOrderItem, RadrootsTradeOrderRequested, RadrootsTradeOrderRevisionDecision,
RadrootsTradeOrderRevisionDecisionEvent, RadrootsTradeOrderRevisionProposed,
RadrootsTradePaymentMethod, RadrootsTradePaymentRecorded, RadrootsTradeSettlementDecision,
RadrootsTradeSettlementDecisionEvent,
@@ -844,58 +843,6 @@ where
}
}
-pub fn canonicalize_order_request_for_signer(
- mut order: TradeOrder,
- signer_pubkey: &str,
-) -> Result<TradeOrder, RadrootsTradeOrderCanonicalizationError> {
- let order_id = normalized_required_string(core::mem::take(&mut order.order_id), "order_id")?;
- let listing_addr_raw =
- normalized_required_string(core::mem::take(&mut order.listing_addr), "listing_addr")?;
- let listing_addr = TradeListingAddress::parse(&listing_addr_raw).map_err(|error| {
- RadrootsTradeOrderCanonicalizationError::InvalidListingAddress(error.to_string())
- })?;
- if u32::from(listing_addr.kind) != KIND_LISTING {
- return Err(RadrootsTradeOrderCanonicalizationError::InvalidListingKind);
- }
-
- let buyer_pubkey = if order.buyer_pubkey.trim().is_empty() {
- signer_pubkey.to_string()
- } else {
- normalized_required_string(core::mem::take(&mut order.buyer_pubkey), "buyer_pubkey")?
- };
- if buyer_pubkey != signer_pubkey {
- return Err(RadrootsTradeOrderCanonicalizationError::InvalidBuyerSigner);
- }
-
- let seller_pubkey = if order.seller_pubkey.trim().is_empty() {
- listing_addr.seller_pubkey.clone()
- } else {
- normalized_required_string(core::mem::take(&mut order.seller_pubkey), "seller_pubkey")?
- };
- if seller_pubkey != listing_addr.seller_pubkey {
- return Err(RadrootsTradeOrderCanonicalizationError::InvalidSellerListing);
- }
-
- if order.items.is_empty() {
- return Err(RadrootsTradeOrderCanonicalizationError::MissingItems);
- }
- for (index, item) in order.items.iter_mut().enumerate() {
- item.bin_id = normalized_required_string(item.bin_id.clone(), "bin_id")?;
- if item.bin_count == 0 {
- return Err(RadrootsTradeOrderCanonicalizationError::InvalidBinCount { index });
- }
- }
-
- order.order_id = order_id;
- order.listing_addr = listing_addr.as_str();
- order.buyer_pubkey = buyer_pubkey;
- order.seller_pubkey = seller_pubkey;
- if order.discounts.as_ref().is_some_and(Vec::is_empty) {
- order.discounts = None;
- }
- Ok(order)
-}
-
pub fn canonicalize_active_order_request_for_signer(
mut request: RadrootsTradeOrderRequested,
signer_pubkey: &str,
@@ -3683,13 +3630,13 @@ mod tests {
use radroots_events::trade::{
RadrootsActiveTradeFulfillmentState, RadrootsTradeBuyerReceipt,
RadrootsTradeFulfillmentUpdated, RadrootsTradeInventoryCommitment,
- RadrootsTradeOrder as TradeOrder, RadrootsTradeOrderCancelled, RadrootsTradeOrderDecision,
- RadrootsTradeOrderDecisionEvent, RadrootsTradeOrderEconomicItem,
- RadrootsTradeOrderEconomicLine, RadrootsTradeOrderEconomics, RadrootsTradeOrderItem,
- RadrootsTradeOrderRequested, RadrootsTradeOrderRevisionDecision,
- RadrootsTradeOrderRevisionDecisionEvent, RadrootsTradeOrderRevisionProposed,
- RadrootsTradePaymentMethod, RadrootsTradePaymentRecorded, RadrootsTradePricingBasis,
- RadrootsTradeSettlementDecision, RadrootsTradeSettlementDecisionEvent,
+ RadrootsTradeOrderCancelled, RadrootsTradeOrderDecision, RadrootsTradeOrderDecisionEvent,
+ RadrootsTradeOrderEconomicItem, RadrootsTradeOrderEconomicLine,
+ RadrootsTradeOrderEconomics, RadrootsTradeOrderItem, RadrootsTradeOrderRequested,
+ RadrootsTradeOrderRevisionDecision, RadrootsTradeOrderRevisionDecisionEvent,
+ RadrootsTradeOrderRevisionProposed, RadrootsTradePaymentMethod,
+ RadrootsTradePaymentRecorded, RadrootsTradePricingBasis, RadrootsTradeSettlementDecision,
+ RadrootsTradeSettlementDecisionEvent,
};
use super::{
@@ -3705,8 +3652,7 @@ mod tests {
RadrootsListingInventoryBinAvailability, RadrootsListingInventoryOrderReservation,
RadrootsTradeOrderCanonicalizationError, add_inventory_reservation,
canonicalize_active_order_decision_for_signer,
- canonicalize_active_order_request_for_signer, canonicalize_order_request_for_signer,
- radroots_trade_order_economics_digest,
+ canonicalize_active_order_request_for_signer, radroots_trade_order_economics_digest,
reduce_active_order_events as reduce_active_order_events_with_revisions,
reduce_listing_inventory_accounting as reduce_listing_inventory_accounting_with_revisions,
};
@@ -3714,20 +3660,6 @@ mod tests {
const SELLER: &str = "1111111111111111111111111111111111111111111111111111111111111111";
const BUYER: &str = "2222222222222222222222222222222222222222222222222222222222222222";
- fn base_order(buyer_pubkey: &str, seller_pubkey: &str) -> TradeOrder {
- TradeOrder {
- order_id: "order-1".to_string(),
- listing_addr: format!("{KIND_LISTING}:{SELLER}:AAAAAAAAAAAAAAAAAAAAAg"),
- buyer_pubkey: buyer_pubkey.to_string(),
- seller_pubkey: seller_pubkey.to_string(),
- items: vec![RadrootsTradeOrderItem {
- bin_id: "bin-1".to_string(),
- bin_count: 1,
- }],
- discounts: None,
- }
- }
-
fn active_request(buyer_pubkey: &str, seller_pubkey: &str) -> RadrootsTradeOrderRequested {
RadrootsTradeOrderRequested {
order_id: " order-1 ".to_string(),
@@ -4147,15 +4079,6 @@ mod tests {
}
#[test]
- fn canonicalize_order_request_sets_missing_pubkeys() {
- let order = canonicalize_order_request_for_signer(base_order("", ""), SELLER)
- .expect("canonical order");
-
- assert_eq!(order.buyer_pubkey, SELLER);
- assert_eq!(order.seller_pubkey, SELLER);
- }
-
- #[test]
fn canonicalize_active_order_request_sets_authority_and_trims_items() {
let request =
canonicalize_active_order_request_for_signer(active_request("", ""), BUYER).unwrap();