commit e24f234fe34026b157d0ba9d24d03febea7110f8
parent 44ab15546bca8034df7f70408d0b977479dbb203
Author: triesap <tyson@radroots.org>
Date: Fri, 26 Jun 2026 13:15:35 +0000
Refactor CLI to trade product surface
Replace the active order command namespace with trade, route workflow status through SDK evidence ingestion, add private farm location set/get/clear handling with shared GeoNames cache wiring, and update CLI unit/target coverage for the new surface and removed command behavior.
Diffstat:
41 files changed, 3892 insertions(+), 4454 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -3944,6 +3944,7 @@ dependencies = [
"radroots_publish_proxy_protocol",
"radroots_relay_transport",
"radroots_runtime_paths",
+ "radroots_sp1_guest_trade",
"radroots_trade",
"reqwest",
"serde",
@@ -4012,6 +4013,7 @@ dependencies = [
"serde",
"serde_json",
"sha2",
+ "sqlx",
"thiserror 1.0.69",
]
diff --git a/src/cli/farm.rs b/src/cli/farm.rs
@@ -80,15 +80,27 @@ pub struct FarmLocationArgs {
#[derive(Debug, Clone, Subcommand)]
pub enum FarmLocationCommand {
- Update(FarmLocationUpdateArgs),
+ Set(FarmLocationSetArgs),
+ Get(FarmLocationKeyArgs),
+ Clear(FarmLocationKeyArgs),
}
#[derive(Debug, Clone, Args)]
-pub struct FarmLocationUpdateArgs {
+pub struct FarmLocationSetArgs {
#[arg(long)]
- pub field: Option<String>,
+ pub lat: Option<f64>,
#[arg(long)]
- pub value: Option<String>,
+ pub lng: Option<f64>,
+ #[arg(long = "farm-d-tag")]
+ pub farm_d_tag: Option<String>,
+ #[arg(long = "lookup", default_value = "geonames")]
+ pub lookup: String,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct FarmLocationKeyArgs {
+ #[arg(long = "farm-d-tag")]
+ pub farm_d_tag: Option<String>,
}
#[derive(Debug, Clone, Args)]
diff --git a/src/cli/global.rs b/src/cli/global.rs
@@ -132,6 +132,19 @@ pub struct FarmUpdateArgs {
pub value: Vec<String>,
}
+#[derive(Debug, Clone)]
+pub struct FarmPrivateLocationSetArgs {
+ pub farm_d_tag: Option<String>,
+ pub latitude: f64,
+ pub longitude: f64,
+ pub lookup: String,
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct FarmPrivateLocationKeyArgs {
+ pub farm_d_tag: Option<String>,
+}
+
#[derive(Debug, Clone, Default)]
pub struct FarmPublishArgs {
pub scope: Option<FarmScopeArg>,
@@ -208,30 +221,30 @@ pub struct OrderDraftAdjustmentArgs {
}
#[derive(Debug, Clone)]
-pub struct OrderSubmitArgs {
+pub struct TradeSubmitArgs {
pub key: String,
pub idempotency_key: Option<String>,
}
#[derive(Debug, Clone)]
-pub struct OrderAppRecordExportArgs {
+pub struct TradeAppRecordExportArgs {
pub record_id: String,
pub output: Option<PathBuf>,
}
#[derive(Debug, Clone)]
-pub struct OrderRebindArgs {
+pub struct TradeRebindArgs {
pub key: String,
pub selector: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum OrderDecisionArg {
+pub enum TradeDecisionArg {
Accept,
Decline,
}
-impl OrderDecisionArg {
+impl TradeDecisionArg {
pub fn as_str(self) -> &'static str {
match self {
Self::Accept => "accepted",
@@ -248,22 +261,22 @@ impl OrderDecisionArg {
}
#[derive(Debug, Clone)]
-pub struct OrderDecisionArgs {
+pub struct TradeDecisionArgs {
pub key: String,
- pub decision: OrderDecisionArg,
+ pub decision: TradeDecisionArg,
pub reason: Option<String>,
pub idempotency_key: Option<String>,
}
#[derive(Debug, Clone)]
-pub struct OrderCancelArgs {
+pub struct TradeCancelArgs {
pub key: String,
pub reason: String,
pub idempotency_key: Option<String>,
}
#[derive(Debug, Clone)]
-pub struct OrderRevisionProposeArgs {
+pub struct TradeRevisionProposeArgs {
pub key: String,
pub reason: String,
pub bin_id: Option<String>,
@@ -277,12 +290,12 @@ pub struct OrderRevisionProposeArgs {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum OrderRevisionDecisionArg {
+pub enum TradeRevisionDecisionArg {
Accept,
Decline,
}
-impl OrderRevisionDecisionArg {
+impl TradeRevisionDecisionArg {
pub fn as_str(self) -> &'static str {
match self {
Self::Accept => "accepted",
@@ -299,16 +312,16 @@ impl OrderRevisionDecisionArg {
}
#[derive(Debug, Clone)]
-pub struct OrderRevisionDecisionArgs {
+pub struct TradeRevisionDecisionArgs {
pub key: String,
pub revision_id: String,
- pub decision: OrderRevisionDecisionArg,
+ pub decision: TradeRevisionDecisionArg,
pub reason: Option<String>,
pub idempotency_key: Option<String>,
}
#[derive(Debug, Clone)]
-pub struct OrderStatusArgs {
+pub struct TradeStatusArgs {
pub key: String,
}
diff --git a/src/cli/input.rs b/src/cli/input.rs
@@ -46,9 +46,9 @@ pub fn target_operation_input(command: &TargetCommand) -> OperationData {
AccountCommand, AccountSelectionCommand, BasketAdjustmentCommand, BasketCommand,
BasketItemCommand, BasketQuoteCommand, FarmCommand, FarmFulfillmentCommand,
FarmLocationCommand, FarmProfileCommand, ListingAppCommand, ListingCommand, MarketCommand,
- MarketListingCommand, MarketProductCommand, OrderAppCommand, OrderCommand,
- OrderEventCommand, OrderRevisionCommand, OrderStatusCommand, StoreBackupCommand,
- StoreCommand, ValidationCommand, ValidationReceiptCommand,
+ MarketListingCommand, MarketProductCommand, StoreBackupCommand, StoreCommand,
+ TradeAppCommand, TradeCommand, TradeEventCommand, TradeRevisionCommand, TradeStatusCommand,
+ ValidationCommand, ValidationReceiptCommand,
};
let mut input = OperationData::new();
@@ -103,9 +103,18 @@ pub fn target_operation_input(command: &TargetCommand) -> OperationData {
}
},
FarmCommand::Location(args) => match &args.command {
- FarmLocationCommand::Update(args) => {
- insert_string(&mut input, "field", &args.field);
- insert_string(&mut input, "value", &args.value);
+ FarmLocationCommand::Set(args) => {
+ if let Some(latitude) = args.lat {
+ insert_number(&mut input, "latitude", latitude);
+ }
+ if let Some(longitude) = args.lng {
+ insert_number(&mut input, "longitude", longitude);
+ }
+ insert_string(&mut input, "farm_d_tag", &args.farm_d_tag);
+ input.insert("lookup".to_owned(), Value::String(args.lookup.clone()));
+ }
+ FarmLocationCommand::Get(args) | FarmLocationCommand::Clear(args) => {
+ insert_string(&mut input, "farm_d_tag", &args.farm_d_tag);
}
},
FarmCommand::Fulfillment(args) => match &args.command {
@@ -227,34 +236,34 @@ pub fn target_operation_input(command: &TargetCommand) -> OperationData {
},
BasketCommand::List => {}
},
- TargetCommand::Order(args) => match &args.command {
- OrderCommand::Submit(args) => {
- insert_string(&mut input, "order_id", &args.order_id);
+ TargetCommand::Trade(args) => match &args.command {
+ TradeCommand::Submit(args) => {
+ insert_string(&mut input, "trade_id", &args.trade_id);
}
- OrderCommand::Get(args) => insert_string(&mut input, "order_id", &args.order_id),
- OrderCommand::App(args) => match &args.command {
- OrderAppCommand::Export(args) => {
+ TradeCommand::Get(args) => insert_string(&mut input, "trade_id", &args.trade_id),
+ TradeCommand::App(args) => match &args.command {
+ TradeAppCommand::Export(args) => {
insert_string(&mut input, "record_id", &args.record_id);
insert_path(&mut input, "output", &args.output);
}
- OrderAppCommand::List => {}
+ TradeAppCommand::List => {}
},
- OrderCommand::Rebind(args) => {
- insert_string(&mut input, "order_id", &args.order_id);
+ TradeCommand::Rebind(args) => {
+ insert_string(&mut input, "trade_id", &args.trade_id);
insert_string(&mut input, "selector", &args.selector);
}
- OrderCommand::Accept(args) => insert_string(&mut input, "order_id", &args.order_id),
- OrderCommand::Decline(args) => {
- insert_string(&mut input, "order_id", &args.order_id);
+ TradeCommand::Accept(args) => insert_string(&mut input, "trade_id", &args.trade_id),
+ TradeCommand::Decline(args) => {
+ insert_string(&mut input, "trade_id", &args.trade_id);
insert_string(&mut input, "reason", &args.reason);
}
- OrderCommand::Cancel(args) => {
- insert_string(&mut input, "order_id", &args.order_id);
+ TradeCommand::Cancel(args) => {
+ insert_string(&mut input, "trade_id", &args.trade_id);
insert_string(&mut input, "reason", &args.reason);
}
- OrderCommand::Revision(revision) => match &revision.command {
- OrderRevisionCommand::Propose(args) => {
- insert_string(&mut input, "order_id", &args.order_id);
+ TradeCommand::Revision(revision) => match &revision.command {
+ TradeRevisionCommand::Propose(args) => {
+ insert_string(&mut input, "trade_id", &args.trade_id);
insert_string(&mut input, "reason", &args.reason);
insert_string(&mut input, "bin_id", &args.bin_id);
if let Some(bin_count) = args.bin_count {
@@ -269,27 +278,27 @@ pub fn target_operation_input(command: &TargetCommand) -> OperationData {
insert_string(&mut input, "adjustment_currency", &args.adjustment_currency);
insert_string(&mut input, "adjustment_reason", &args.adjustment_reason);
}
- OrderRevisionCommand::Accept(args) => {
- insert_string(&mut input, "order_id", &args.order_id);
+ TradeRevisionCommand::Accept(args) => {
+ insert_string(&mut input, "trade_id", &args.trade_id);
insert_string(&mut input, "revision_id", &args.revision_id);
}
- OrderRevisionCommand::Decline(args) => {
- insert_string(&mut input, "order_id", &args.order_id);
+ TradeRevisionCommand::Decline(args) => {
+ insert_string(&mut input, "trade_id", &args.trade_id);
insert_string(&mut input, "revision_id", &args.revision_id);
insert_string(&mut input, "reason", &args.reason);
}
},
- OrderCommand::Status(status) => match &status.command {
- OrderStatusCommand::Get(args) => {
- insert_string(&mut input, "order_id", &args.order_id)
+ TradeCommand::Status(status) => match &status.command {
+ TradeStatusCommand::Get(args) => {
+ insert_string(&mut input, "trade_id", &args.trade_id)
}
},
- OrderCommand::Event(event) => match &event.command {
- OrderEventCommand::List(args) | OrderEventCommand::Watch(args) => {
- insert_string(&mut input, "order_id", &args.order_id)
+ TradeCommand::Event(event) => match &event.command {
+ TradeEventCommand::List(args) | TradeEventCommand::Watch(args) => {
+ insert_string(&mut input, "trade_id", &args.trade_id)
}
},
- OrderCommand::List => {}
+ TradeCommand::List => {}
},
TargetCommand::Validation(args) => match &args.command {
ValidationCommand::Receipt(receipt) => match &receipt.command {
@@ -297,7 +306,7 @@ pub fn target_operation_input(command: &TargetCommand) -> OperationData {
insert_string(&mut input, "receipt_event_id", &args.receipt_event_id);
}
ValidationReceiptCommand::List(args) => {
- insert_string(&mut input, "order_id", &args.order_id);
+ insert_string(&mut input, "trade_id", &args.trade_id);
}
},
},
@@ -329,6 +338,12 @@ fn insert_string_array(input: &mut OperationData, key: &str, values: &[String])
}
}
+fn insert_number(input: &mut OperationData, key: &str, value: f64) {
+ if let Some(number) = serde_json::Number::from_f64(value) {
+ input.insert(key.to_owned(), Value::Number(number));
+ }
+}
+
fn insert_path(input: &mut OperationData, key: &str, value: &Option<std::path::PathBuf>) {
if let Some(value) = value {
input.insert(
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
@@ -10,11 +10,11 @@ pub mod health;
pub mod input;
pub mod listing;
pub mod market;
-pub mod order;
pub mod relay;
pub mod signer;
pub mod store;
pub mod sync;
+pub mod trade;
pub mod validation;
pub mod workspace;
@@ -25,11 +25,11 @@ pub use farm::*;
pub use health::*;
pub use listing::*;
pub use market::*;
-pub use order::*;
pub use relay::*;
pub use signer::*;
pub use store::*;
pub use sync::*;
+pub use trade::*;
pub use validation::*;
pub use workspace::*;
@@ -130,10 +130,10 @@ pub enum TargetCommand {
Listing(ListingArgs),
#[command(about = "Refresh and query market data from the local replica.")]
Market(MarketArgs),
- #[command(about = "Prepare baskets and quotes before order coordination.")]
+ #[command(about = "Prepare baskets and quotes before trade coordination.")]
Basket(BasketArgs),
- #[command(about = "Coordinate buyer and farmer order agreement events.")]
- Order(OrderArgs),
+ #[command(about = "Coordinate buyer and farmer trade agreement events.")]
+ Trade(TradeArgs),
#[command(about = "Inspect validation receipts and proof state.")]
Validation(ValidationArgs),
}
@@ -204,7 +204,9 @@ impl TargetCommand {
FarmProfileCommand::Update(_) => "farm.profile.update",
},
FarmCommand::Location(location) => match location.command {
- FarmLocationCommand::Update(_) => "farm.location.update",
+ FarmLocationCommand::Set(_) => "farm.location.set",
+ FarmLocationCommand::Get(_) => "farm.location.get",
+ FarmLocationCommand::Clear(_) => "farm.location.clear",
},
FarmCommand::Fulfillment(fulfillment) => match fulfillment.command {
FarmFulfillmentCommand::Update(_) => "farm.fulfillment.update",
@@ -255,29 +257,29 @@ impl TargetCommand {
BasketQuoteCommand::Create(_) => "basket.quote.create",
},
},
- Self::Order(args) => match &args.command {
- OrderCommand::Submit(_) => "order.submit",
- OrderCommand::Get(_) => "order.get",
- OrderCommand::List => "order.list",
- OrderCommand::App(app) => match &app.command {
- OrderAppCommand::List => "order.app.list",
- OrderAppCommand::Export(_) => "order.app.export",
+ Self::Trade(args) => match &args.command {
+ TradeCommand::Submit(_) => "trade.submit",
+ TradeCommand::Get(_) => "trade.get",
+ TradeCommand::List => "trade.list",
+ TradeCommand::App(app) => match &app.command {
+ TradeAppCommand::List => "trade.app.list",
+ TradeAppCommand::Export(_) => "trade.app.export",
},
- OrderCommand::Rebind(_) => "order.rebind",
- OrderCommand::Accept(_) => "order.accept",
- OrderCommand::Decline(_) => "order.decline",
- OrderCommand::Cancel(_) => "order.cancel",
- OrderCommand::Revision(revision) => match &revision.command {
- OrderRevisionCommand::Propose(_) => "order.revision.propose",
- OrderRevisionCommand::Accept(_) => "order.revision.accept",
- OrderRevisionCommand::Decline(_) => "order.revision.decline",
+ TradeCommand::Rebind(_) => "trade.rebind",
+ TradeCommand::Accept(_) => "trade.accept",
+ TradeCommand::Decline(_) => "trade.decline",
+ TradeCommand::Cancel(_) => "trade.cancel",
+ TradeCommand::Revision(revision) => match &revision.command {
+ TradeRevisionCommand::Propose(_) => "trade.revision.propose",
+ TradeRevisionCommand::Accept(_) => "trade.revision.accept",
+ TradeRevisionCommand::Decline(_) => "trade.revision.decline",
},
- OrderCommand::Status(status) => match &status.command {
- OrderStatusCommand::Get(_) => "order.status.get",
+ TradeCommand::Status(status) => match &status.command {
+ TradeStatusCommand::Get(_) => "trade.status.get",
},
- OrderCommand::Event(event) => match &event.command {
- OrderEventCommand::List(_) => "order.event.list",
- OrderEventCommand::Watch(_) => "order.event.watch",
+ TradeCommand::Event(event) => match &event.command {
+ TradeEventCommand::List(_) => "trade.event.list",
+ TradeEventCommand::Watch(_) => "trade.event.watch",
},
},
Self::Validation(args) => match &args.command {
@@ -297,8 +299,8 @@ mod tests {
use clap::{CommandFactory, Parser};
use super::{
- AccountCommand, FarmCommand, ListingCommand, OrderCommand, OrderRevisionCommand,
- TargetCliArgs, TargetOutputFormat, ValidationCommand, ValidationReceiptCommand,
+ AccountCommand, FarmCommand, ListingCommand, TargetCliArgs, TargetOutputFormat,
+ TradeCommand, TradeRevisionCommand, ValidationCommand, ValidationReceiptCommand,
};
use crate::registry::OPERATION_REGISTRY;
@@ -332,7 +334,7 @@ mod tests {
"listing",
"market",
"basket",
- "order",
+ "trade",
"validation",
]
.into_iter()
@@ -460,17 +462,17 @@ mod tests {
#[test]
fn target_parser_accepts_order_rebind_inputs() {
let parsed =
- TargetCliArgs::try_parse_from(["radroots", "order", "rebind", "ord_test", "acct_test"])
+ TargetCliArgs::try_parse_from(["radroots", "trade", "rebind", "ord_test", "acct_test"])
.expect("target args parse");
- assert_eq!(parsed.command.operation_id(), "order.rebind");
- let crate::cli::TargetCommand::Order(order) = parsed.command else {
- panic!("expected order command")
+ assert_eq!(parsed.command.operation_id(), "trade.rebind");
+ let crate::cli::TargetCommand::Trade(trade) = parsed.command else {
+ panic!("expected trade command")
};
- let OrderCommand::Rebind(args) = order.command else {
- panic!("expected order rebind command")
+ let TradeCommand::Rebind(args) = trade.command else {
+ panic!("expected trade rebind command")
};
- assert_eq!(args.order_id.as_deref(), Some("ord_test"));
+ assert_eq!(args.trade_id.as_deref(), Some("ord_test"));
assert_eq!(args.selector.as_deref(), Some("acct_test"));
}
@@ -478,7 +480,7 @@ mod tests {
fn target_parser_accepts_order_cancel_reason() {
let parsed = TargetCliArgs::try_parse_from([
"radroots",
- "order",
+ "trade",
"cancel",
"ord_test",
"--reason",
@@ -486,14 +488,14 @@ mod tests {
])
.expect("target args parse");
- assert_eq!(parsed.command.operation_id(), "order.cancel");
- let crate::cli::TargetCommand::Order(order) = parsed.command else {
- panic!("expected order command")
+ assert_eq!(parsed.command.operation_id(), "trade.cancel");
+ let crate::cli::TargetCommand::Trade(trade) = parsed.command else {
+ panic!("expected trade command")
};
- let OrderCommand::Cancel(args) = order.command else {
- panic!("expected order cancel command")
+ let TradeCommand::Cancel(args) = trade.command else {
+ panic!("expected trade cancel command")
};
- assert_eq!(args.order_id.as_deref(), Some("ord_test"));
+ assert_eq!(args.trade_id.as_deref(), Some("ord_test"));
assert_eq!(args.reason.as_deref(), Some("changed plans"));
}
@@ -501,7 +503,7 @@ mod tests {
fn target_parser_accepts_order_revision_propose_inputs() {
let parsed = TargetCliArgs::try_parse_from([
"radroots",
- "order",
+ "trade",
"revision",
"propose",
"ord_test",
@@ -524,17 +526,17 @@ mod tests {
])
.expect("target args parse");
- assert_eq!(parsed.command.operation_id(), "order.revision.propose");
- let crate::cli::TargetCommand::Order(order) = parsed.command else {
- panic!("expected order command")
+ assert_eq!(parsed.command.operation_id(), "trade.revision.propose");
+ let crate::cli::TargetCommand::Trade(trade) = parsed.command else {
+ panic!("expected trade command")
};
- let OrderCommand::Revision(revision) = order.command else {
- panic!("expected order revision command")
+ let TradeCommand::Revision(revision) = trade.command else {
+ panic!("expected trade revision command")
};
- let OrderRevisionCommand::Propose(args) = revision.command else {
- panic!("expected order revision propose command")
+ let TradeRevisionCommand::Propose(args) = revision.command else {
+ panic!("expected trade revision propose command")
};
- assert_eq!(args.order_id.as_deref(), Some("ord_test"));
+ assert_eq!(args.trade_id.as_deref(), Some("ord_test"));
assert_eq!(args.reason.as_deref(), Some("update count"));
assert_eq!(args.bin_id.as_deref(), Some("bin-1"));
assert_eq!(args.bin_count, Some(3));
@@ -546,7 +548,7 @@ mod tests {
fn target_parser_accepts_order_revision_decision_inputs() {
let accepted = TargetCliArgs::try_parse_from([
"radroots",
- "order",
+ "trade",
"revision",
"accept",
"ord_test",
@@ -555,45 +557,45 @@ mod tests {
])
.expect("target args parse");
- assert_eq!(accepted.command.operation_id(), "order.revision.accept");
- let crate::cli::TargetCommand::Order(order) = accepted.command else {
- panic!("expected order command")
+ assert_eq!(accepted.command.operation_id(), "trade.revision.accept");
+ let crate::cli::TargetCommand::Trade(trade) = accepted.command else {
+ panic!("expected trade command")
};
- let OrderCommand::Revision(revision) = order.command else {
- panic!("expected order revision command")
+ let TradeCommand::Revision(revision) = trade.command else {
+ panic!("expected trade revision command")
};
- let OrderRevisionCommand::Accept(args) = revision.command else {
- panic!("expected order revision accept command")
+ let TradeRevisionCommand::Accept(args) = revision.command else {
+ panic!("expected trade revision accept command")
};
- assert_eq!(args.order_id.as_deref(), Some("ord_test"));
+ assert_eq!(args.trade_id.as_deref(), Some("ord_test"));
assert_eq!(args.revision_id.as_deref(), Some("rev_test"));
let declined = TargetCliArgs::try_parse_from([
"radroots",
- "order",
+ "trade",
"revision",
"decline",
"ord_test",
"--revision-id",
"rev_test",
"--reason",
- "keep original order",
+ "keep original trade",
])
.expect("target args parse");
- assert_eq!(declined.command.operation_id(), "order.revision.decline");
- let crate::cli::TargetCommand::Order(order) = declined.command else {
- panic!("expected order command")
+ assert_eq!(declined.command.operation_id(), "trade.revision.decline");
+ let crate::cli::TargetCommand::Trade(trade) = declined.command else {
+ panic!("expected trade command")
};
- let OrderCommand::Revision(revision) = order.command else {
- panic!("expected order revision command")
+ let TradeCommand::Revision(revision) = trade.command else {
+ panic!("expected trade revision command")
};
- let OrderRevisionCommand::Decline(args) = revision.command else {
- panic!("expected order revision decline command")
+ let TradeRevisionCommand::Decline(args) = revision.command else {
+ panic!("expected trade revision decline command")
};
- assert_eq!(args.order_id.as_deref(), Some("ord_test"));
+ assert_eq!(args.trade_id.as_deref(), Some("ord_test"));
assert_eq!(args.revision_id.as_deref(), Some("rev_test"));
- assert_eq!(args.reason.as_deref(), Some("keep original order"));
+ assert_eq!(args.reason.as_deref(), Some("keep original trade"));
}
#[test]
@@ -624,7 +626,7 @@ mod tests {
"validation",
"receipt",
"list",
- "--order-id",
+ "--trade-id",
"ord_1",
])
.expect("target args parse");
@@ -636,7 +638,7 @@ mod tests {
let ValidationReceiptCommand::List(args) = receipt.command else {
panic!("expected validation receipt list command")
};
- assert_eq!(args.order_id.as_deref(), Some("ord_1"));
+ assert_eq!(args.trade_id.as_deref(), Some("ord_1"));
let verify = TargetCliArgs::try_parse_from([
"radroots",
diff --git a/src/cli/order.rs b/src/cli/order.rs
@@ -1,146 +0,0 @@
-use std::path::PathBuf;
-
-use clap::{Args, Subcommand};
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderArgs {
- #[command(subcommand)]
- pub command: OrderCommand,
-}
-
-#[derive(Debug, Clone, Subcommand)]
-pub enum OrderCommand {
- Submit(OrderSubmitArgs),
- Get(OrderKeyArgs),
- List,
- App(OrderAppArgs),
- Rebind(OrderRebindArgs),
- Accept(OrderKeyArgs),
- Decline(OrderDeclineArgs),
- Cancel(OrderCancelArgs),
- Revision(OrderRevisionArgs),
- Status(OrderStatusArgs),
- Event(OrderEventArgs),
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderSubmitArgs {
- pub order_id: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderKeyArgs {
- pub order_id: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderAppArgs {
- #[command(subcommand)]
- pub command: OrderAppCommand,
-}
-
-#[derive(Debug, Clone, Subcommand)]
-pub enum OrderAppCommand {
- List,
- Export(OrderAppExportArgs),
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderAppExportArgs {
- pub record_id: Option<String>,
- #[arg(long)]
- pub output: Option<PathBuf>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderRebindArgs {
- pub order_id: Option<String>,
- pub selector: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderDeclineArgs {
- pub order_id: Option<String>,
- #[arg(long)]
- pub reason: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderCancelArgs {
- pub order_id: Option<String>,
- #[arg(long)]
- pub reason: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderRevisionArgs {
- #[command(subcommand)]
- pub command: OrderRevisionCommand,
-}
-
-#[derive(Debug, Clone, Subcommand)]
-pub enum OrderRevisionCommand {
- Propose(OrderRevisionProposeArgs),
- Accept(OrderRevisionDecisionArgs),
- Decline(OrderRevisionDeclineArgs),
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderRevisionProposeArgs {
- pub order_id: Option<String>,
- #[arg(long)]
- pub reason: Option<String>,
- #[arg(long)]
- pub bin_id: Option<String>,
- #[arg(long)]
- pub bin_count: Option<u32>,
- #[arg(long)]
- pub adjustment_id: Option<String>,
- #[arg(long)]
- pub adjustment_effect: Option<String>,
- #[arg(long)]
- pub adjustment_amount: Option<String>,
- #[arg(long)]
- pub adjustment_currency: Option<String>,
- #[arg(long)]
- pub adjustment_reason: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderRevisionDecisionArgs {
- pub order_id: Option<String>,
- #[arg(long)]
- pub revision_id: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderRevisionDeclineArgs {
- pub order_id: Option<String>,
- #[arg(long)]
- pub revision_id: Option<String>,
- #[arg(long)]
- pub reason: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderStatusArgs {
- #[command(subcommand)]
- pub command: OrderStatusCommand,
-}
-
-#[derive(Debug, Clone, Subcommand)]
-pub enum OrderStatusCommand {
- Get(OrderKeyArgs),
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct OrderEventArgs {
- #[command(subcommand)]
- pub command: OrderEventCommand,
-}
-
-#[derive(Debug, Clone, Subcommand)]
-pub enum OrderEventCommand {
- List(OrderKeyArgs),
- Watch(OrderKeyArgs),
-}
diff --git a/src/cli/trade.rs b/src/cli/trade.rs
@@ -0,0 +1,146 @@
+use std::path::PathBuf;
+
+use clap::{Args, Subcommand};
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeArgs {
+ #[command(subcommand)]
+ pub command: TradeCommand,
+}
+
+#[derive(Debug, Clone, Subcommand)]
+pub enum TradeCommand {
+ Submit(TradeSubmitArgs),
+ Get(TradeKeyArgs),
+ List,
+ App(TradeAppArgs),
+ Rebind(TradeRebindArgs),
+ Accept(TradeKeyArgs),
+ Decline(TradeDeclineArgs),
+ Cancel(TradeCancelArgs),
+ Revision(TradeRevisionArgs),
+ Status(TradeStatusArgs),
+ Event(TradeEventArgs),
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeSubmitArgs {
+ pub trade_id: Option<String>,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeKeyArgs {
+ pub trade_id: Option<String>,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeAppArgs {
+ #[command(subcommand)]
+ pub command: TradeAppCommand,
+}
+
+#[derive(Debug, Clone, Subcommand)]
+pub enum TradeAppCommand {
+ List,
+ Export(TradeAppExportArgs),
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeAppExportArgs {
+ pub record_id: Option<String>,
+ #[arg(long)]
+ pub output: Option<PathBuf>,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeRebindArgs {
+ pub trade_id: Option<String>,
+ pub selector: Option<String>,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeDeclineArgs {
+ pub trade_id: Option<String>,
+ #[arg(long)]
+ pub reason: Option<String>,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeCancelArgs {
+ pub trade_id: Option<String>,
+ #[arg(long)]
+ pub reason: Option<String>,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeRevisionArgs {
+ #[command(subcommand)]
+ pub command: TradeRevisionCommand,
+}
+
+#[derive(Debug, Clone, Subcommand)]
+pub enum TradeRevisionCommand {
+ Propose(TradeRevisionProposeArgs),
+ Accept(TradeRevisionDecisionArgs),
+ Decline(TradeRevisionDeclineArgs),
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeRevisionProposeArgs {
+ pub trade_id: Option<String>,
+ #[arg(long)]
+ pub reason: Option<String>,
+ #[arg(long)]
+ pub bin_id: Option<String>,
+ #[arg(long)]
+ pub bin_count: Option<u32>,
+ #[arg(long)]
+ pub adjustment_id: Option<String>,
+ #[arg(long)]
+ pub adjustment_effect: Option<String>,
+ #[arg(long)]
+ pub adjustment_amount: Option<String>,
+ #[arg(long)]
+ pub adjustment_currency: Option<String>,
+ #[arg(long)]
+ pub adjustment_reason: Option<String>,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeRevisionDecisionArgs {
+ pub trade_id: Option<String>,
+ #[arg(long)]
+ pub revision_id: Option<String>,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeRevisionDeclineArgs {
+ pub trade_id: Option<String>,
+ #[arg(long)]
+ pub revision_id: Option<String>,
+ #[arg(long)]
+ pub reason: Option<String>,
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeStatusArgs {
+ #[command(subcommand)]
+ pub command: TradeStatusCommand,
+}
+
+#[derive(Debug, Clone, Subcommand)]
+pub enum TradeStatusCommand {
+ Get(TradeKeyArgs),
+}
+
+#[derive(Debug, Clone, Args)]
+pub struct TradeEventArgs {
+ #[command(subcommand)]
+ pub command: TradeEventCommand,
+}
+
+#[derive(Debug, Clone, Subcommand)]
+pub enum TradeEventCommand {
+ List(TradeKeyArgs),
+ Watch(TradeKeyArgs),
+}
diff --git a/src/cli/validation.rs b/src/cli/validation.rs
@@ -33,8 +33,8 @@ pub struct ValidationReceiptEventArgs {
#[derive(Debug, Clone, Args)]
pub struct ValidationReceiptListArgs {
- #[arg(long)]
- pub order_id: Option<String>,
+ #[arg(long = "trade-id")]
+ pub trade_id: Option<String>,
}
#[derive(Debug, Clone, Args)]
diff --git a/src/main.rs b/src/main.rs
@@ -19,7 +19,7 @@ use crate::cli::input::runtime_invocation_args_from_target;
use crate::cli::{TargetCliArgs, TargetOutputFormat};
use crate::ops::exec::{
BasketOperationService, CoreOperationService, FarmOperationService, ListingOperationService,
- MarketOperationService, OrderOperationService, RuntimeOperationService,
+ MarketOperationService, RuntimeOperationService, TradeOperationService,
ValidationOperationService,
};
use crate::ops::{
@@ -158,7 +158,13 @@ fn execute_request(
TargetOperationRequest::FarmProfileUpdate(request) => {
execute_with(FarmOperationService::new(config), request)
}
- TargetOperationRequest::FarmLocationUpdate(request) => {
+ TargetOperationRequest::FarmLocationSet(request) => {
+ execute_with(FarmOperationService::new(config), request)
+ }
+ TargetOperationRequest::FarmLocationGet(request) => {
+ execute_with(FarmOperationService::new(config), request)
+ }
+ TargetOperationRequest::FarmLocationClear(request) => {
execute_with(FarmOperationService::new(config), request)
}
TargetOperationRequest::FarmFulfillmentUpdate(request) => {
@@ -239,50 +245,50 @@ fn execute_request(
TargetOperationRequest::BasketQuoteCreate(request) => {
execute_with(BasketOperationService::new(config), request)
}
- TargetOperationRequest::OrderSubmit(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeSubmit(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderGet(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeGet(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderList(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeList(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderAppList(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeAppList(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderAppExport(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeAppExport(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderRebind(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeRebind(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderAccept(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeAccept(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderDecline(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeDecline(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderCancel(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeCancel(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderRevisionPropose(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeRevisionPropose(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderRevisionAccept(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeRevisionAccept(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderRevisionDecline(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeRevisionDecline(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderStatusGet(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeStatusGet(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderEventList(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeEventList(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::OrderEventWatch(request) => {
- execute_with(OrderOperationService::new(config), request)
+ TargetOperationRequest::TradeEventWatch(request) => {
+ execute_with(TradeOperationService::new(config), request)
}
TargetOperationRequest::ValidationReceiptGet(request) => {
execute_with(ValidationOperationService::new(config), request)
diff --git a/src/ops/exec/basket.rs b/src/ops/exec/basket.rs
@@ -79,8 +79,8 @@ struct BasketAdjustment {
struct BasketQuote {
quote_id: String,
quote_version: u32,
- order_id: String,
- order_file: String,
+ trade_id: String,
+ trade_file: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
economics: Option<RadrootsOrderEconomics>,
ready_for_submit: bool,
@@ -516,7 +516,7 @@ impl OperationService<BasketQuoteCreateRequest> for BasketOperationService<'_> {
"basket_id": basket_id,
"file": loaded.file.display().to_string(),
"item": item,
- "order": order,
+ "trade": order,
"actions": ["radroots basket quote create"],
}));
}
@@ -542,8 +542,8 @@ impl OperationService<BasketQuoteCreateRequest> for BasketOperationService<'_> {
.as_ref()
.map(|economics| economics.quote_version)
.unwrap_or(1),
- order_id: order.order_id.clone(),
- order_file: order.file.clone(),
+ trade_id: order.order_id.clone(),
+ trade_file: order.file.clone(),
economics: quote_economics,
ready_for_submit: order.ready_for_submit,
created_at_unix: now_unix(),
@@ -559,7 +559,7 @@ impl OperationService<BasketQuoteCreateRequest> for BasketOperationService<'_> {
"basket_id": loaded.document.basket.basket_id,
"file": loaded.file.display().to_string(),
"quote": quote,
- "order": order,
+ "trade": order,
"actions": quote_actions(&order),
}))
}
@@ -1083,9 +1083,9 @@ fn basket_actions(document: &BasketDocument, issues: &[BasketIssue]) -> Vec<Stri
fn quote_actions(order: &OrderNewView) -> Vec<String> {
if order.ready_for_submit {
- vec![format!("radroots order submit {}", order.order_id)]
+ vec![format!("radroots trade submit {}", order.order_id)]
} else {
- let mut actions = vec![format!("radroots order get {}", order.order_id)];
+ let mut actions = vec![format!("radroots trade get {}", order.order_id)];
actions.extend(order.actions.iter().cloned());
actions
}
@@ -1528,30 +1528,30 @@ mod tests {
assert_eq!(envelope.operation_id, "basket.quote.create");
assert_eq!(envelope.result["state"], "quoted");
assert!(
- envelope.result["quote"]["order_id"]
+ envelope.result["quote"]["trade_id"]
.as_str()
.unwrap()
.starts_with("ord_")
);
assert!(
- envelope.result["order"]["buyer_account_id"]
+ envelope.result["trade"]["buyer_account_id"]
.as_str()
.expect("buyer account id")
.len()
> 8
);
assert!(
- envelope.result["order"]["buyer_pubkey"]
+ envelope.result["trade"]["buyer_pubkey"]
.as_str()
.expect("buyer pubkey")
.len()
== 64
);
assert_eq!(
- envelope.result["order"]["buyer_actor_source"],
+ envelope.result["trade"]["buyer_actor_source"],
"resolved_account"
);
- let order_file = PathBuf::from(envelope.result["quote"]["order_file"].as_str().unwrap());
+ let order_file = PathBuf::from(envelope.result["quote"]["trade_file"].as_str().unwrap());
assert!(order_file.exists());
let draft = std::fs::read_to_string(order_file).expect("read order draft");
assert!(draft.contains("[buyer_actor]"));
@@ -1584,12 +1584,12 @@ mod tests {
assert_eq!(envelope.operation_id, "basket.quote.create");
assert_eq!(envelope.dry_run, true);
assert_eq!(envelope.result["state"], "dry_run");
- assert_eq!(envelope.result["order"]["state"], "dry_run");
+ assert_eq!(envelope.result["trade"]["state"], "dry_run");
assert_eq!(
- envelope.result["order"]["buyer_actor_source"],
+ envelope.result["trade"]["buyer_actor_source"],
"resolved_account"
);
- assert!(!PathBuf::from(envelope.result["order"]["file"].as_str().unwrap()).exists());
+ assert!(!PathBuf::from(envelope.result["trade"]["file"].as_str().unwrap()).exists());
}
#[test]
@@ -1848,6 +1848,7 @@ mod tests {
fn sample_config(root: &Path) -> RuntimeConfig {
let data = root.join("data");
+ let cache = root.join("cache");
let logs = root.join("logs");
let secrets = root.join("secrets");
RuntimeConfig {
@@ -1879,6 +1880,7 @@ mod tests {
app_config_path: root.join("config/apps/cli/config.toml"),
workspace_config_path: None,
app_data_root: data.join("apps/cli"),
+ shared_cache_root: cache.clone(),
app_logs_root: logs.join("apps/cli"),
shared_accounts_data_root: data.join("shared/accounts"),
shared_accounts_secrets_root: secrets.join("shared/accounts"),
diff --git a/src/ops/exec/core.rs b/src/ops/exec/core.rs
@@ -87,6 +87,7 @@ impl OperationService<WorkspaceGetRequest> for CoreOperationService<'_> {
"workspace_config_path": self.config.paths.workspace_config_path.as_ref().map(|path| path.display().to_string()),
"app_config_path": self.config.paths.app_config_path.display().to_string(),
"app_data_root": self.config.paths.app_data_root.display().to_string(),
+ "shared_cache_root": self.config.paths.shared_cache_root.display().to_string(),
"app_logs_root": self.config.paths.app_logs_root.display().to_string(),
"local_root": self.config.local.root.display().to_string(),
"replica_db_path": self.config.local.replica_db_path.display().to_string(),
@@ -213,6 +214,7 @@ impl OperationService<ConfigGetRequest> for CoreOperationService<'_> {
"app_config_path": self.config.paths.app_config_path.display().to_string(),
"workspace_config_path": self.config.paths.workspace_config_path.as_ref().map(|path| path.display().to_string()),
"app_data_root": self.config.paths.app_data_root.display().to_string(),
+ "shared_cache_root": self.config.paths.shared_cache_root.display().to_string(),
"app_logs_root": self.config.paths.app_logs_root.display().to_string(),
},
"account": {
@@ -1311,6 +1313,7 @@ mod tests {
fn sample_config(root: &Path) -> RuntimeConfig {
let data = root.join("data");
+ let cache = root.join("cache");
let logs = root.join("logs");
let secrets = root.join("secrets");
RuntimeConfig {
@@ -1342,6 +1345,7 @@ mod tests {
app_config_path: root.join("config/apps/cli/config.toml"),
workspace_config_path: None,
app_data_root: data.join("apps/cli"),
+ shared_cache_root: cache.clone(),
app_logs_root: logs.join("apps/cli"),
shared_accounts_data_root: data.join("shared/accounts"),
shared_accounts_secrets_root: secrets.join("shared/accounts"),
diff --git a/src/ops/exec/farm.rs b/src/ops/exec/farm.rs
@@ -2,12 +2,13 @@ use serde::Serialize;
use serde_json::Value;
use crate::cli::global::{
- FarmCreateArgs, FarmFieldArg, FarmPublishArgs, FarmRebindArgs, FarmScopeArg, FarmScopedArgs,
- FarmUpdateArgs,
+ FarmCreateArgs, FarmFieldArg, FarmPrivateLocationKeyArgs, FarmPrivateLocationSetArgs,
+ FarmPublishArgs, FarmRebindArgs, FarmScopeArg, FarmScopedArgs, FarmUpdateArgs,
};
use crate::ops::{
FarmCreateRequest, FarmCreateResult, FarmFulfillmentUpdateRequest, FarmFulfillmentUpdateResult,
- FarmGetRequest, FarmGetResult, FarmLocationUpdateRequest, FarmLocationUpdateResult,
+ FarmGetRequest, FarmGetResult, FarmLocationClearRequest, FarmLocationClearResult,
+ FarmLocationGetRequest, FarmLocationGetResult, FarmLocationSetRequest, FarmLocationSetResult,
FarmProfileUpdateRequest, FarmProfileUpdateResult, FarmPublishRequest, FarmPublishResult,
FarmReadinessCheckRequest, FarmReadinessCheckResult, FarmRebindRequest, FarmRebindResult,
OperationAdapterError, OperationRequest, OperationRequestData, OperationRequestPayload,
@@ -122,14 +123,72 @@ impl OperationService<FarmProfileUpdateRequest> for FarmOperationService<'_> {
}
}
-impl OperationService<FarmLocationUpdateRequest> for FarmOperationService<'_> {
- type Result = FarmLocationUpdateResult;
+impl OperationService<FarmLocationSetRequest> for FarmOperationService<'_> {
+ type Result = FarmLocationSetResult;
fn execute(
&self,
- request: OperationRequest<FarmLocationUpdateRequest>,
+ request: OperationRequest<FarmLocationSetRequest>,
) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- farm_set::<FarmLocationUpdateResult>(&request, self.config, location_field(&request)?)
+ let lookup = string_input(&request, "lookup").unwrap_or_else(|| "geonames".to_owned());
+ if lookup != "geonames" {
+ return Err(invalid_input(
+ request.operation_id(),
+ format!("farm location lookup `{lookup}` is not supported"),
+ ));
+ }
+ let args = FarmPrivateLocationSetArgs {
+ farm_d_tag: string_input(&request, "farm_d_tag"),
+ latitude: required_f64(&request, "latitude")?,
+ longitude: required_f64(&request, "longitude")?,
+ lookup,
+ };
+ let view =
+ crate::runtime::farm::private_location_set(self.config, &args).map_err(|error| {
+ OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
+ })?;
+ serialized_operation_result::<FarmLocationSetResult, _>(&view)
+ }
+}
+
+impl OperationService<FarmLocationGetRequest> for FarmOperationService<'_> {
+ type Result = FarmLocationGetResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<FarmLocationGetRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let args = FarmPrivateLocationKeyArgs {
+ farm_d_tag: string_input(&request, "farm_d_tag"),
+ };
+ let view =
+ crate::runtime::farm::private_location_get(self.config, &args).map_err(|error| {
+ OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
+ })?;
+ serialized_operation_result::<FarmLocationGetResult, _>(&view)
+ }
+}
+
+impl OperationService<FarmLocationClearRequest> for FarmOperationService<'_> {
+ type Result = FarmLocationClearResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<FarmLocationClearRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ if request.context.requires_approval_token() {
+ return Err(OperationAdapterError::approval_required(
+ request.operation_id(),
+ ));
+ }
+ let args = FarmPrivateLocationKeyArgs {
+ farm_d_tag: string_input(&request, "farm_d_tag"),
+ };
+ let view =
+ crate::runtime::farm::private_location_clear(self.config, &args).map_err(|error| {
+ OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
+ })?;
+ serialized_operation_result::<FarmLocationClearResult, _>(&view)
}
}
@@ -363,6 +422,24 @@ where
.map(str::to_owned)
}
+fn required_f64<P>(request: &OperationRequest<P>, key: &str) -> Result<f64, OperationAdapterError>
+where
+ P: OperationRequestPayload + OperationRequestData,
+{
+ request
+ .payload
+ .input()
+ .get(key)
+ .and_then(Value::as_f64)
+ .filter(|value| value.is_finite())
+ .ok_or_else(|| {
+ invalid_input(
+ request.operation_id(),
+ format!("missing required finite `{key}` input"),
+ )
+ })
+}
+
fn bool_input<P>(request: &OperationRequest<P>, key: &str) -> Option<bool>
where
P: OperationRequestPayload + OperationRequestData,
@@ -485,6 +562,7 @@ mod tests {
fn sample_config(root: &Path) -> RuntimeConfig {
let data = root.join("data");
+ let cache = root.join("cache");
let logs = root.join("logs");
let secrets = root.join("secrets");
RuntimeConfig {
@@ -516,6 +594,7 @@ mod tests {
app_config_path: root.join("config/apps/cli/config.toml"),
workspace_config_path: None,
app_data_root: data.join("apps/cli"),
+ shared_cache_root: cache.clone(),
app_logs_root: logs.join("apps/cli"),
shared_accounts_data_root: data.join("shared/accounts"),
shared_accounts_secrets_root: secrets.join("shared/accounts"),
diff --git a/src/ops/exec/listing.rs b/src/ops/exec/listing.rs
@@ -549,6 +549,7 @@ mod tests {
fn sample_config(root: &Path) -> RuntimeConfig {
let data = root.join("data");
+ let cache = root.join("cache");
let logs = root.join("logs");
let secrets = root.join("secrets");
RuntimeConfig {
@@ -580,6 +581,7 @@ mod tests {
app_config_path: root.join("config/apps/cli/config.toml"),
workspace_config_path: None,
app_data_root: data.join("apps/cli"),
+ shared_cache_root: cache.clone(),
app_logs_root: logs.join("apps/cli"),
shared_accounts_data_root: data.join("shared/accounts"),
shared_accounts_secrets_root: secrets.join("shared/accounts"),
diff --git a/src/ops/exec/market.rs b/src/ops/exec/market.rs
@@ -104,7 +104,7 @@ fn market_product_search_view(mut view: FindView) -> FindView {
"radroots market listing get {}",
result.product_key
)];
- if result.readiness.order_request_enabled {
+ if result.readiness.checkout_enabled {
actions.push("radroots basket create".to_owned());
}
actions
@@ -126,7 +126,7 @@ fn market_product_search_view(mut view: FindView) -> FindView {
fn market_listing_get_view(mut view: ListingGetView) -> ListingGetView {
view.actions = match view.state.as_str() {
"ready" => {
- if view.readiness.order_request_enabled {
+ if view.readiness.checkout_enabled {
vec!["radroots basket create".to_owned()]
} else {
Vec::new()
@@ -495,7 +495,7 @@ mod tests {
}
#[test]
- fn market_ready_actions_require_order_request_enabled() {
+ fn market_ready_actions_require_checkout_enabled() {
let disabled_search = market_product_search_view(FindView {
state: "ready".to_owned(),
source: "test".to_owned(),
@@ -592,7 +592,7 @@ mod tests {
MarketReadinessView {
protocol_valid: true,
marketplace_eligible: true,
- order_request_enabled: true,
+ checkout_enabled: true,
primary_bin_verified: true,
reason_codes: Vec::new(),
}
@@ -602,10 +602,10 @@ mod tests {
MarketReadinessView {
protocol_valid: true,
marketplace_eligible: true,
- order_request_enabled: false,
+ checkout_enabled: false,
primary_bin_verified: true,
reason_codes: vec![
- "listing_order_request_disabled".to_owned(),
+ "listing_checkout_disabled".to_owned(),
"listing_inventory_unavailable".to_owned(),
],
}
@@ -613,6 +613,7 @@ mod tests {
fn sample_config(root: &Path) -> RuntimeConfig {
let data = root.join("data");
+ let cache = root.join("cache");
let logs = root.join("logs");
let secrets = root.join("secrets");
RuntimeConfig {
@@ -644,6 +645,7 @@ mod tests {
app_config_path: root.join("config/apps/cli/config.toml"),
workspace_config_path: None,
app_data_root: data.join("apps/cli"),
+ shared_cache_root: cache.clone(),
app_logs_root: logs.join("apps/cli"),
shared_accounts_data_root: data.join("shared/accounts"),
shared_accounts_secrets_root: secrets.join("shared/accounts"),
diff --git a/src/ops/exec/mod.rs b/src/ops/exec/mod.rs
@@ -3,8 +3,8 @@ pub mod core;
pub mod farm;
pub mod listing;
pub mod market;
-pub mod order;
pub mod runtime;
+pub mod trade;
pub mod validation;
pub use basket::BasketOperationService;
@@ -12,6 +12,6 @@ pub use core::CoreOperationService;
pub use farm::FarmOperationService;
pub use listing::ListingOperationService;
pub use market::MarketOperationService;
-pub use order::OrderOperationService;
pub use runtime::RuntimeOperationService;
+pub use trade::TradeOperationService;
pub use validation::ValidationOperationService;
diff --git a/src/ops/exec/order.rs b/src/ops/exec/order.rs
@@ -1,1890 +0,0 @@
-use std::path::PathBuf;
-
-use serde::Serialize;
-use serde_json::{Value, json};
-
-use crate::cli::global::{
- OrderAppRecordExportArgs, OrderCancelArgs, OrderDecisionArg, OrderDecisionArgs,
- OrderRebindArgs, OrderRevisionDecisionArg, OrderRevisionDecisionArgs, OrderRevisionProposeArgs,
- OrderStatusArgs, OrderSubmitArgs, RecordLookupArgs,
-};
-use crate::ops::{
- OperationAdapterError, OperationRequest, OperationRequestData, OperationRequestPayload,
- OperationResult, OperationResultData, OperationService, OrderAcceptRequest, OrderAcceptResult,
- OrderAppExportRequest, OrderAppExportResult, OrderAppListRequest, OrderAppListResult,
- OrderCancelRequest, OrderCancelResult, OrderDeclineRequest, OrderDeclineResult,
- OrderEventListRequest, OrderEventListResult, OrderEventWatchRequest, OrderEventWatchResult,
- OrderGetRequest, OrderGetResult, OrderListRequest, OrderListResult, OrderRebindRequest,
- OrderRebindResult, OrderRevisionAcceptRequest, OrderRevisionAcceptResult,
- OrderRevisionDeclineRequest, OrderRevisionDeclineResult, OrderRevisionProposeRequest,
- OrderRevisionProposeResult, OrderStatusGetRequest, OrderStatusGetResult, OrderSubmitRequest,
- OrderSubmitResult,
-};
-use crate::runtime::RuntimeError;
-use crate::runtime::config::RuntimeConfig;
-use crate::view::runtime::{
- CommandDisposition, OrderAppRecordExportView, OrderCancellationView, OrderDecisionView,
- OrderRebindView, OrderRevisionDecisionView, OrderRevisionProposalView, OrderStatusView,
- OrderSubmitView,
-};
-
-const ORDER_EVENT_WATCH_DEFERRED_REASON: &str = "relay-backed order event watch is not implemented";
-
-pub struct OrderOperationService<'a> {
- config: &'a RuntimeConfig,
-}
-
-impl<'a> OrderOperationService<'a> {
- pub fn new(config: &'a RuntimeConfig) -> Self {
- Self { config }
- }
-}
-
-impl OperationService<OrderSubmitRequest> for OrderOperationService<'_> {
- type Result = OrderSubmitResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderSubmitRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let key = required_order_key(&request)?;
- let args = OrderSubmitArgs {
- key,
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- };
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::submit(&config, &args).map_err(|error| {
- OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
- })?;
- submit_result::<OrderSubmitResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderGetRequest> for OrderOperationService<'_> {
- type Result = OrderGetResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderGetRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let args = RecordLookupArgs {
- key: required_order_key(&request)?,
- };
- let view = map_runtime(crate::runtime::order::get(self.config, &args))?;
- serialized_target_result::<OrderGetResult, _>(&view)
- }
-}
-
-impl OperationService<OrderListRequest> for OrderOperationService<'_> {
- type Result = OrderListResult;
-
- fn execute(
- &self,
- _request: OperationRequest<OrderListRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let view = map_runtime(crate::runtime::order::list(self.config))?;
- serialized_target_result::<OrderListResult, _>(&view)
- }
-}
-
-impl OperationService<OrderAppListRequest> for OrderOperationService<'_> {
- type Result = OrderAppListResult;
-
- fn execute(
- &self,
- _request: OperationRequest<OrderAppListRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let view = map_runtime(crate::runtime::order::app_record_list(self.config))?;
- serialized_target_result::<OrderAppListResult, _>(&view)
- }
-}
-
-impl OperationService<OrderAppExportRequest> for OrderOperationService<'_> {
- type Result = OrderAppExportResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderAppExportRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let args = OrderAppRecordExportArgs {
- record_id: required_string_input(&request, "record_id")?,
- output: optional_path_input(&request, "output"),
- };
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::app_record_export(&config, &args).map_err(|error| {
- OperationAdapterError::runtime_failure(request.operation_id(), error)
- })?;
- order_app_record_export_result::<OrderAppExportResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderRebindRequest> for OrderOperationService<'_> {
- type Result = OrderRebindResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderRebindRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let args = OrderRebindArgs {
- key: required_order_key(&request)?,
- selector: required_string_input(&request, "selector")?,
- };
- if request.context.dry_run {
- let view =
- crate::runtime::order::rebind_preflight(self.config, &args).map_err(|error| {
- OperationAdapterError::runtime_failure(request.operation_id(), error)
- })?;
- return order_rebind_result::<OrderRebindResult>(request.operation_id(), &view);
- }
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let view = crate::runtime::order::rebind(self.config, &args).map_err(|error| {
- OperationAdapterError::runtime_failure(request.operation_id(), error)
- })?;
- order_rebind_result::<OrderRebindResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderAcceptRequest> for OrderOperationService<'_> {
- type Result = OrderAcceptResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderAcceptRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let args = OrderDecisionArgs {
- key: required_order_key(&request)?,
- decision: OrderDecisionArg::Accept,
- reason: None,
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- };
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::decide(&config, &args).map_err(|error| {
- OperationAdapterError::runtime_failure(request.operation_id(), error)
- })?;
- decision_result::<OrderAcceptResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderDeclineRequest> for OrderOperationService<'_> {
- type Result = OrderDeclineResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderDeclineRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let reason = string_input(&request, "reason")
- .map(|reason| reason.trim().to_owned())
- .filter(|reason| !reason.is_empty())
- .ok_or_else(|| {
- invalid_input(
- request.operation_id(),
- "missing required `reason` input".to_owned(),
- )
- })?;
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let args = OrderDecisionArgs {
- key: required_order_key(&request)?,
- decision: OrderDecisionArg::Decline,
- reason: Some(reason),
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- };
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::decide(&config, &args).map_err(|error| {
- OperationAdapterError::runtime_failure(request.operation_id(), error)
- })?;
- decision_result::<OrderDeclineResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderCancelRequest> for OrderOperationService<'_> {
- type Result = OrderCancelResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderCancelRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let reason = string_input(&request, "reason")
- .map(|reason| reason.trim().to_owned())
- .filter(|reason| !reason.is_empty())
- .ok_or_else(|| {
- invalid_input(
- request.operation_id(),
- "missing required `reason` input".to_owned(),
- )
- })?;
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let args = OrderCancelArgs {
- key: required_order_key(&request)?,
- reason,
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- };
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::cancel(&config, &args).map_err(|error| {
- OperationAdapterError::runtime_failure(request.operation_id(), error)
- })?;
- cancellation_result::<OrderCancelResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderRevisionProposeRequest> for OrderOperationService<'_> {
- type Result = OrderRevisionProposeResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderRevisionProposeRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let reason = string_input(&request, "reason")
- .map(|reason| reason.trim().to_owned())
- .filter(|reason| !reason.is_empty())
- .ok_or_else(|| {
- invalid_input(
- request.operation_id(),
- "missing required `reason` input".to_owned(),
- )
- })?;
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let args = OrderRevisionProposeArgs {
- key: required_order_key(&request)?,
- reason,
- bin_id: string_input(&request, "bin_id"),
- bin_count: u32_input(&request, "bin_count"),
- adjustment_id: string_input(&request, "adjustment_id"),
- adjustment_effect: string_input(&request, "adjustment_effect"),
- adjustment_amount: string_input(&request, "adjustment_amount"),
- adjustment_currency: string_input(&request, "adjustment_currency"),
- adjustment_reason: string_input(&request, "adjustment_reason"),
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- };
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::revision_propose(&config, &args).map_err(|error| {
- OperationAdapterError::runtime_failure(request.operation_id(), error)
- })?;
- revision_proposal_result::<OrderRevisionProposeResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderRevisionAcceptRequest> for OrderOperationService<'_> {
- type Result = OrderRevisionAcceptResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderRevisionAcceptRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let args = OrderRevisionDecisionArgs {
- key: required_order_key(&request)?,
- revision_id: required_string_input(&request, "revision_id")?,
- decision: OrderRevisionDecisionArg::Accept,
- reason: None,
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- };
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::revision_decide(&config, &args).map_err(|error| {
- OperationAdapterError::runtime_failure(request.operation_id(), error)
- })?;
- revision_decision_result::<OrderRevisionAcceptResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderRevisionDeclineRequest> for OrderOperationService<'_> {
- type Result = OrderRevisionDeclineResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderRevisionDeclineRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let reason = string_input(&request, "reason")
- .map(|reason| reason.trim().to_owned())
- .filter(|reason| !reason.is_empty())
- .ok_or_else(|| {
- invalid_input(
- request.operation_id(),
- "missing required `reason` input".to_owned(),
- )
- })?;
- let args = OrderRevisionDecisionArgs {
- key: required_order_key(&request)?,
- revision_id: required_string_input(&request, "revision_id")?,
- decision: OrderRevisionDecisionArg::Decline,
- reason: Some(reason),
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- };
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::revision_decide(&config, &args).map_err(|error| {
- OperationAdapterError::runtime_failure(request.operation_id(), error)
- })?;
- revision_decision_result::<OrderRevisionDeclineResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderStatusGetRequest> for OrderOperationService<'_> {
- type Result = OrderStatusGetResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderStatusGetRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let args = OrderStatusArgs {
- key: required_order_key(&request)?,
- };
- let view = crate::runtime::order::status(self.config, &args).map_err(|error| {
- OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
- })?;
- status_result::<OrderStatusGetResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderEventListRequest> for OrderOperationService<'_> {
- type Result = OrderEventListResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderEventListRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let order_id = string_input(&request, "order_id");
- let view = crate::runtime::order::event_list(self.config, order_id.as_deref()).map_err(
- |error| OperationAdapterError::runtime_failure(request.operation_id(), error),
- )?;
- event_list_result::<OrderEventListResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<OrderEventWatchRequest> for OrderOperationService<'_> {
- type Result = OrderEventWatchResult;
-
- fn execute(
- &self,
- request: OperationRequest<OrderEventWatchRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let order_id = required_order_key(&request)?;
- let action = format!("radroots order status get {order_id}");
- Err(OperationAdapterError::not_implemented_with_detail(
- request.operation_id(),
- ORDER_EVENT_WATCH_DEFERRED_REASON.to_owned(),
- json!({
- "state": "not_implemented",
- "order_id": order_id,
- "reason": ORDER_EVENT_WATCH_DEFERRED_REASON,
- "actions": [action],
- }),
- ))
- }
-}
-
-fn decision_result<R>(
- operation_id: &str,
- view: &OrderDecisionView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- CommandDisposition::ValidationFailed => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!(
- "order decision failed validation with state `{}`",
- view.state
- )
- });
- Err(OperationAdapterError::validation_failed_with_detail(
- operation_id,
- message,
- order_decision_error_detail(view),
- ))
- }
- disposition => {
- let message = view
- .reason
- .clone()
- .unwrap_or_else(|| format!("order decision finished with state `{}`", view.state));
- if disposition == CommandDisposition::ExternalUnavailable {
- let detail = order_decision_error_detail(view);
- if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
- Err(OperationAdapterError::network_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- } else {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- } else if disposition == CommandDisposition::Unconfigured {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- order_decision_error_detail(view),
- ))
- } else if disposition == CommandDisposition::NotFound {
- Err(OperationAdapterError::not_found_with_detail(
- operation_id,
- message,
- order_decision_error_detail(view),
- ))
- } else {
- Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- message,
- ))
- }
- }
- }
-}
-
-fn order_decision_error_detail(view: &OrderDecisionView) -> Value {
- json!({
- "state": &view.state,
- "order_id": &view.order_id,
- "listing_addr": &view.listing_addr,
- "listing_event_id": &view.listing_event_id,
- "request_event_id": &view.request_event_id,
- "root_event_id": &view.root_event_id,
- "prev_event_id": &view.prev_event_id,
- "event_id": &view.event_id,
- "event_kind": view.event_kind,
- "inventory": &view.inventory,
- "buyer_pubkey": &view.buyer_pubkey,
- "seller_pubkey": &view.seller_pubkey,
- "decision": &view.decision,
- "dry_run": view.dry_run,
- "target_relays": &view.target_relays,
- "connected_relays": &view.connected_relays,
- "acknowledged_relays": &view.acknowledged_relays,
- "failed_relays": &view.failed_relays,
- "fetched_count": view.fetched_count,
- "decoded_count": view.decoded_count,
- "skipped_count": view.skipped_count,
- "idempotency_key": &view.idempotency_key,
- "signer_mode": &view.signer_mode,
- "issues": &view.issues,
- "actions": &view.actions,
- })
-}
-
-fn cancellation_result<R>(
- operation_id: &str,
- view: &OrderCancellationView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- CommandDisposition::ValidationFailed => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!("order cancel failed validation with state `{}`", view.state)
- });
- Err(OperationAdapterError::validation_failed_with_detail(
- operation_id,
- message,
- order_cancellation_error_detail(view),
- ))
- }
- disposition => {
- let message = view
- .reason
- .clone()
- .unwrap_or_else(|| format!("order cancel finished with state `{}`", view.state));
- if disposition == CommandDisposition::ExternalUnavailable {
- let detail = order_cancellation_error_detail(view);
- if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
- Err(OperationAdapterError::network_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- } else {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- } else if disposition == CommandDisposition::Unconfigured {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- order_cancellation_error_detail(view),
- ))
- } else {
- Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- message,
- ))
- }
- }
- }
-}
-
-fn order_cancellation_error_detail(view: &OrderCancellationView) -> Value {
- json!({
- "state": &view.state,
- "order_id": &view.order_id,
- "listing_addr": &view.listing_addr,
- "request_event_id": &view.request_event_id,
- "decision_event_id": &view.decision_event_id,
- "root_event_id": &view.root_event_id,
- "prev_event_id": &view.prev_event_id,
- "event_id": &view.event_id,
- "event_kind": view.event_kind,
- "buyer_pubkey": &view.buyer_pubkey,
- "seller_pubkey": &view.seller_pubkey,
- "cancellation_reason": &view.cancellation_reason,
- "dry_run": view.dry_run,
- "target_relays": &view.target_relays,
- "connected_relays": &view.connected_relays,
- "acknowledged_relays": &view.acknowledged_relays,
- "failed_relays": &view.failed_relays,
- "fetched_count": view.fetched_count,
- "decoded_count": view.decoded_count,
- "skipped_count": view.skipped_count,
- "idempotency_key": &view.idempotency_key,
- "signer_mode": &view.signer_mode,
- "issues": &view.issues,
- "actions": &view.actions,
- })
-}
-
-fn revision_proposal_result<R>(
- operation_id: &str,
- view: &OrderRevisionProposalView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- CommandDisposition::ValidationFailed => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!(
- "order revision propose failed validation with state `{}`",
- view.state
- )
- });
- Err(OperationAdapterError::validation_failed_with_detail(
- operation_id,
- message,
- order_revision_proposal_error_detail(view),
- ))
- }
- disposition => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!(
- "order revision propose finished with state `{}`",
- view.state
- )
- });
- if disposition == CommandDisposition::ExternalUnavailable {
- let detail = order_revision_proposal_error_detail(view);
- if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
- Err(OperationAdapterError::network_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- } else {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- } else if disposition == CommandDisposition::Unconfigured {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- order_revision_proposal_error_detail(view),
- ))
- } else {
- Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- message,
- ))
- }
- }
- }
-}
-
-fn order_revision_proposal_error_detail(view: &OrderRevisionProposalView) -> Value {
- json!({
- "state": &view.state,
- "order_id": &view.order_id,
- "revision_id": &view.revision_id,
- "listing_addr": &view.listing_addr,
- "request_event_id": &view.request_event_id,
- "decision_event_id": &view.decision_event_id,
- "root_event_id": &view.root_event_id,
- "prev_event_id": &view.prev_event_id,
- "event_id": &view.event_id,
- "event_kind": view.event_kind,
- "items": &view.items,
- "economics": &view.economics,
- "inventory": &view.inventory,
- "buyer_pubkey": &view.buyer_pubkey,
- "seller_pubkey": &view.seller_pubkey,
- "dry_run": view.dry_run,
- "target_relays": &view.target_relays,
- "connected_relays": &view.connected_relays,
- "acknowledged_relays": &view.acknowledged_relays,
- "failed_relays": &view.failed_relays,
- "fetched_count": view.fetched_count,
- "decoded_count": view.decoded_count,
- "skipped_count": view.skipped_count,
- "idempotency_key": &view.idempotency_key,
- "signer_mode": &view.signer_mode,
- "issues": &view.issues,
- "actions": &view.actions,
- })
-}
-
-fn revision_decision_result<R>(
- operation_id: &str,
- view: &OrderRevisionDecisionView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- CommandDisposition::ValidationFailed => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!(
- "order revision {} failed validation with state `{}`",
- view.decision.as_deref().unwrap_or("decision"),
- view.state
- )
- });
- Err(OperationAdapterError::validation_failed_with_detail(
- operation_id,
- message,
- order_revision_decision_error_detail(view),
- ))
- }
- disposition => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!(
- "order revision {} finished with state `{}`",
- view.decision.as_deref().unwrap_or("decision"),
- view.state
- )
- });
- if disposition == CommandDisposition::ExternalUnavailable {
- let detail = order_revision_decision_error_detail(view);
- if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
- Err(OperationAdapterError::network_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- } else {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- } else if disposition == CommandDisposition::Unconfigured {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- order_revision_decision_error_detail(view),
- ))
- } else {
- Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- message,
- ))
- }
- }
- }
-}
-
-fn order_revision_decision_error_detail(view: &OrderRevisionDecisionView) -> Value {
- json!({
- "state": &view.state,
- "order_id": &view.order_id,
- "revision_id": &view.revision_id,
- "decision": &view.decision,
- "listing_addr": &view.listing_addr,
- "request_event_id": &view.request_event_id,
- "decision_event_id": &view.decision_event_id,
- "agreement_event_id": &view.agreement_event_id,
- "root_event_id": &view.root_event_id,
- "prev_event_id": &view.prev_event_id,
- "event_id": &view.event_id,
- "event_kind": view.event_kind,
- "economics": &view.economics,
- "inventory": &view.inventory,
- "buyer_pubkey": &view.buyer_pubkey,
- "seller_pubkey": &view.seller_pubkey,
- "dry_run": view.dry_run,
- "target_relays": &view.target_relays,
- "connected_relays": &view.connected_relays,
- "acknowledged_relays": &view.acknowledged_relays,
- "failed_relays": &view.failed_relays,
- "fetched_count": view.fetched_count,
- "decoded_count": view.decoded_count,
- "skipped_count": view.skipped_count,
- "idempotency_key": &view.idempotency_key,
- "signer_mode": &view.signer_mode,
- "issues": &view.issues,
- "actions": &view.actions,
- })
-}
-
-fn status_result<R>(
- operation_id: &str,
- view: &OrderStatusView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- disposition => {
- let message = view
- .reason
- .clone()
- .unwrap_or_else(|| format!("order status finished with state `{}`", view.state));
- if disposition == CommandDisposition::ExternalUnavailable {
- let detail = order_status_error_detail(view);
- if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
- Err(OperationAdapterError::network_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- } else {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- } else if disposition == CommandDisposition::Unconfigured {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- order_status_error_detail(view),
- ))
- } else {
- Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- message,
- ))
- }
- }
- }
-}
-
-fn order_status_error_detail(view: &OrderStatusView) -> Value {
- json!({
- "state": &view.state,
- "order_id": &view.order_id,
- "request_event_id": &view.request_event_id,
- "decision_event_id": &view.decision_event_id,
- "agreement_event_id": &view.agreement_event_id,
- "listing_event_id": &view.listing_event_id,
- "listing_addr": &view.listing_addr,
- "buyer_pubkey": &view.buyer_pubkey,
- "seller_pubkey": &view.seller_pubkey,
- "last_event_id": &view.last_event_id,
- "revision": &view.revision,
- "inventory": &view.inventory,
- "lifecycle": &view.lifecycle,
- "reducer_issues": &view.reducer_issues,
- "target_relays": &view.target_relays,
- "connected_relays": &view.connected_relays,
- "failed_relays": &view.failed_relays,
- "fetched_count": view.fetched_count,
- "decoded_count": view.decoded_count,
- "skipped_count": view.skipped_count,
- "actions": &view.actions,
- })
-}
-
-fn serialized_target_result<R, T>(value: &T) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
- T: Serialize,
-{
- OperationResult::new(R::from_serializable(value)?)
-}
-
-fn submit_result<R>(
- operation_id: &str,
- view: &OrderSubmitView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- disposition => {
- let message = view
- .reason
- .clone()
- .unwrap_or_else(|| format!("order submit finished with state `{}`", view.state));
- let detail = order_submit_error_detail(view);
- match disposition {
- CommandDisposition::NotFound => Err(OperationAdapterError::not_found_with_detail(
- operation_id,
- message,
- detail,
- )),
- CommandDisposition::ValidationFailed => {
- Err(OperationAdapterError::validation_failed_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- CommandDisposition::Unconfigured => {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- CommandDisposition::ExternalUnavailable => {
- if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
- Err(OperationAdapterError::network_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- } else {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- }
- _ => Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- message,
- )),
- }
- }
- }
-}
-
-fn order_app_record_export_result<R>(
- operation_id: &str,
- view: &OrderAppRecordExportView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- CommandDisposition::NotFound => Err(OperationAdapterError::not_found_with_detail(
- operation_id,
- view.reason.clone().unwrap_or_else(|| {
- format!(
- "app-authored local order record `{}` was not found",
- view.record_id
- )
- }),
- serde_json::to_value(view).unwrap_or(Value::Null),
- )),
- CommandDisposition::ValidationFailed => {
- Err(OperationAdapterError::validation_failed_with_detail(
- operation_id,
- view.reason.clone().unwrap_or_else(|| {
- format!(
- "app-authored local order record `{}` cannot be exported",
- view.record_id
- )
- }),
- serde_json::to_value(view).unwrap_or(Value::Null),
- ))
- }
- disposition => Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- view.reason.clone().unwrap_or_else(|| {
- format!(
- "app-authored local order record export finished with state `{}`",
- view.state
- )
- }),
- )),
- }
-}
-
-fn order_rebind_result<R>(
- operation_id: &str,
- view: &OrderRebindView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- CommandDisposition::NotFound => Err(OperationAdapterError::not_found_with_detail(
- operation_id,
- view.reason
- .clone()
- .unwrap_or_else(|| format!("order draft `{}` was not found", view.lookup)),
- order_rebind_error_detail(view),
- )),
- CommandDisposition::ValidationFailed => {
- Err(OperationAdapterError::validation_failed_with_detail(
- operation_id,
- view.reason.clone().unwrap_or_else(|| {
- format!("order rebind finished with state `{}`", view.state)
- }),
- order_rebind_error_detail(view),
- ))
- }
- disposition => Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- view.reason
- .clone()
- .unwrap_or_else(|| format!("order rebind finished with state `{}`", view.state)),
- )),
- }
-}
-
-fn order_rebind_error_detail(view: &OrderRebindView) -> Value {
- json!({
- "state": &view.state,
- "source": &view.source,
- "lookup": &view.lookup,
- "file": &view.file,
- "dry_run": view.dry_run,
- "from_order_id": &view.from_order_id,
- "to_order_id": &view.to_order_id,
- "order_id_changed": view.order_id_changed,
- "from_buyer_account_id": &view.from_buyer_account_id,
- "from_buyer_pubkey": &view.from_buyer_pubkey,
- "from_buyer_actor_source": &view.from_buyer_actor_source,
- "to_buyer_account_id": &view.to_buyer_account_id,
- "to_buyer_pubkey": &view.to_buyer_pubkey,
- "to_buyer_actor_source": &view.to_buyer_actor_source,
- "buyer_pubkey_changed": view.buyer_pubkey_changed,
- "existing_request_check": &view.existing_request_check,
- "existing_request_event_ids": &view.existing_request_event_ids,
- "actions": &view.actions,
- })
-}
-
-fn order_submit_error_detail(view: &OrderSubmitView) -> Value {
- json!({
- "state": &view.state,
- "source": &view.source,
- "order_id": &view.order_id,
- "file": &view.file,
- "listing_lookup": &view.listing_lookup,
- "listing_addr": &view.listing_addr,
- "listing_event_id": &view.listing_event_id,
- "listing_relays": &view.listing_relays,
- "buyer_account_id": &view.buyer_account_id,
- "buyer_pubkey": &view.buyer_pubkey,
- "seller_pubkey": &view.seller_pubkey,
- "event_id": &view.event_id,
- "event_kind": view.event_kind,
- "dry_run": view.dry_run,
- "deduplicated": view.deduplicated,
- "target_relays": &view.target_relays,
- "connected_relays": &view.connected_relays,
- "acknowledged_relays": &view.acknowledged_relays,
- "failed_relays": &view.failed_relays,
- "idempotency_key": &view.idempotency_key,
- "signer_mode": &view.signer_mode,
- "issues": &view.issues,
- "actions": &view.actions,
- })
-}
-
-fn event_list_result<R>(
- operation_id: &str,
- view: &crate::view::runtime::OrderEventListView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- disposition => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!("order event list finished with state `{}`", view.state)
- });
- if disposition == CommandDisposition::ExternalUnavailable {
- let detail = order_event_list_error_detail(view);
- Err(OperationAdapterError::network_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- } else if disposition == CommandDisposition::Unconfigured {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- order_event_list_error_detail(view),
- ))
- } else {
- Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- message,
- ))
- }
- }
- }
-}
-
-fn order_event_list_error_detail(view: &crate::view::runtime::OrderEventListView) -> Value {
- json!({
- "state": &view.state,
- "seller_pubkey": &view.seller_pubkey,
- "target_relays": &view.target_relays,
- "connected_relays": &view.connected_relays,
- "failed_relays": &view.failed_relays,
- "fetched_count": view.fetched_count,
- "decoded_count": view.decoded_count,
- "skipped_count": view.skipped_count,
- "count": view.count,
- "actions": &view.actions,
- })
-}
-
-fn required_order_key<P>(request: &OperationRequest<P>) -> Result<String, OperationAdapterError>
-where
- P: OperationRequestPayload + OperationRequestData,
-{
- string_input(request, "order_id")
- .or_else(|| string_input(request, "key"))
- .ok_or_else(|| {
- invalid_input(
- request.operation_id(),
- "missing required `order_id` input".to_owned(),
- )
- })
-}
-
-fn required_string_input<P>(
- request: &OperationRequest<P>,
- key: &str,
-) -> Result<String, OperationAdapterError>
-where
- P: OperationRequestPayload + OperationRequestData,
-{
- string_input(request, key)
- .map(|value| value.trim().to_owned())
- .filter(|value| !value.is_empty())
- .ok_or_else(|| {
- invalid_input(
- request.operation_id(),
- format!("missing required `{key}` input"),
- )
- })
-}
-
-fn string_input<P>(request: &OperationRequest<P>, key: &str) -> Option<String>
-where
- P: OperationRequestPayload + OperationRequestData,
-{
- request
- .payload
- .input()
- .get(key)
- .and_then(Value::as_str)
- .map(str::to_owned)
-}
-
-fn optional_path_input<P>(request: &OperationRequest<P>, key: &str) -> Option<PathBuf>
-where
- P: OperationRequestPayload + OperationRequestData,
-{
- string_input(request, key).map(PathBuf::from)
-}
-
-fn bool_input<P>(request: &OperationRequest<P>, key: &str) -> Option<bool>
-where
- P: OperationRequestPayload + OperationRequestData,
-{
- request.payload.input().get(key).and_then(Value::as_bool)
-}
-
-fn u32_input<P>(request: &OperationRequest<P>, key: &str) -> Option<u32>
-where
- P: OperationRequestPayload + OperationRequestData,
-{
- request
- .payload
- .input()
- .get(key)
- .and_then(Value::as_u64)
- .and_then(|value| u32::try_from(value).ok())
-}
-
-fn map_runtime<T>(result: Result<T, RuntimeError>) -> Result<T, OperationAdapterError> {
- result.map_err(|error| OperationAdapterError::Runtime(error.to_string()))
-}
-
-fn invalid_input(operation_id: &str, message: String) -> OperationAdapterError {
- OperationAdapterError::InvalidInput {
- operation_id: operation_id.to_owned(),
- message,
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::path::{Path, PathBuf};
-
- use radroots_runtime_paths::RadrootsMigrationReport;
- use radroots_secret_vault::RadrootsSecretBackend;
- use serde_json::{Map, Value};
- use tempfile::tempdir;
-
- use super::{OrderOperationService, decision_result};
- use crate::ops::{
- OperationAdapter, OperationContext, OperationData, OperationRequest, OrderAcceptRequest,
- OrderAcceptResult, OrderCancelRequest, OrderDeclineRequest, OrderDeclineResult,
- OrderEventListRequest, OrderEventWatchRequest, OrderGetRequest, OrderListRequest,
- OrderRevisionAcceptRequest, OrderRevisionDeclineRequest, OrderRevisionProposeRequest,
- OrderStatusGetRequest, OrderSubmitRequest,
- };
- use crate::runtime::config::{
- AccountConfig, AccountSecretContractConfig, HyfConfig, IdentityConfig, InteractionConfig,
- LocalConfig, LoggingConfig, MigrationConfig, MycConfig, OutputConfig, OutputFormat,
- PathsConfig, PublishConfig, PublishTransport, PublishTransportSource, RelayConfig,
- RelayConfigSource, RelayPublishPolicy, RpcConfig, RuntimeConfig, SignerBackend,
- SignerConfig, Verbosity,
- };
- use crate::view::runtime::OrderDecisionView;
-
- #[test]
- fn order_service_get_and_list_preserve_order_truth() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let get = OperationRequest::new(
- OperationContext::default(),
- OrderGetRequest::from_data(data(&[("order_id", "ord_missing")])),
- )
- .expect("order get request");
- let get_envelope = service
- .execute(get)
- .expect("order get result")
- .to_envelope(OperationContext::default().envelope_context("req_order_get"))
- .expect("order get envelope");
-
- assert_eq!(get_envelope.operation_id, "order.get");
- assert_eq!(get_envelope.result["state"], "missing");
- assert_eq!(get_envelope.result["actions"][0], "radroots order list");
- assert_eq!(get_envelope.result["actions"][1], "radroots basket create");
-
- let list = OperationRequest::new(OperationContext::default(), OrderListRequest::default())
- .expect("order list request");
- let list_envelope = service
- .execute(list)
- .expect("order list result")
- .to_envelope(OperationContext::default().envelope_context("req_order_list"))
- .expect("order list envelope");
- assert_eq!(list_envelope.operation_id, "order.list");
- assert_eq!(list_envelope.result["state"], "empty");
- assert_eq!(list_envelope.result["actions"][0], "radroots basket create");
- }
-
- #[test]
- fn order_submit_requires_approval_token() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let submit = OperationRequest::new(
- OperationContext::default(),
- OrderSubmitRequest::from_data(data(&[("order_id", "ord_missing")])),
- )
- .expect("order submit request");
- let error = service.execute(submit).expect_err("approval required");
-
- assert!(format!("{error}").contains("approval_token"));
- assert_eq!(error.to_output_error().code, "approval_required");
- assert_eq!(error.to_output_error().exit_code, 6);
- }
-
- #[test]
- fn order_submit_with_approval_returns_not_found_for_missing_order() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let mut context = OperationContext::default();
- context.approval_token = Some("approve_test".to_owned());
- let submit = OperationRequest::new(
- context.clone(),
- OrderSubmitRequest::from_data(data(&[("order_id", "ord_missing")])),
- )
- .expect("order submit request");
- let error = service.execute(submit).expect_err("missing order error");
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "not_found");
- assert_eq!(output_error.exit_code, 4);
- assert!(output_error.message.contains("ord_missing"));
- let envelope = crate::out::envelope::OutputEnvelope::failure(
- "order.submit",
- output_error,
- context.envelope_context("req_order_submit"),
- );
- let detail = envelope.errors[0].detail.as_ref().expect("submit detail");
- assert_eq!(detail["state"], "missing");
- assert_eq!(detail["order_id"], "ord_missing");
- assert_eq!(detail["actions"][0], "radroots order list");
- assert_eq!(detail["actions"][1], "radroots basket create");
- assert_eq!(
- envelope.next_actions[0].command.as_deref(),
- Some("radroots order list")
- );
- assert_eq!(
- envelope.next_actions[1].command.as_deref(),
- Some("radroots basket create")
- );
- }
-
- #[test]
- fn order_accept_requires_approval_token() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let accept = OperationRequest::new(
- OperationContext::default(),
- OrderAcceptRequest::from_data(data(&[("order_id", "ord_pending")])),
- )
- .expect("order accept request");
- let error = service.execute(accept).expect_err("approval required");
-
- assert_eq!(error.to_output_error().code, "approval_required");
- }
-
- #[test]
- fn order_accept_unconfigured_preserves_decision_detail() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let mut context = OperationContext::default();
- context.dry_run = true;
- let accept = OperationRequest::new(
- context,
- OrderAcceptRequest::from_data(data(&[("order_id", "ord_pending")])),
- )
- .expect("order accept request");
- let error = service
- .execute(accept)
- .expect_err("order accept unconfigured");
- let output_error = error.to_output_error();
- let detail = output_error.detail.as_ref().expect("decision detail");
-
- assert_eq!(output_error.code, "operation_unavailable");
- assert_eq!(detail["state"], "unconfigured");
- assert_eq!(detail["order_id"], "ord_pending");
- assert_eq!(detail["decision"], "accepted");
- assert!(detail["target_relays"].as_array().unwrap().is_empty());
- }
-
- #[test]
- fn order_decision_already_decided_maps_to_validation_failure() {
- let view = already_decided_view();
- let error = match decision_result::<OrderAcceptResult>("order.accept", &view) {
- Ok(_) => panic!("already decided view should fail validation"),
- Err(error) => error,
- };
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "validation_failed");
- assert_eq!(output_error.exit_code, 10);
- let detail = output_error.detail.expect("validation detail");
- assert_eq!(detail["state"], "already_decided");
- assert_eq!(detail["operation_id"], "order.accept");
- assert_eq!(detail["listing_event_id"], "l".repeat(64));
- assert_eq!(detail["event_id"], "d".repeat(64));
- assert_eq!(detail["event_kind"], 3423);
- assert_eq!(detail["idempotency_key"], "idem_test");
- assert_eq!(detail["signer_mode"], "local");
- assert_eq!(detail["actions"][0], "radroots order status get ord_test");
- }
-
- #[test]
- fn order_decision_invalid_maps_to_validation_failure() {
- let view = invalid_decision_view();
- let error = match decision_result::<OrderDeclineResult>("order.decline", &view) {
- Ok(_) => panic!("invalid view should fail validation"),
- Err(error) => error,
- };
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "validation_failed");
- assert_eq!(output_error.exit_code, 10);
- assert_eq!(
- output_error.message,
- "active order events for `ord_test` failed reducer validation"
- );
- let detail = output_error.detail.expect("validation detail");
- assert_eq!(detail["state"], "invalid");
- assert_eq!(detail["operation_id"], "order.decline");
- assert_eq!(detail["listing_event_id"], "l".repeat(64));
- assert_eq!(detail["event_id"], Value::Null);
- assert_eq!(detail["event_kind"], Value::Null);
- assert_eq!(detail["idempotency_key"], "idem_test");
- assert_eq!(detail["signer_mode"], "local");
- }
-
- #[test]
- fn order_decline_requires_reason_before_approval() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let decline = OperationRequest::new(
- OperationContext::default(),
- OrderDeclineRequest::from_data(data(&[("order_id", "ord_pending")])),
- )
- .expect("order decline request");
- let error = service.execute(decline).expect_err("reason required");
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "invalid_input");
- assert!(output_error.message.contains("reason"));
- }
-
- #[test]
- fn order_decline_rejects_blank_reason_before_approval() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let decline = OperationRequest::new(
- OperationContext::default(),
- OrderDeclineRequest::from_data(data(&[("order_id", "ord_pending"), ("reason", " ")])),
- )
- .expect("order decline request");
- let error = service.execute(decline).expect_err("reason required");
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "invalid_input");
- assert!(output_error.message.contains("reason"));
- }
-
- #[test]
- fn order_cancel_requires_reason_before_approval() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let cancel = OperationRequest::new(
- OperationContext::default(),
- OrderCancelRequest::from_data(data(&[("order_id", "ord_pending")])),
- )
- .expect("order cancel request");
- let error = service.execute(cancel).expect_err("reason required");
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "invalid_input");
- assert!(output_error.message.contains("reason"));
- }
-
- #[test]
- fn order_cancel_requires_approval_token() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let cancel = OperationRequest::new(
- OperationContext::default(),
- OrderCancelRequest::from_data(data(&[
- ("order_id", "ord_pending"),
- ("reason", "changed plans"),
- ])),
- )
- .expect("order cancel request");
- let error = service.execute(cancel).expect_err("approval required");
-
- assert_eq!(error.to_output_error().code, "approval_required");
- }
-
- #[test]
- fn order_revision_propose_requires_reason_before_approval() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let revision = OperationRequest::new(
- OperationContext::default(),
- OrderRevisionProposeRequest::from_data(data(&[("order_id", "ord_pending")])),
- )
- .expect("order revision request");
- let error = service.execute(revision).expect_err("reason required");
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "invalid_input");
- assert!(output_error.message.contains("reason"));
- }
-
- #[test]
- fn order_revision_propose_requires_approval_token() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let mut input = data(&[
- ("order_id", "ord_pending"),
- ("reason", "update count"),
- ("bin_id", "bin-1"),
- ]);
- input.insert("bin_count".to_owned(), Value::from(3));
- let revision = OperationRequest::new(
- OperationContext::default(),
- OrderRevisionProposeRequest::from_data(input),
- )
- .expect("order revision request");
- let error = service.execute(revision).expect_err("approval required");
-
- assert_eq!(error.to_output_error().code, "approval_required");
- }
-
- #[test]
- fn order_revision_accept_requires_approval_token() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let revision = OperationRequest::new(
- OperationContext::default(),
- OrderRevisionAcceptRequest::from_data(data(&[
- ("order_id", "ord_pending"),
- ("revision_id", "rev_pending"),
- ])),
- )
- .expect("order revision accept request");
- let error = service.execute(revision).expect_err("approval required");
-
- assert_eq!(error.to_output_error().code, "approval_required");
- }
-
- #[test]
- fn order_revision_decline_requires_reason_before_approval() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let revision = OperationRequest::new(
- OperationContext::default(),
- OrderRevisionDeclineRequest::from_data(data(&[
- ("order_id", "ord_pending"),
- ("revision_id", "rev_pending"),
- ])),
- )
- .expect("order revision decline request");
- let error = service.execute(revision).expect_err("reason required");
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "invalid_input");
- assert!(output_error.message.contains("reason"));
- }
-
- #[test]
- fn order_revision_decline_requires_approval_token() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let revision = OperationRequest::new(
- OperationContext::default(),
- OrderRevisionDeclineRequest::from_data(data(&[
- ("order_id", "ord_pending"),
- ("revision_id", "rev_pending"),
- ("reason", "keep original order"),
- ])),
- )
- .expect("order revision decline request");
- let error = service.execute(revision).expect_err("approval required");
-
- assert_eq!(error.to_output_error().code, "approval_required");
- }
-
- #[test]
- fn order_status_get_uses_local_sdk_projection_without_relay() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let status = OperationRequest::new(
- OperationContext::default(),
- OrderStatusGetRequest::from_data(data(&[("order_id", "ord_pending")])),
- )
- .expect("order status request");
- let envelope = service
- .execute(status)
- .expect("status result")
- .to_envelope(OperationContext::default().envelope_context("req_order_status"))
- .expect("status envelope");
-
- assert_eq!(envelope.operation_id, "order.status.get");
- assert_eq!(envelope.result["state"], "missing");
- assert_eq!(envelope.result["source"], "SDK local order projection");
- assert_eq!(
- envelope.result["actor_context_source"],
- "sdk_local_projection"
- );
- assert_eq!(envelope.result["order_id"], "ord_pending");
- assert_eq!(envelope.result["fetched_count"], 0);
- assert_eq!(envelope.result["decoded_count"], 0);
- assert_eq!(envelope.result["skipped_count"], 0);
- assert!(envelope.next_actions.is_empty());
- }
-
- #[test]
- fn order_event_list_requires_relay_configuration() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let context = OperationContext::default();
- let request = OperationRequest::new(context.clone(), OrderEventListRequest::default())
- .expect("order event list request");
- let output_error = service
- .execute(request)
- .expect_err("order event list unconfigured")
- .to_output_error();
- let envelope = crate::out::envelope::OutputEnvelope::failure(
- "order.event.list",
- output_error,
- context.envelope_context("req_order_event_list"),
- );
-
- assert_eq!(envelope.errors[0].code, "operation_unavailable");
- assert_eq!(envelope.errors[0].exit_code, 3);
- assert!(envelope.errors[0].message.contains("configured relay"));
- assert_eq!(
- envelope.errors[0].detail.as_ref().unwrap()["actions"][0],
- "radroots --relay wss://relay.example.com order event list"
- );
- assert_eq!(
- envelope.next_actions[0].command.as_deref(),
- Some("radroots --relay wss://relay.example.com order event list")
- );
- }
-
- #[test]
- fn order_event_list_requires_seller_account_with_account_action() {
- let dir = tempdir().expect("tempdir");
- let mut config = sample_config(dir.path());
- config.relay.urls = vec!["ws://127.0.0.1:9".to_owned()];
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let context = OperationContext::default();
- let request = OperationRequest::new(context.clone(), OrderEventListRequest::default())
- .expect("order event list request");
- let output_error = service
- .execute(request)
- .expect_err("order event list missing account")
- .to_output_error();
- let envelope = crate::out::envelope::OutputEnvelope::failure(
- "order.event.list",
- output_error,
- context.envelope_context("req_order_event_list"),
- );
-
- assert_eq!(envelope.errors[0].code, "operation_unavailable");
- assert!(
- envelope.errors[0]
- .message
- .contains("selected seller account")
- );
- assert_eq!(
- envelope.errors[0].detail.as_ref().unwrap()["actions"][0],
- "radroots account create"
- );
- assert_eq!(
- envelope.next_actions[0].command.as_deref(),
- Some("radroots account create")
- );
- }
-
- #[test]
- fn order_event_watch_returns_deferred_error_with_target_action() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(OrderOperationService::new(&config));
- let request = OperationRequest::new(
- OperationContext::default(),
- OrderEventWatchRequest::from_data(data(&[("order_id", "ord_missing")])),
- )
- .expect("order event watch request");
- let error = service
- .execute(request)
- .expect_err("order event watch deferred");
- let envelope = crate::out::envelope::OutputEnvelope::failure(
- "order.event.watch",
- error.to_output_error(),
- OperationContext::default().envelope_context("req_order_watch"),
- );
-
- assert_eq!(envelope.operation_id, "order.event.watch");
- assert!(envelope.result.is_null());
- assert_eq!(envelope.errors[0].code, "not_implemented");
- assert_eq!(
- envelope.errors[0].detail.as_ref().unwrap()["state"],
- "not_implemented"
- );
- assert_eq!(
- envelope.errors[0].detail.as_ref().unwrap()["order_id"],
- "ord_missing"
- );
- assert_eq!(
- envelope.next_actions[0].command.as_deref(),
- Some("radroots order status get ord_missing")
- );
- }
-
- fn sample_config(root: &Path) -> RuntimeConfig {
- let data = root.join("data");
- let logs = root.join("logs");
- let secrets = root.join("secrets");
- RuntimeConfig {
- output: OutputConfig {
- format: OutputFormat::Human,
- verbosity: Verbosity::Normal,
- color: true,
- dry_run: false,
- },
- interaction: InteractionConfig {
- input_enabled: true,
- assume_yes: false,
- stdin_tty: false,
- stdout_tty: false,
- prompts_allowed: false,
- confirmations_allowed: false,
- },
- paths: PathsConfig {
- profile: "interactive_user".into(),
- profile_source: "test".into(),
- allowed_profiles: vec!["interactive_user".into(), "repo_local".into()],
- root_source: "test".into(),
- repo_local_root: None,
- repo_local_root_source: None,
- subordinate_path_override_source: "runtime_config".into(),
- app_namespace: "apps/cli".into(),
- shared_accounts_namespace: "shared/accounts".into(),
- shared_identities_namespace: "shared/identities".into(),
- app_config_path: root.join("config/apps/cli/config.toml"),
- workspace_config_path: None,
- app_data_root: data.join("apps/cli"),
- app_logs_root: logs.join("apps/cli"),
- shared_accounts_data_root: data.join("shared/accounts"),
- shared_accounts_secrets_root: secrets.join("shared/accounts"),
- default_identity_path: secrets.join("shared/identities/default.json"),
- },
- migration: MigrationConfig {
- report: RadrootsMigrationReport::empty(),
- },
- logging: LoggingConfig {
- filter: "info".into(),
- directory: None,
- stdout: false,
- },
- account: AccountConfig {
- selector: None,
- store_path: data.join("shared/accounts/store.json"),
- secrets_dir: secrets.join("shared/accounts"),
- secret_backend: RadrootsSecretBackend::EncryptedFile,
- secret_fallback: None,
- },
- account_secret_contract: AccountSecretContractConfig {
- default_backend: "host_vault".into(),
- default_fallback: Some("encrypted_file".into()),
- allowed_backends: vec!["host_vault".into(), "encrypted_file".into()],
- host_vault_policy: Some("desktop".into()),
- uses_protected_store: true,
- },
- identity: IdentityConfig {
- path: secrets.join("shared/identities/default.json"),
- },
- signer: SignerConfig {
- backend: SignerBackend::Local,
- },
- publish: PublishConfig {
- transport: PublishTransport::DirectNostrRelay,
- source: PublishTransportSource::Defaults,
- radrootsd_proxy: crate::runtime::config::RadrootsdProxyConfig::default(),
- },
- relay: RelayConfig {
- urls: Vec::new(),
- publish_policy: RelayPublishPolicy::Any,
- source: RelayConfigSource::Defaults,
- },
- local: LocalConfig {
- root: data.join("apps/cli/replica"),
- replica_db_path: data.join("apps/cli/replica/replica.sqlite"),
- backups_dir: data.join("apps/cli/replica/backups"),
- exports_dir: data.join("apps/cli/replica/exports"),
- },
- myc: MycConfig {
- executable: PathBuf::from("myc"),
- status_timeout_ms: 2_000,
- },
- hyf: HyfConfig {
- enabled: false,
- executable: PathBuf::from("hyfd"),
- },
- rpc: RpcConfig {
- url: "http://127.0.0.1:7070".into(),
- },
- rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
- },
- capability_bindings: Vec::new(),
- }
- }
-
- fn data(entries: &[(&str, &str)]) -> OperationData {
- entries
- .iter()
- .map(|(key, value)| ((*key).to_owned(), Value::String((*value).to_owned())))
- .collect::<Map<String, Value>>()
- }
-
- fn already_decided_view() -> OrderDecisionView {
- OrderDecisionView {
- state: "already_decided".to_owned(),
- source: "test".to_owned(),
- order_id: "ord_test".to_owned(),
- listing_addr: Some("30402:seller:listing".to_owned()),
- buyer_pubkey: Some("b".repeat(64)),
- seller_pubkey: Some("s".repeat(64)),
- decision: "accepted".to_owned(),
- request_event_id: Some("r".repeat(64)),
- listing_event_id: Some("l".repeat(64)),
- root_event_id: Some("r".repeat(64)),
- prev_event_id: Some("r".repeat(64)),
- event_id: Some("d".repeat(64)),
- event_kind: Some(3423),
- inventory: None,
- dry_run: false,
- target_relays: vec!["ws://relay.test".to_owned()],
- connected_relays: vec!["ws://relay.test".to_owned()],
- acknowledged_relays: Vec::new(),
- failed_relays: Vec::new(),
- fetched_count: 2,
- decoded_count: 2,
- skipped_count: 0,
- idempotency_key: Some("idem_test".to_owned()),
- signer_mode: Some("local".to_owned()),
- reason: Some(
- "order accept refused because order `ord_test` already has a visible `accepted` seller decision"
- .to_owned(),
- ),
- issues: Vec::new(),
- actions: vec!["radroots order status get ord_test".to_owned()],
- }
- }
-
- fn invalid_decision_view() -> OrderDecisionView {
- let mut view = already_decided_view();
- view.state = "invalid".to_owned();
- view.decision = "declined".to_owned();
- view.event_id = None;
- view.event_kind = None;
- view.reason =
- Some("active order events for `ord_test` failed reducer validation".to_owned());
- view
- }
-}
diff --git a/src/ops/exec/runtime.rs b/src/ops/exec/runtime.rs
@@ -293,6 +293,7 @@ mod tests {
fn sample_config(root: &Path, relays: Vec<String>) -> RuntimeConfig {
let data = root.join("data");
+ let cache = root.join("cache");
let logs = root.join("logs");
let secrets = root.join("secrets");
RuntimeConfig {
@@ -324,6 +325,7 @@ mod tests {
app_config_path: root.join("config/apps/cli/config.toml"),
workspace_config_path: None,
app_data_root: data.join("apps/cli"),
+ shared_cache_root: cache.clone(),
app_logs_root: logs.join("apps/cli"),
shared_accounts_data_root: data.join("shared/accounts"),
shared_accounts_secrets_root: secrets.join("shared/accounts"),
diff --git a/src/ops/exec/trade.rs b/src/ops/exec/trade.rs
@@ -0,0 +1,1890 @@
+use std::path::PathBuf;
+
+use serde::Serialize;
+use serde_json::{Value, json};
+
+use crate::cli::global::{
+ RecordLookupArgs, TradeAppRecordExportArgs, TradeCancelArgs, TradeDecisionArg,
+ TradeDecisionArgs, TradeRebindArgs, TradeRevisionDecisionArg, TradeRevisionDecisionArgs,
+ TradeRevisionProposeArgs, TradeStatusArgs, TradeSubmitArgs,
+};
+use crate::ops::{
+ OperationAdapterError, OperationRequest, OperationRequestData, OperationRequestPayload,
+ OperationResult, OperationResultData, OperationService, TradeAcceptRequest, TradeAcceptResult,
+ TradeAppExportRequest, TradeAppExportResult, TradeAppListRequest, TradeAppListResult,
+ TradeCancelRequest, TradeCancelResult, TradeDeclineRequest, TradeDeclineResult,
+ TradeEventListRequest, TradeEventListResult, TradeEventWatchRequest, TradeEventWatchResult,
+ TradeGetRequest, TradeGetResult, TradeListRequest, TradeListResult, TradeRebindRequest,
+ TradeRebindResult, TradeRevisionAcceptRequest, TradeRevisionAcceptResult,
+ TradeRevisionDeclineRequest, TradeRevisionDeclineResult, TradeRevisionProposeRequest,
+ TradeRevisionProposeResult, TradeStatusGetRequest, TradeStatusGetResult, TradeSubmitRequest,
+ TradeSubmitResult,
+};
+use crate::runtime::RuntimeError;
+use crate::runtime::config::RuntimeConfig;
+use crate::view::runtime::{
+ CommandDisposition, OrderAppRecordExportView, OrderCancellationView, OrderDecisionView,
+ OrderRebindView, OrderRevisionDecisionView, OrderRevisionProposalView, OrderStatusView,
+ OrderSubmitView,
+};
+
+const TRADE_EVENT_WATCH_DEFERRED_REASON: &str = "relay-backed trade event watch is not implemented";
+
+pub struct TradeOperationService<'a> {
+ config: &'a RuntimeConfig,
+}
+
+impl<'a> TradeOperationService<'a> {
+ pub fn new(config: &'a RuntimeConfig) -> Self {
+ Self { config }
+ }
+}
+
+impl OperationService<TradeSubmitRequest> for TradeOperationService<'_> {
+ type Result = TradeSubmitResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeSubmitRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ if request.context.requires_approval_token() {
+ return Err(OperationAdapterError::approval_required(
+ request.operation_id(),
+ ));
+ }
+
+ let key = required_trade_key(&request)?;
+ let args = TradeSubmitArgs {
+ key,
+ idempotency_key: request
+ .context
+ .idempotency_key
+ .clone()
+ .or_else(|| string_input(&request, "idempotency_key")),
+ };
+ let mut config = self.config.clone();
+ if request.context.dry_run {
+ config.output.dry_run = true;
+ }
+ let view = crate::runtime::order::submit(&config, &args).map_err(|error| {
+ OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
+ })?;
+ submit_result::<TradeSubmitResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeGetRequest> for TradeOperationService<'_> {
+ type Result = TradeGetResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeGetRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let args = RecordLookupArgs {
+ key: required_trade_key(&request)?,
+ };
+ let view = map_runtime(crate::runtime::order::get(self.config, &args))?;
+ serialized_target_result::<TradeGetResult, _>(&view)
+ }
+}
+
+impl OperationService<TradeListRequest> for TradeOperationService<'_> {
+ type Result = TradeListResult;
+
+ fn execute(
+ &self,
+ _request: OperationRequest<TradeListRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let view = map_runtime(crate::runtime::order::list(self.config))?;
+ serialized_target_result::<TradeListResult, _>(&view)
+ }
+}
+
+impl OperationService<TradeAppListRequest> for TradeOperationService<'_> {
+ type Result = TradeAppListResult;
+
+ fn execute(
+ &self,
+ _request: OperationRequest<TradeAppListRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let view = map_runtime(crate::runtime::order::app_record_list(self.config))?;
+ serialized_target_result::<TradeAppListResult, _>(&view)
+ }
+}
+
+impl OperationService<TradeAppExportRequest> for TradeOperationService<'_> {
+ type Result = TradeAppExportResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeAppExportRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let args = TradeAppRecordExportArgs {
+ record_id: required_string_input(&request, "record_id")?,
+ output: optional_path_input(&request, "output"),
+ };
+ let mut config = self.config.clone();
+ if request.context.dry_run {
+ config.output.dry_run = true;
+ }
+ let view = crate::runtime::order::app_record_export(&config, &args).map_err(|error| {
+ OperationAdapterError::runtime_failure(request.operation_id(), error)
+ })?;
+ order_app_record_export_result::<TradeAppExportResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeRebindRequest> for TradeOperationService<'_> {
+ type Result = TradeRebindResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeRebindRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let args = TradeRebindArgs {
+ key: required_trade_key(&request)?,
+ selector: required_string_input(&request, "selector")?,
+ };
+ if request.context.dry_run {
+ let view =
+ crate::runtime::order::rebind_preflight(self.config, &args).map_err(|error| {
+ OperationAdapterError::runtime_failure(request.operation_id(), error)
+ })?;
+ return order_rebind_result::<TradeRebindResult>(request.operation_id(), &view);
+ }
+ if request.context.requires_approval_token() {
+ return Err(OperationAdapterError::approval_required(
+ request.operation_id(),
+ ));
+ }
+
+ let view = crate::runtime::order::rebind(self.config, &args).map_err(|error| {
+ OperationAdapterError::runtime_failure(request.operation_id(), error)
+ })?;
+ order_rebind_result::<TradeRebindResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeAcceptRequest> for TradeOperationService<'_> {
+ type Result = TradeAcceptResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeAcceptRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ if request.context.requires_approval_token() {
+ return Err(OperationAdapterError::approval_required(
+ request.operation_id(),
+ ));
+ }
+
+ let args = TradeDecisionArgs {
+ key: required_trade_key(&request)?,
+ decision: TradeDecisionArg::Accept,
+ reason: None,
+ idempotency_key: request
+ .context
+ .idempotency_key
+ .clone()
+ .or_else(|| string_input(&request, "idempotency_key")),
+ };
+ let mut config = self.config.clone();
+ if request.context.dry_run {
+ config.output.dry_run = true;
+ }
+ let view = crate::runtime::order::decide(&config, &args).map_err(|error| {
+ OperationAdapterError::runtime_failure(request.operation_id(), error)
+ })?;
+ decision_result::<TradeAcceptResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeDeclineRequest> for TradeOperationService<'_> {
+ type Result = TradeDeclineResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeDeclineRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let reason = string_input(&request, "reason")
+ .map(|reason| reason.trim().to_owned())
+ .filter(|reason| !reason.is_empty())
+ .ok_or_else(|| {
+ invalid_input(
+ request.operation_id(),
+ "missing required `reason` input".to_owned(),
+ )
+ })?;
+ if request.context.requires_approval_token() {
+ return Err(OperationAdapterError::approval_required(
+ request.operation_id(),
+ ));
+ }
+
+ let args = TradeDecisionArgs {
+ key: required_trade_key(&request)?,
+ decision: TradeDecisionArg::Decline,
+ reason: Some(reason),
+ idempotency_key: request
+ .context
+ .idempotency_key
+ .clone()
+ .or_else(|| string_input(&request, "idempotency_key")),
+ };
+ let mut config = self.config.clone();
+ if request.context.dry_run {
+ config.output.dry_run = true;
+ }
+ let view = crate::runtime::order::decide(&config, &args).map_err(|error| {
+ OperationAdapterError::runtime_failure(request.operation_id(), error)
+ })?;
+ decision_result::<TradeDeclineResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeCancelRequest> for TradeOperationService<'_> {
+ type Result = TradeCancelResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeCancelRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let reason = string_input(&request, "reason")
+ .map(|reason| reason.trim().to_owned())
+ .filter(|reason| !reason.is_empty())
+ .ok_or_else(|| {
+ invalid_input(
+ request.operation_id(),
+ "missing required `reason` input".to_owned(),
+ )
+ })?;
+ if request.context.requires_approval_token() {
+ return Err(OperationAdapterError::approval_required(
+ request.operation_id(),
+ ));
+ }
+
+ let args = TradeCancelArgs {
+ key: required_trade_key(&request)?,
+ reason,
+ idempotency_key: request
+ .context
+ .idempotency_key
+ .clone()
+ .or_else(|| string_input(&request, "idempotency_key")),
+ };
+ let mut config = self.config.clone();
+ if request.context.dry_run {
+ config.output.dry_run = true;
+ }
+ let view = crate::runtime::order::cancel(&config, &args).map_err(|error| {
+ OperationAdapterError::runtime_failure(request.operation_id(), error)
+ })?;
+ cancellation_result::<TradeCancelResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeRevisionProposeRequest> for TradeOperationService<'_> {
+ type Result = TradeRevisionProposeResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeRevisionProposeRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let reason = string_input(&request, "reason")
+ .map(|reason| reason.trim().to_owned())
+ .filter(|reason| !reason.is_empty())
+ .ok_or_else(|| {
+ invalid_input(
+ request.operation_id(),
+ "missing required `reason` input".to_owned(),
+ )
+ })?;
+ if request.context.requires_approval_token() {
+ return Err(OperationAdapterError::approval_required(
+ request.operation_id(),
+ ));
+ }
+
+ let args = TradeRevisionProposeArgs {
+ key: required_trade_key(&request)?,
+ reason,
+ bin_id: string_input(&request, "bin_id"),
+ bin_count: u32_input(&request, "bin_count"),
+ adjustment_id: string_input(&request, "adjustment_id"),
+ adjustment_effect: string_input(&request, "adjustment_effect"),
+ adjustment_amount: string_input(&request, "adjustment_amount"),
+ adjustment_currency: string_input(&request, "adjustment_currency"),
+ adjustment_reason: string_input(&request, "adjustment_reason"),
+ idempotency_key: request
+ .context
+ .idempotency_key
+ .clone()
+ .or_else(|| string_input(&request, "idempotency_key")),
+ };
+ let mut config = self.config.clone();
+ if request.context.dry_run {
+ config.output.dry_run = true;
+ }
+ let view = crate::runtime::order::revision_propose(&config, &args).map_err(|error| {
+ OperationAdapterError::runtime_failure(request.operation_id(), error)
+ })?;
+ revision_proposal_result::<TradeRevisionProposeResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeRevisionAcceptRequest> for TradeOperationService<'_> {
+ type Result = TradeRevisionAcceptResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeRevisionAcceptRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let args = TradeRevisionDecisionArgs {
+ key: required_trade_key(&request)?,
+ revision_id: required_string_input(&request, "revision_id")?,
+ decision: TradeRevisionDecisionArg::Accept,
+ reason: None,
+ idempotency_key: request
+ .context
+ .idempotency_key
+ .clone()
+ .or_else(|| string_input(&request, "idempotency_key")),
+ };
+ if request.context.requires_approval_token() {
+ return Err(OperationAdapterError::approval_required(
+ request.operation_id(),
+ ));
+ }
+
+ let mut config = self.config.clone();
+ if request.context.dry_run {
+ config.output.dry_run = true;
+ }
+ let view = crate::runtime::order::revision_decide(&config, &args).map_err(|error| {
+ OperationAdapterError::runtime_failure(request.operation_id(), error)
+ })?;
+ revision_decision_result::<TradeRevisionAcceptResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeRevisionDeclineRequest> for TradeOperationService<'_> {
+ type Result = TradeRevisionDeclineResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeRevisionDeclineRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let reason = string_input(&request, "reason")
+ .map(|reason| reason.trim().to_owned())
+ .filter(|reason| !reason.is_empty())
+ .ok_or_else(|| {
+ invalid_input(
+ request.operation_id(),
+ "missing required `reason` input".to_owned(),
+ )
+ })?;
+ let args = TradeRevisionDecisionArgs {
+ key: required_trade_key(&request)?,
+ revision_id: required_string_input(&request, "revision_id")?,
+ decision: TradeRevisionDecisionArg::Decline,
+ reason: Some(reason),
+ idempotency_key: request
+ .context
+ .idempotency_key
+ .clone()
+ .or_else(|| string_input(&request, "idempotency_key")),
+ };
+ if request.context.requires_approval_token() {
+ return Err(OperationAdapterError::approval_required(
+ request.operation_id(),
+ ));
+ }
+
+ let mut config = self.config.clone();
+ if request.context.dry_run {
+ config.output.dry_run = true;
+ }
+ let view = crate::runtime::order::revision_decide(&config, &args).map_err(|error| {
+ OperationAdapterError::runtime_failure(request.operation_id(), error)
+ })?;
+ revision_decision_result::<TradeRevisionDeclineResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeStatusGetRequest> for TradeOperationService<'_> {
+ type Result = TradeStatusGetResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeStatusGetRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let args = TradeStatusArgs {
+ key: required_trade_key(&request)?,
+ };
+ let view = crate::runtime::order::status(self.config, &args).map_err(|error| {
+ OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
+ })?;
+ status_result::<TradeStatusGetResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeEventListRequest> for TradeOperationService<'_> {
+ type Result = TradeEventListResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeEventListRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let trade_id = string_input(&request, "trade_id");
+ let view = crate::runtime::order::event_list(self.config, trade_id.as_deref()).map_err(
+ |error| OperationAdapterError::runtime_failure(request.operation_id(), error),
+ )?;
+ event_list_result::<TradeEventListResult>(request.operation_id(), &view)
+ }
+}
+
+impl OperationService<TradeEventWatchRequest> for TradeOperationService<'_> {
+ type Result = TradeEventWatchResult;
+
+ fn execute(
+ &self,
+ request: OperationRequest<TradeEventWatchRequest>,
+ ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
+ let trade_id = required_trade_key(&request)?;
+ let action = format!("radroots trade status get {trade_id}");
+ Err(OperationAdapterError::not_implemented_with_detail(
+ request.operation_id(),
+ TRADE_EVENT_WATCH_DEFERRED_REASON.to_owned(),
+ json!({
+ "state": "not_implemented",
+ "trade_id": trade_id,
+ "reason": TRADE_EVENT_WATCH_DEFERRED_REASON,
+ "actions": [action],
+ }),
+ ))
+ }
+}
+
+fn decision_result<R>(
+ operation_id: &str,
+ view: &OrderDecisionView,
+) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+{
+ match view.disposition() {
+ CommandDisposition::Success => serialized_target_result::<R, _>(view),
+ CommandDisposition::ValidationFailed => {
+ let message = view.reason.clone().unwrap_or_else(|| {
+ format!(
+ "order decision failed validation with state `{}`",
+ view.state
+ )
+ });
+ Err(OperationAdapterError::validation_failed_with_detail(
+ operation_id,
+ message,
+ order_decision_error_detail(view),
+ ))
+ }
+ disposition => {
+ let message = view
+ .reason
+ .clone()
+ .unwrap_or_else(|| format!("order decision finished with state `{}`", view.state));
+ if disposition == CommandDisposition::ExternalUnavailable {
+ let detail = order_decision_error_detail(view);
+ if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
+ Err(OperationAdapterError::network_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ } else {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ }
+ } else if disposition == CommandDisposition::Unconfigured {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ order_decision_error_detail(view),
+ ))
+ } else if disposition == CommandDisposition::NotFound {
+ Err(OperationAdapterError::not_found_with_detail(
+ operation_id,
+ message,
+ order_decision_error_detail(view),
+ ))
+ } else {
+ Err(OperationAdapterError::from_command_disposition(
+ operation_id,
+ disposition,
+ message,
+ ))
+ }
+ }
+ }
+}
+
+fn order_decision_error_detail(view: &OrderDecisionView) -> Value {
+ json!({
+ "state": &view.state,
+ "trade_id": &view.order_id,
+ "listing_addr": &view.listing_addr,
+ "listing_event_id": &view.listing_event_id,
+ "request_event_id": &view.request_event_id,
+ "root_event_id": &view.root_event_id,
+ "prev_event_id": &view.prev_event_id,
+ "event_id": &view.event_id,
+ "event_kind": view.event_kind,
+ "inventory": &view.inventory,
+ "buyer_pubkey": &view.buyer_pubkey,
+ "seller_pubkey": &view.seller_pubkey,
+ "decision": &view.decision,
+ "dry_run": view.dry_run,
+ "target_relays": &view.target_relays,
+ "connected_relays": &view.connected_relays,
+ "acknowledged_relays": &view.acknowledged_relays,
+ "failed_relays": &view.failed_relays,
+ "fetched_count": view.fetched_count,
+ "decoded_count": view.decoded_count,
+ "skipped_count": view.skipped_count,
+ "idempotency_key": &view.idempotency_key,
+ "signer_mode": &view.signer_mode,
+ "issues": &view.issues,
+ "actions": &view.actions,
+ })
+}
+
+fn cancellation_result<R>(
+ operation_id: &str,
+ view: &OrderCancellationView,
+) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+{
+ match view.disposition() {
+ CommandDisposition::Success => serialized_target_result::<R, _>(view),
+ CommandDisposition::ValidationFailed => {
+ let message = view.reason.clone().unwrap_or_else(|| {
+ format!("order cancel failed validation with state `{}`", view.state)
+ });
+ Err(OperationAdapterError::validation_failed_with_detail(
+ operation_id,
+ message,
+ order_cancellation_error_detail(view),
+ ))
+ }
+ disposition => {
+ let message = view
+ .reason
+ .clone()
+ .unwrap_or_else(|| format!("order cancel finished with state `{}`", view.state));
+ if disposition == CommandDisposition::ExternalUnavailable {
+ let detail = order_cancellation_error_detail(view);
+ if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
+ Err(OperationAdapterError::network_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ } else {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ }
+ } else if disposition == CommandDisposition::Unconfigured {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ order_cancellation_error_detail(view),
+ ))
+ } else {
+ Err(OperationAdapterError::from_command_disposition(
+ operation_id,
+ disposition,
+ message,
+ ))
+ }
+ }
+ }
+}
+
+fn order_cancellation_error_detail(view: &OrderCancellationView) -> Value {
+ json!({
+ "state": &view.state,
+ "trade_id": &view.order_id,
+ "listing_addr": &view.listing_addr,
+ "request_event_id": &view.request_event_id,
+ "decision_event_id": &view.decision_event_id,
+ "root_event_id": &view.root_event_id,
+ "prev_event_id": &view.prev_event_id,
+ "event_id": &view.event_id,
+ "event_kind": view.event_kind,
+ "buyer_pubkey": &view.buyer_pubkey,
+ "seller_pubkey": &view.seller_pubkey,
+ "cancellation_reason": &view.cancellation_reason,
+ "dry_run": view.dry_run,
+ "target_relays": &view.target_relays,
+ "connected_relays": &view.connected_relays,
+ "acknowledged_relays": &view.acknowledged_relays,
+ "failed_relays": &view.failed_relays,
+ "fetched_count": view.fetched_count,
+ "decoded_count": view.decoded_count,
+ "skipped_count": view.skipped_count,
+ "idempotency_key": &view.idempotency_key,
+ "signer_mode": &view.signer_mode,
+ "issues": &view.issues,
+ "actions": &view.actions,
+ })
+}
+
+fn revision_proposal_result<R>(
+ operation_id: &str,
+ view: &OrderRevisionProposalView,
+) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+{
+ match view.disposition() {
+ CommandDisposition::Success => serialized_target_result::<R, _>(view),
+ CommandDisposition::ValidationFailed => {
+ let message = view.reason.clone().unwrap_or_else(|| {
+ format!(
+ "order revision propose failed validation with state `{}`",
+ view.state
+ )
+ });
+ Err(OperationAdapterError::validation_failed_with_detail(
+ operation_id,
+ message,
+ order_revision_proposal_error_detail(view),
+ ))
+ }
+ disposition => {
+ let message = view.reason.clone().unwrap_or_else(|| {
+ format!(
+ "order revision propose finished with state `{}`",
+ view.state
+ )
+ });
+ if disposition == CommandDisposition::ExternalUnavailable {
+ let detail = order_revision_proposal_error_detail(view);
+ if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
+ Err(OperationAdapterError::network_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ } else {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ }
+ } else if disposition == CommandDisposition::Unconfigured {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ order_revision_proposal_error_detail(view),
+ ))
+ } else {
+ Err(OperationAdapterError::from_command_disposition(
+ operation_id,
+ disposition,
+ message,
+ ))
+ }
+ }
+ }
+}
+
+fn order_revision_proposal_error_detail(view: &OrderRevisionProposalView) -> Value {
+ json!({
+ "state": &view.state,
+ "trade_id": &view.order_id,
+ "revision_id": &view.revision_id,
+ "listing_addr": &view.listing_addr,
+ "request_event_id": &view.request_event_id,
+ "decision_event_id": &view.decision_event_id,
+ "root_event_id": &view.root_event_id,
+ "prev_event_id": &view.prev_event_id,
+ "event_id": &view.event_id,
+ "event_kind": view.event_kind,
+ "items": &view.items,
+ "economics": &view.economics,
+ "inventory": &view.inventory,
+ "buyer_pubkey": &view.buyer_pubkey,
+ "seller_pubkey": &view.seller_pubkey,
+ "dry_run": view.dry_run,
+ "target_relays": &view.target_relays,
+ "connected_relays": &view.connected_relays,
+ "acknowledged_relays": &view.acknowledged_relays,
+ "failed_relays": &view.failed_relays,
+ "fetched_count": view.fetched_count,
+ "decoded_count": view.decoded_count,
+ "skipped_count": view.skipped_count,
+ "idempotency_key": &view.idempotency_key,
+ "signer_mode": &view.signer_mode,
+ "issues": &view.issues,
+ "actions": &view.actions,
+ })
+}
+
+fn revision_decision_result<R>(
+ operation_id: &str,
+ view: &OrderRevisionDecisionView,
+) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+{
+ match view.disposition() {
+ CommandDisposition::Success => serialized_target_result::<R, _>(view),
+ CommandDisposition::ValidationFailed => {
+ let message = view.reason.clone().unwrap_or_else(|| {
+ format!(
+ "order revision {} failed validation with state `{}`",
+ view.decision.as_deref().unwrap_or("decision"),
+ view.state
+ )
+ });
+ Err(OperationAdapterError::validation_failed_with_detail(
+ operation_id,
+ message,
+ order_revision_decision_error_detail(view),
+ ))
+ }
+ disposition => {
+ let message = view.reason.clone().unwrap_or_else(|| {
+ format!(
+ "order revision {} finished with state `{}`",
+ view.decision.as_deref().unwrap_or("decision"),
+ view.state
+ )
+ });
+ if disposition == CommandDisposition::ExternalUnavailable {
+ let detail = order_revision_decision_error_detail(view);
+ if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
+ Err(OperationAdapterError::network_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ } else {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ }
+ } else if disposition == CommandDisposition::Unconfigured {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ order_revision_decision_error_detail(view),
+ ))
+ } else {
+ Err(OperationAdapterError::from_command_disposition(
+ operation_id,
+ disposition,
+ message,
+ ))
+ }
+ }
+ }
+}
+
+fn order_revision_decision_error_detail(view: &OrderRevisionDecisionView) -> Value {
+ json!({
+ "state": &view.state,
+ "trade_id": &view.order_id,
+ "revision_id": &view.revision_id,
+ "decision": &view.decision,
+ "listing_addr": &view.listing_addr,
+ "request_event_id": &view.request_event_id,
+ "decision_event_id": &view.decision_event_id,
+ "agreement_event_id": &view.agreement_event_id,
+ "root_event_id": &view.root_event_id,
+ "prev_event_id": &view.prev_event_id,
+ "event_id": &view.event_id,
+ "event_kind": view.event_kind,
+ "economics": &view.economics,
+ "inventory": &view.inventory,
+ "buyer_pubkey": &view.buyer_pubkey,
+ "seller_pubkey": &view.seller_pubkey,
+ "dry_run": view.dry_run,
+ "target_relays": &view.target_relays,
+ "connected_relays": &view.connected_relays,
+ "acknowledged_relays": &view.acknowledged_relays,
+ "failed_relays": &view.failed_relays,
+ "fetched_count": view.fetched_count,
+ "decoded_count": view.decoded_count,
+ "skipped_count": view.skipped_count,
+ "idempotency_key": &view.idempotency_key,
+ "signer_mode": &view.signer_mode,
+ "issues": &view.issues,
+ "actions": &view.actions,
+ })
+}
+
+fn status_result<R>(
+ operation_id: &str,
+ view: &OrderStatusView,
+) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+{
+ match view.disposition() {
+ CommandDisposition::Success => serialized_target_result::<R, _>(view),
+ disposition => {
+ let message = view
+ .reason
+ .clone()
+ .unwrap_or_else(|| format!("order status finished with state `{}`", view.state));
+ if disposition == CommandDisposition::ExternalUnavailable {
+ let detail = order_status_error_detail(view);
+ if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
+ Err(OperationAdapterError::network_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ } else {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ }
+ } else if disposition == CommandDisposition::Unconfigured {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ order_status_error_detail(view),
+ ))
+ } else {
+ Err(OperationAdapterError::from_command_disposition(
+ operation_id,
+ disposition,
+ message,
+ ))
+ }
+ }
+ }
+}
+
+fn order_status_error_detail(view: &OrderStatusView) -> Value {
+ json!({
+ "state": &view.state,
+ "trade_id": &view.order_id,
+ "request_event_id": &view.request_event_id,
+ "decision_event_id": &view.decision_event_id,
+ "agreement_event_id": &view.agreement_event_id,
+ "listing_event_id": &view.listing_event_id,
+ "listing_addr": &view.listing_addr,
+ "buyer_pubkey": &view.buyer_pubkey,
+ "seller_pubkey": &view.seller_pubkey,
+ "last_event_id": &view.last_event_id,
+ "revision": &view.revision,
+ "inventory": &view.inventory,
+ "lifecycle": &view.lifecycle,
+ "reducer_issues": &view.reducer_issues,
+ "target_relays": &view.target_relays,
+ "connected_relays": &view.connected_relays,
+ "failed_relays": &view.failed_relays,
+ "fetched_count": view.fetched_count,
+ "decoded_count": view.decoded_count,
+ "skipped_count": view.skipped_count,
+ "actions": &view.actions,
+ })
+}
+
+fn serialized_target_result<R, T>(value: &T) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+ T: Serialize,
+{
+ OperationResult::new(R::from_serializable(value)?)
+}
+
+fn submit_result<R>(
+ operation_id: &str,
+ view: &OrderSubmitView,
+) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+{
+ match view.disposition() {
+ CommandDisposition::Success => serialized_target_result::<R, _>(view),
+ disposition => {
+ let message = view
+ .reason
+ .clone()
+ .unwrap_or_else(|| format!("order submit finished with state `{}`", view.state));
+ let detail = order_submit_error_detail(view);
+ match disposition {
+ CommandDisposition::NotFound => Err(OperationAdapterError::not_found_with_detail(
+ operation_id,
+ message,
+ detail,
+ )),
+ CommandDisposition::ValidationFailed => {
+ Err(OperationAdapterError::validation_failed_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ }
+ CommandDisposition::Unconfigured => {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ }
+ CommandDisposition::ExternalUnavailable => {
+ if !view.failed_relays.is_empty() && view.connected_relays.is_empty() {
+ Err(OperationAdapterError::network_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ } else {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ }
+ }
+ _ => Err(OperationAdapterError::from_command_disposition(
+ operation_id,
+ disposition,
+ message,
+ )),
+ }
+ }
+ }
+}
+
+fn order_app_record_export_result<R>(
+ operation_id: &str,
+ view: &OrderAppRecordExportView,
+) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+{
+ match view.disposition() {
+ CommandDisposition::Success => serialized_target_result::<R, _>(view),
+ CommandDisposition::NotFound => Err(OperationAdapterError::not_found_with_detail(
+ operation_id,
+ view.reason.clone().unwrap_or_else(|| {
+ format!(
+ "app-authored local order record `{}` was not found",
+ view.record_id
+ )
+ }),
+ serde_json::to_value(view).unwrap_or(Value::Null),
+ )),
+ CommandDisposition::ValidationFailed => {
+ Err(OperationAdapterError::validation_failed_with_detail(
+ operation_id,
+ view.reason.clone().unwrap_or_else(|| {
+ format!(
+ "app-authored local order record `{}` cannot be exported",
+ view.record_id
+ )
+ }),
+ serde_json::to_value(view).unwrap_or(Value::Null),
+ ))
+ }
+ disposition => Err(OperationAdapterError::from_command_disposition(
+ operation_id,
+ disposition,
+ view.reason.clone().unwrap_or_else(|| {
+ format!(
+ "app-authored local order record export finished with state `{}`",
+ view.state
+ )
+ }),
+ )),
+ }
+}
+
+fn order_rebind_result<R>(
+ operation_id: &str,
+ view: &OrderRebindView,
+) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+{
+ match view.disposition() {
+ CommandDisposition::Success => serialized_target_result::<R, _>(view),
+ CommandDisposition::NotFound => Err(OperationAdapterError::not_found_with_detail(
+ operation_id,
+ view.reason
+ .clone()
+ .unwrap_or_else(|| format!("order draft `{}` was not found", view.lookup)),
+ order_rebind_error_detail(view),
+ )),
+ CommandDisposition::ValidationFailed => {
+ Err(OperationAdapterError::validation_failed_with_detail(
+ operation_id,
+ view.reason.clone().unwrap_or_else(|| {
+ format!("order rebind finished with state `{}`", view.state)
+ }),
+ order_rebind_error_detail(view),
+ ))
+ }
+ disposition => Err(OperationAdapterError::from_command_disposition(
+ operation_id,
+ disposition,
+ view.reason
+ .clone()
+ .unwrap_or_else(|| format!("order rebind finished with state `{}`", view.state)),
+ )),
+ }
+}
+
+fn order_rebind_error_detail(view: &OrderRebindView) -> Value {
+ json!({
+ "state": &view.state,
+ "source": &view.source,
+ "lookup": &view.lookup,
+ "file": &view.file,
+ "dry_run": view.dry_run,
+ "from_order_id": &view.from_order_id,
+ "to_order_id": &view.to_order_id,
+ "order_id_changed": view.order_id_changed,
+ "from_buyer_account_id": &view.from_buyer_account_id,
+ "from_buyer_pubkey": &view.from_buyer_pubkey,
+ "from_buyer_actor_source": &view.from_buyer_actor_source,
+ "to_buyer_account_id": &view.to_buyer_account_id,
+ "to_buyer_pubkey": &view.to_buyer_pubkey,
+ "to_buyer_actor_source": &view.to_buyer_actor_source,
+ "buyer_pubkey_changed": view.buyer_pubkey_changed,
+ "existing_request_check": &view.existing_request_check,
+ "existing_request_event_ids": &view.existing_request_event_ids,
+ "actions": &view.actions,
+ })
+}
+
+fn order_submit_error_detail(view: &OrderSubmitView) -> Value {
+ json!({
+ "state": &view.state,
+ "source": &view.source,
+ "trade_id": &view.order_id,
+ "file": &view.file,
+ "listing_lookup": &view.listing_lookup,
+ "listing_addr": &view.listing_addr,
+ "listing_event_id": &view.listing_event_id,
+ "listing_relays": &view.listing_relays,
+ "buyer_account_id": &view.buyer_account_id,
+ "buyer_pubkey": &view.buyer_pubkey,
+ "seller_pubkey": &view.seller_pubkey,
+ "event_id": &view.event_id,
+ "event_kind": view.event_kind,
+ "dry_run": view.dry_run,
+ "deduplicated": view.deduplicated,
+ "target_relays": &view.target_relays,
+ "connected_relays": &view.connected_relays,
+ "acknowledged_relays": &view.acknowledged_relays,
+ "failed_relays": &view.failed_relays,
+ "idempotency_key": &view.idempotency_key,
+ "signer_mode": &view.signer_mode,
+ "issues": &view.issues,
+ "actions": &view.actions,
+ })
+}
+
+fn event_list_result<R>(
+ operation_id: &str,
+ view: &crate::view::runtime::OrderEventListView,
+) -> Result<OperationResult<R>, OperationAdapterError>
+where
+ R: OperationResultData,
+{
+ match view.disposition() {
+ CommandDisposition::Success => serialized_target_result::<R, _>(view),
+ disposition => {
+ let message = view.reason.clone().unwrap_or_else(|| {
+ format!("order event list finished with state `{}`", view.state)
+ });
+ if disposition == CommandDisposition::ExternalUnavailable {
+ let detail = order_event_list_error_detail(view);
+ Err(OperationAdapterError::network_unavailable_with_detail(
+ operation_id,
+ message,
+ detail,
+ ))
+ } else if disposition == CommandDisposition::Unconfigured {
+ Err(OperationAdapterError::operation_unavailable_with_detail(
+ operation_id,
+ message,
+ order_event_list_error_detail(view),
+ ))
+ } else {
+ Err(OperationAdapterError::from_command_disposition(
+ operation_id,
+ disposition,
+ message,
+ ))
+ }
+ }
+ }
+}
+
+fn order_event_list_error_detail(view: &crate::view::runtime::OrderEventListView) -> Value {
+ json!({
+ "state": &view.state,
+ "seller_pubkey": &view.seller_pubkey,
+ "target_relays": &view.target_relays,
+ "connected_relays": &view.connected_relays,
+ "failed_relays": &view.failed_relays,
+ "fetched_count": view.fetched_count,
+ "decoded_count": view.decoded_count,
+ "skipped_count": view.skipped_count,
+ "count": view.count,
+ "actions": &view.actions,
+ })
+}
+
+fn required_trade_key<P>(request: &OperationRequest<P>) -> Result<String, OperationAdapterError>
+where
+ P: OperationRequestPayload + OperationRequestData,
+{
+ string_input(request, "trade_id").ok_or_else(|| {
+ invalid_input(
+ request.operation_id(),
+ "missing required `trade_id` input".to_owned(),
+ )
+ })
+}
+
+fn required_string_input<P>(
+ request: &OperationRequest<P>,
+ key: &str,
+) -> Result<String, OperationAdapterError>
+where
+ P: OperationRequestPayload + OperationRequestData,
+{
+ string_input(request, key)
+ .map(|value| value.trim().to_owned())
+ .filter(|value| !value.is_empty())
+ .ok_or_else(|| {
+ invalid_input(
+ request.operation_id(),
+ format!("missing required `{key}` input"),
+ )
+ })
+}
+
+fn string_input<P>(request: &OperationRequest<P>, key: &str) -> Option<String>
+where
+ P: OperationRequestPayload + OperationRequestData,
+{
+ request
+ .payload
+ .input()
+ .get(key)
+ .and_then(Value::as_str)
+ .map(str::to_owned)
+}
+
+fn optional_path_input<P>(request: &OperationRequest<P>, key: &str) -> Option<PathBuf>
+where
+ P: OperationRequestPayload + OperationRequestData,
+{
+ string_input(request, key).map(PathBuf::from)
+}
+
+fn bool_input<P>(request: &OperationRequest<P>, key: &str) -> Option<bool>
+where
+ P: OperationRequestPayload + OperationRequestData,
+{
+ request.payload.input().get(key).and_then(Value::as_bool)
+}
+
+fn u32_input<P>(request: &OperationRequest<P>, key: &str) -> Option<u32>
+where
+ P: OperationRequestPayload + OperationRequestData,
+{
+ request
+ .payload
+ .input()
+ .get(key)
+ .and_then(Value::as_u64)
+ .and_then(|value| u32::try_from(value).ok())
+}
+
+fn map_runtime<T>(result: Result<T, RuntimeError>) -> Result<T, OperationAdapterError> {
+ result.map_err(|error| OperationAdapterError::Runtime(error.to_string()))
+}
+
+fn invalid_input(operation_id: &str, message: String) -> OperationAdapterError {
+ OperationAdapterError::InvalidInput {
+ operation_id: operation_id.to_owned(),
+ message,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::path::{Path, PathBuf};
+
+ use radroots_runtime_paths::RadrootsMigrationReport;
+ use radroots_secret_vault::RadrootsSecretBackend;
+ use serde_json::{Map, Value};
+ use tempfile::tempdir;
+
+ use super::{TradeOperationService, decision_result};
+ use crate::ops::{
+ OperationAdapter, OperationContext, OperationData, OperationRequest, TradeAcceptRequest,
+ TradeAcceptResult, TradeCancelRequest, TradeDeclineRequest, TradeDeclineResult,
+ TradeEventListRequest, TradeEventWatchRequest, TradeGetRequest, TradeListRequest,
+ TradeRevisionAcceptRequest, TradeRevisionDeclineRequest, TradeRevisionProposeRequest,
+ TradeStatusGetRequest, TradeSubmitRequest,
+ };
+ use crate::runtime::config::{
+ AccountConfig, AccountSecretContractConfig, HyfConfig, IdentityConfig, InteractionConfig,
+ LocalConfig, LoggingConfig, MigrationConfig, MycConfig, OutputConfig, OutputFormat,
+ PathsConfig, PublishConfig, PublishTransport, PublishTransportSource, RelayConfig,
+ RelayConfigSource, RelayPublishPolicy, RpcConfig, RuntimeConfig, SignerBackend,
+ SignerConfig, Verbosity,
+ };
+ use crate::view::runtime::OrderDecisionView;
+
+ #[test]
+ fn order_service_get_and_list_preserve_order_truth() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let get = OperationRequest::new(
+ OperationContext::default(),
+ TradeGetRequest::from_data(data(&[("trade_id", "ord_missing")])),
+ )
+ .expect("order get request");
+ let get_envelope = service
+ .execute(get)
+ .expect("order get result")
+ .to_envelope(OperationContext::default().envelope_context("req_order_get"))
+ .expect("order get envelope");
+
+ assert_eq!(get_envelope.operation_id, "trade.get");
+ assert_eq!(get_envelope.result["state"], "missing");
+ assert_eq!(get_envelope.result["actions"][0], "radroots trade list");
+ assert_eq!(get_envelope.result["actions"][1], "radroots basket create");
+
+ let list = OperationRequest::new(OperationContext::default(), TradeListRequest::default())
+ .expect("order list request");
+ let list_envelope = service
+ .execute(list)
+ .expect("order list result")
+ .to_envelope(OperationContext::default().envelope_context("req_order_list"))
+ .expect("order list envelope");
+ assert_eq!(list_envelope.operation_id, "trade.list");
+ assert_eq!(list_envelope.result["state"], "empty");
+ assert_eq!(list_envelope.result["actions"][0], "radroots basket create");
+ }
+
+ #[test]
+ fn order_submit_requires_approval_token() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let submit = OperationRequest::new(
+ OperationContext::default(),
+ TradeSubmitRequest::from_data(data(&[("trade_id", "ord_missing")])),
+ )
+ .expect("order submit request");
+ let error = service.execute(submit).expect_err("approval required");
+
+ assert!(format!("{error}").contains("approval_token"));
+ assert_eq!(error.to_output_error().code, "approval_required");
+ assert_eq!(error.to_output_error().exit_code, 6);
+ }
+
+ #[test]
+ fn order_submit_with_approval_returns_not_found_for_missing_order() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let mut context = OperationContext::default();
+ context.approval_token = Some("approve_test".to_owned());
+ let submit = OperationRequest::new(
+ context.clone(),
+ TradeSubmitRequest::from_data(data(&[("trade_id", "ord_missing")])),
+ )
+ .expect("order submit request");
+ let error = service.execute(submit).expect_err("missing order error");
+ let output_error = error.to_output_error();
+
+ assert_eq!(output_error.code, "not_found");
+ assert_eq!(output_error.exit_code, 4);
+ assert!(output_error.message.contains("ord_missing"));
+ let envelope = crate::out::envelope::OutputEnvelope::failure(
+ "trade.submit",
+ output_error,
+ context.envelope_context("req_order_submit"),
+ );
+ let detail = envelope.errors[0].detail.as_ref().expect("submit detail");
+ assert_eq!(detail["state"], "missing");
+ assert_eq!(detail["trade_id"], "ord_missing");
+ assert_eq!(detail["actions"][0], "radroots trade list");
+ assert_eq!(detail["actions"][1], "radroots basket create");
+ assert_eq!(
+ envelope.next_actions[0].command.as_deref(),
+ Some("radroots trade list")
+ );
+ assert_eq!(
+ envelope.next_actions[1].command.as_deref(),
+ Some("radroots basket create")
+ );
+ }
+
+ #[test]
+ fn order_accept_requires_approval_token() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let accept = OperationRequest::new(
+ OperationContext::default(),
+ TradeAcceptRequest::from_data(data(&[("trade_id", "ord_pending")])),
+ )
+ .expect("order accept request");
+ let error = service.execute(accept).expect_err("approval required");
+
+ assert_eq!(error.to_output_error().code, "approval_required");
+ }
+
+ #[test]
+ fn order_accept_unconfigured_preserves_decision_detail() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let mut context = OperationContext::default();
+ context.dry_run = true;
+ let accept = OperationRequest::new(
+ context,
+ TradeAcceptRequest::from_data(data(&[("trade_id", "ord_pending")])),
+ )
+ .expect("order accept request");
+ let error = service
+ .execute(accept)
+ .expect_err("order accept unconfigured");
+ let output_error = error.to_output_error();
+ let detail = output_error.detail.as_ref().expect("decision detail");
+
+ assert_eq!(output_error.code, "operation_unavailable");
+ assert_eq!(detail["state"], "unconfigured");
+ assert_eq!(detail["trade_id"], "ord_pending");
+ assert_eq!(detail["decision"], "accepted");
+ assert!(detail["target_relays"].as_array().unwrap().is_empty());
+ }
+
+ #[test]
+ fn order_decision_already_decided_maps_to_validation_failure() {
+ let view = already_decided_view();
+ let error = match decision_result::<TradeAcceptResult>("trade.accept", &view) {
+ Ok(_) => panic!("already decided view should fail validation"),
+ Err(error) => error,
+ };
+ let output_error = error.to_output_error();
+
+ assert_eq!(output_error.code, "validation_failed");
+ assert_eq!(output_error.exit_code, 10);
+ let detail = output_error.detail.expect("validation detail");
+ assert_eq!(detail["state"], "already_decided");
+ assert_eq!(detail["operation_id"], "trade.accept");
+ assert_eq!(detail["listing_event_id"], "l".repeat(64));
+ assert_eq!(detail["event_id"], "d".repeat(64));
+ assert_eq!(detail["event_kind"], 3423);
+ assert_eq!(detail["idempotency_key"], "idem_test");
+ assert_eq!(detail["signer_mode"], "local");
+ assert_eq!(detail["actions"][0], "radroots trade status get ord_test");
+ }
+
+ #[test]
+ fn order_decision_invalid_maps_to_validation_failure() {
+ let view = invalid_decision_view();
+ let error = match decision_result::<TradeDeclineResult>("trade.decline", &view) {
+ Ok(_) => panic!("invalid view should fail validation"),
+ Err(error) => error,
+ };
+ let output_error = error.to_output_error();
+
+ assert_eq!(output_error.code, "validation_failed");
+ assert_eq!(output_error.exit_code, 10);
+ assert_eq!(
+ output_error.message,
+ "active trade events for `ord_test` failed reducer validation"
+ );
+ let detail = output_error.detail.expect("validation detail");
+ assert_eq!(detail["state"], "invalid");
+ assert_eq!(detail["operation_id"], "trade.decline");
+ assert_eq!(detail["listing_event_id"], "l".repeat(64));
+ assert_eq!(detail["event_id"], Value::Null);
+ assert_eq!(detail["event_kind"], Value::Null);
+ assert_eq!(detail["idempotency_key"], "idem_test");
+ assert_eq!(detail["signer_mode"], "local");
+ }
+
+ #[test]
+ fn order_decline_requires_reason_before_approval() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let decline = OperationRequest::new(
+ OperationContext::default(),
+ TradeDeclineRequest::from_data(data(&[("trade_id", "ord_pending")])),
+ )
+ .expect("order decline request");
+ let error = service.execute(decline).expect_err("reason required");
+ let output_error = error.to_output_error();
+
+ assert_eq!(output_error.code, "invalid_input");
+ assert!(output_error.message.contains("reason"));
+ }
+
+ #[test]
+ fn order_decline_rejects_blank_reason_before_approval() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let decline = OperationRequest::new(
+ OperationContext::default(),
+ TradeDeclineRequest::from_data(data(&[("trade_id", "ord_pending"), ("reason", " ")])),
+ )
+ .expect("order decline request");
+ let error = service.execute(decline).expect_err("reason required");
+ let output_error = error.to_output_error();
+
+ assert_eq!(output_error.code, "invalid_input");
+ assert!(output_error.message.contains("reason"));
+ }
+
+ #[test]
+ fn order_cancel_requires_reason_before_approval() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let cancel = OperationRequest::new(
+ OperationContext::default(),
+ TradeCancelRequest::from_data(data(&[("trade_id", "ord_pending")])),
+ )
+ .expect("order cancel request");
+ let error = service.execute(cancel).expect_err("reason required");
+ let output_error = error.to_output_error();
+
+ assert_eq!(output_error.code, "invalid_input");
+ assert!(output_error.message.contains("reason"));
+ }
+
+ #[test]
+ fn order_cancel_requires_approval_token() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let cancel = OperationRequest::new(
+ OperationContext::default(),
+ TradeCancelRequest::from_data(data(&[
+ ("trade_id", "ord_pending"),
+ ("reason", "changed plans"),
+ ])),
+ )
+ .expect("order cancel request");
+ let error = service.execute(cancel).expect_err("approval required");
+
+ assert_eq!(error.to_output_error().code, "approval_required");
+ }
+
+ #[test]
+ fn order_revision_propose_requires_reason_before_approval() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let revision = OperationRequest::new(
+ OperationContext::default(),
+ TradeRevisionProposeRequest::from_data(data(&[("trade_id", "ord_pending")])),
+ )
+ .expect("order revision request");
+ let error = service.execute(revision).expect_err("reason required");
+ let output_error = error.to_output_error();
+
+ assert_eq!(output_error.code, "invalid_input");
+ assert!(output_error.message.contains("reason"));
+ }
+
+ #[test]
+ fn order_revision_propose_requires_approval_token() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let mut input = data(&[
+ ("trade_id", "ord_pending"),
+ ("reason", "update count"),
+ ("bin_id", "bin-1"),
+ ]);
+ input.insert("bin_count".to_owned(), Value::from(3));
+ let revision = OperationRequest::new(
+ OperationContext::default(),
+ TradeRevisionProposeRequest::from_data(input),
+ )
+ .expect("order revision request");
+ let error = service.execute(revision).expect_err("approval required");
+
+ assert_eq!(error.to_output_error().code, "approval_required");
+ }
+
+ #[test]
+ fn order_revision_accept_requires_approval_token() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let revision = OperationRequest::new(
+ OperationContext::default(),
+ TradeRevisionAcceptRequest::from_data(data(&[
+ ("trade_id", "ord_pending"),
+ ("revision_id", "rev_pending"),
+ ])),
+ )
+ .expect("order revision accept request");
+ let error = service.execute(revision).expect_err("approval required");
+
+ assert_eq!(error.to_output_error().code, "approval_required");
+ }
+
+ #[test]
+ fn order_revision_decline_requires_reason_before_approval() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let revision = OperationRequest::new(
+ OperationContext::default(),
+ TradeRevisionDeclineRequest::from_data(data(&[
+ ("trade_id", "ord_pending"),
+ ("revision_id", "rev_pending"),
+ ])),
+ )
+ .expect("order revision decline request");
+ let error = service.execute(revision).expect_err("reason required");
+ let output_error = error.to_output_error();
+
+ assert_eq!(output_error.code, "invalid_input");
+ assert!(output_error.message.contains("reason"));
+ }
+
+ #[test]
+ fn order_revision_decline_requires_approval_token() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let revision = OperationRequest::new(
+ OperationContext::default(),
+ TradeRevisionDeclineRequest::from_data(data(&[
+ ("trade_id", "ord_pending"),
+ ("revision_id", "rev_pending"),
+ ("reason", "keep original order"),
+ ])),
+ )
+ .expect("order revision decline request");
+ let error = service.execute(revision).expect_err("approval required");
+
+ assert_eq!(error.to_output_error().code, "approval_required");
+ }
+
+ #[test]
+ fn order_status_get_uses_local_sdk_projection_without_relay() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let status = OperationRequest::new(
+ OperationContext::default(),
+ TradeStatusGetRequest::from_data(data(&[("trade_id", "ord_pending")])),
+ )
+ .expect("order status request");
+ let envelope = service
+ .execute(status)
+ .expect("status result")
+ .to_envelope(OperationContext::default().envelope_context("req_order_status"))
+ .expect("status envelope");
+
+ assert_eq!(envelope.operation_id, "trade.status.get");
+ assert_eq!(envelope.result["state"], "missing");
+ assert_eq!(envelope.result["source"], "SDK local trade projection");
+ assert_eq!(
+ envelope.result["actor_context_source"],
+ "sdk_local_projection"
+ );
+ assert_eq!(envelope.result["trade_id"], "ord_pending");
+ assert_eq!(envelope.result["fetched_count"], 0);
+ assert_eq!(envelope.result["decoded_count"], 0);
+ assert_eq!(envelope.result["skipped_count"], 0);
+ assert!(envelope.next_actions.is_empty());
+ }
+
+ #[test]
+ fn order_event_list_requires_relay_configuration() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let context = OperationContext::default();
+ let request = OperationRequest::new(context.clone(), TradeEventListRequest::default())
+ .expect("order event list request");
+ let output_error = service
+ .execute(request)
+ .expect_err("order event list unconfigured")
+ .to_output_error();
+ let envelope = crate::out::envelope::OutputEnvelope::failure(
+ "trade.event.list",
+ output_error,
+ context.envelope_context("req_order_event_list"),
+ );
+
+ assert_eq!(envelope.errors[0].code, "operation_unavailable");
+ assert_eq!(envelope.errors[0].exit_code, 3);
+ assert!(envelope.errors[0].message.contains("configured relay"));
+ assert_eq!(
+ envelope.errors[0].detail.as_ref().unwrap()["actions"][0],
+ "radroots --relay wss://relay.example.com trade event list"
+ );
+ assert_eq!(
+ envelope.next_actions[0].command.as_deref(),
+ Some("radroots --relay wss://relay.example.com trade event list")
+ );
+ }
+
+ #[test]
+ fn order_event_list_requires_seller_account_with_account_action() {
+ let dir = tempdir().expect("tempdir");
+ let mut config = sample_config(dir.path());
+ config.relay.urls = vec!["ws://127.0.0.1:9".to_owned()];
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let context = OperationContext::default();
+ let request = OperationRequest::new(context.clone(), TradeEventListRequest::default())
+ .expect("order event list request");
+ let output_error = service
+ .execute(request)
+ .expect_err("order event list missing account")
+ .to_output_error();
+ let envelope = crate::out::envelope::OutputEnvelope::failure(
+ "trade.event.list",
+ output_error,
+ context.envelope_context("req_order_event_list"),
+ );
+
+ assert_eq!(envelope.errors[0].code, "operation_unavailable");
+ assert!(
+ envelope.errors[0]
+ .message
+ .contains("selected seller account")
+ );
+ assert_eq!(
+ envelope.errors[0].detail.as_ref().unwrap()["actions"][0],
+ "radroots account create"
+ );
+ assert_eq!(
+ envelope.next_actions[0].command.as_deref(),
+ Some("radroots account create")
+ );
+ }
+
+ #[test]
+ fn order_event_watch_returns_deferred_error_with_target_action() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let service = OperationAdapter::new(TradeOperationService::new(&config));
+ let request = OperationRequest::new(
+ OperationContext::default(),
+ TradeEventWatchRequest::from_data(data(&[("trade_id", "ord_missing")])),
+ )
+ .expect("order event watch request");
+ let error = service
+ .execute(request)
+ .expect_err("order event watch deferred");
+ let envelope = crate::out::envelope::OutputEnvelope::failure(
+ "trade.event.watch",
+ error.to_output_error(),
+ OperationContext::default().envelope_context("req_order_watch"),
+ );
+
+ assert_eq!(envelope.operation_id, "trade.event.watch");
+ assert!(envelope.result.is_null());
+ assert_eq!(envelope.errors[0].code, "not_implemented");
+ assert_eq!(
+ envelope.errors[0].detail.as_ref().unwrap()["state"],
+ "not_implemented"
+ );
+ assert_eq!(
+ envelope.errors[0].detail.as_ref().unwrap()["trade_id"],
+ "ord_missing"
+ );
+ assert_eq!(
+ envelope.next_actions[0].command.as_deref(),
+ Some("radroots trade status get ord_missing")
+ );
+ }
+
+ fn sample_config(root: &Path) -> RuntimeConfig {
+ let data = root.join("data");
+ let cache = root.join("cache");
+ let logs = root.join("logs");
+ let secrets = root.join("secrets");
+ RuntimeConfig {
+ output: OutputConfig {
+ format: OutputFormat::Human,
+ verbosity: Verbosity::Normal,
+ color: true,
+ dry_run: false,
+ },
+ interaction: InteractionConfig {
+ input_enabled: true,
+ assume_yes: false,
+ stdin_tty: false,
+ stdout_tty: false,
+ prompts_allowed: false,
+ confirmations_allowed: false,
+ },
+ paths: PathsConfig {
+ profile: "interactive_user".into(),
+ profile_source: "test".into(),
+ allowed_profiles: vec!["interactive_user".into(), "repo_local".into()],
+ root_source: "test".into(),
+ repo_local_root: None,
+ repo_local_root_source: None,
+ subordinate_path_override_source: "runtime_config".into(),
+ app_namespace: "apps/cli".into(),
+ shared_accounts_namespace: "shared/accounts".into(),
+ shared_identities_namespace: "shared/identities".into(),
+ app_config_path: root.join("config/apps/cli/config.toml"),
+ workspace_config_path: None,
+ app_data_root: data.join("apps/cli"),
+ shared_cache_root: cache.clone(),
+ app_logs_root: logs.join("apps/cli"),
+ shared_accounts_data_root: data.join("shared/accounts"),
+ shared_accounts_secrets_root: secrets.join("shared/accounts"),
+ default_identity_path: secrets.join("shared/identities/default.json"),
+ },
+ migration: MigrationConfig {
+ report: RadrootsMigrationReport::empty(),
+ },
+ logging: LoggingConfig {
+ filter: "info".into(),
+ directory: None,
+ stdout: false,
+ },
+ account: AccountConfig {
+ selector: None,
+ store_path: data.join("shared/accounts/store.json"),
+ secrets_dir: secrets.join("shared/accounts"),
+ secret_backend: RadrootsSecretBackend::EncryptedFile,
+ secret_fallback: None,
+ },
+ account_secret_contract: AccountSecretContractConfig {
+ default_backend: "host_vault".into(),
+ default_fallback: Some("encrypted_file".into()),
+ allowed_backends: vec!["host_vault".into(), "encrypted_file".into()],
+ host_vault_policy: Some("desktop".into()),
+ uses_protected_store: true,
+ },
+ identity: IdentityConfig {
+ path: secrets.join("shared/identities/default.json"),
+ },
+ signer: SignerConfig {
+ backend: SignerBackend::Local,
+ },
+ publish: PublishConfig {
+ transport: PublishTransport::DirectNostrRelay,
+ source: PublishTransportSource::Defaults,
+ radrootsd_proxy: crate::runtime::config::RadrootsdProxyConfig::default(),
+ },
+ relay: RelayConfig {
+ urls: Vec::new(),
+ publish_policy: RelayPublishPolicy::Any,
+ source: RelayConfigSource::Defaults,
+ },
+ local: LocalConfig {
+ root: data.join("apps/cli/replica"),
+ replica_db_path: data.join("apps/cli/replica/replica.sqlite"),
+ backups_dir: data.join("apps/cli/replica/backups"),
+ exports_dir: data.join("apps/cli/replica/exports"),
+ },
+ myc: MycConfig {
+ executable: PathBuf::from("myc"),
+ status_timeout_ms: 2_000,
+ },
+ hyf: HyfConfig {
+ enabled: false,
+ executable: PathBuf::from("hyfd"),
+ },
+ rpc: RpcConfig {
+ url: "http://127.0.0.1:7070".into(),
+ },
+ rhi: crate::runtime::config::RhiConfig {
+ trusted_worker_pubkeys: Vec::new(),
+ },
+ capability_bindings: Vec::new(),
+ }
+ }
+
+ fn data(entries: &[(&str, &str)]) -> OperationData {
+ entries
+ .iter()
+ .map(|(key, value)| ((*key).to_owned(), Value::String((*value).to_owned())))
+ .collect::<Map<String, Value>>()
+ }
+
+ fn already_decided_view() -> OrderDecisionView {
+ OrderDecisionView {
+ state: "already_decided".to_owned(),
+ source: "test".to_owned(),
+ order_id: "ord_test".to_owned(),
+ listing_addr: Some("30402:seller:listing".to_owned()),
+ buyer_pubkey: Some("b".repeat(64)),
+ seller_pubkey: Some("s".repeat(64)),
+ decision: "accepted".to_owned(),
+ request_event_id: Some("r".repeat(64)),
+ listing_event_id: Some("l".repeat(64)),
+ root_event_id: Some("r".repeat(64)),
+ prev_event_id: Some("r".repeat(64)),
+ event_id: Some("d".repeat(64)),
+ event_kind: Some(3423),
+ inventory: None,
+ dry_run: false,
+ target_relays: vec!["ws://relay.test".to_owned()],
+ connected_relays: vec!["ws://relay.test".to_owned()],
+ acknowledged_relays: Vec::new(),
+ failed_relays: Vec::new(),
+ fetched_count: 2,
+ decoded_count: 2,
+ skipped_count: 0,
+ idempotency_key: Some("idem_test".to_owned()),
+ signer_mode: Some("local".to_owned()),
+ reason: Some(
+ "order accept refused because order `ord_test` already has a visible `accepted` seller decision"
+ .to_owned(),
+ ),
+ issues: Vec::new(),
+ actions: vec!["radroots trade status get ord_test".to_owned()],
+ }
+ }
+
+ fn invalid_decision_view() -> OrderDecisionView {
+ let mut view = already_decided_view();
+ view.state = "invalid".to_owned();
+ view.decision = "declined".to_owned();
+ view.event_id = None;
+ view.event_kind = None;
+ view.reason =
+ Some("active trade events for `ord_test` failed reducer validation".to_owned());
+ view
+ }
+}
diff --git a/src/ops/exec/validation.rs b/src/ops/exec/validation.rs
@@ -87,7 +87,7 @@ where
P: OperationRequestPayload + OperationRequestData,
{
Ok(ValidationReceiptListArgs {
- order_id: required_string(request, "order_id")?,
+ order_id: required_string(request, "trade_id")?,
})
}
diff --git a/src/ops/mod.rs b/src/ops/mod.rs
@@ -215,21 +215,21 @@ mod tests {
#[test]
fn adapter_maps_order_rebind_inputs() {
let parsed =
- TargetCliArgs::try_parse_from(["radroots", "order", "rebind", "ord_test", "acct_test"])
+ TargetCliArgs::try_parse_from(["radroots", "trade", "rebind", "ord_test", "acct_test"])
.expect("target args parse");
let request = TargetOperationRequest::from_target_args(&parsed)
.expect("operation request from target args");
- let TargetOperationRequest::OrderRebind(request) = request else {
+ let TargetOperationRequest::TradeRebind(request) = request else {
panic!("expected order rebind request")
};
- assert_eq!(request.operation_id(), "order.rebind");
+ assert_eq!(request.operation_id(), "trade.rebind");
assert_eq!(
request
.payload
.input
- .get("order_id")
+ .get("trade_id")
.and_then(Value::as_str),
Some("ord_test")
);
@@ -247,7 +247,7 @@ mod tests {
fn adapter_maps_order_lifecycle_inputs() {
let revision = TargetCliArgs::try_parse_from([
"radroots",
- "order",
+ "trade",
"revision",
"propose",
"ord_test",
@@ -271,15 +271,15 @@ mod tests {
.expect("target args parse");
let request =
TargetOperationRequest::from_target_args(&revision).expect("operation request");
- let TargetOperationRequest::OrderRevisionPropose(request) = request else {
+ let TargetOperationRequest::TradeRevisionPropose(request) = request else {
panic!("expected order revision propose request")
};
- assert_eq!(request.operation_id(), "order.revision.propose");
+ assert_eq!(request.operation_id(), "trade.revision.propose");
assert_eq!(
request
.payload
.input
- .get("order_id")
+ .get("trade_id")
.and_then(Value::as_str),
Some("ord_test")
);
@@ -342,7 +342,7 @@ mod tests {
let revision_accept = TargetCliArgs::try_parse_from([
"radroots",
- "order",
+ "trade",
"revision",
"accept",
"ord_test",
@@ -352,15 +352,15 @@ mod tests {
.expect("target args parse");
let request =
TargetOperationRequest::from_target_args(&revision_accept).expect("operation request");
- let TargetOperationRequest::OrderRevisionAccept(request) = request else {
+ let TargetOperationRequest::TradeRevisionAccept(request) = request else {
panic!("expected order revision accept request")
};
- assert_eq!(request.operation_id(), "order.revision.accept");
+ assert_eq!(request.operation_id(), "trade.revision.accept");
assert_eq!(
request
.payload
.input
- .get("order_id")
+ .get("trade_id")
.and_then(Value::as_str),
Some("ord_test")
);
@@ -375,7 +375,7 @@ mod tests {
let revision_decline = TargetCliArgs::try_parse_from([
"radroots",
- "order",
+ "trade",
"revision",
"decline",
"ord_test",
@@ -387,15 +387,15 @@ mod tests {
.expect("target args parse");
let request =
TargetOperationRequest::from_target_args(&revision_decline).expect("operation request");
- let TargetOperationRequest::OrderRevisionDecline(request) = request else {
+ let TargetOperationRequest::TradeRevisionDecline(request) = request else {
panic!("expected order revision decline request")
};
- assert_eq!(request.operation_id(), "order.revision.decline");
+ assert_eq!(request.operation_id(), "trade.revision.decline");
assert_eq!(
request
.payload
.input
- .get("order_id")
+ .get("trade_id")
.and_then(Value::as_str),
Some("ord_test")
);
@@ -414,7 +414,7 @@ mod tests {
let cancel = TargetCliArgs::try_parse_from([
"radroots",
- "order",
+ "trade",
"cancel",
"ord_test",
"--reason",
@@ -422,15 +422,15 @@ mod tests {
])
.expect("target args parse");
let request = TargetOperationRequest::from_target_args(&cancel).expect("operation request");
- let TargetOperationRequest::OrderCancel(request) = request else {
+ let TargetOperationRequest::TradeCancel(request) = request else {
panic!("expected order cancel request")
};
- assert_eq!(request.operation_id(), "order.cancel");
+ assert_eq!(request.operation_id(), "trade.cancel");
assert_eq!(
request
.payload
.input
- .get("order_id")
+ .get("trade_id")
.and_then(Value::as_str),
Some("ord_test")
);
@@ -473,7 +473,7 @@ mod tests {
#[test]
fn approval_errors_map_to_structured_exit_code() {
- let error = OperationAdapterError::approval_required("order.submit");
+ let error = OperationAdapterError::approval_required("trade.submit");
let output_error = error.to_output_error();
assert_eq!(output_error.code, "approval_required");
diff --git a/src/ops/target.rs b/src/ops/target.rs
@@ -197,7 +197,9 @@ target_operation_contracts! {
FarmGet => (FarmGetRequest, FarmGetResult, "farm.get"),
FarmRebind => (FarmRebindRequest, FarmRebindResult, "farm.rebind"),
FarmProfileUpdate => (FarmProfileUpdateRequest, FarmProfileUpdateResult, "farm.profile.update"),
- FarmLocationUpdate => (FarmLocationUpdateRequest, FarmLocationUpdateResult, "farm.location.update"),
+ FarmLocationSet => (FarmLocationSetRequest, FarmLocationSetResult, "farm.location.set"),
+ FarmLocationGet => (FarmLocationGetRequest, FarmLocationGetResult, "farm.location.get"),
+ FarmLocationClear => (FarmLocationClearRequest, FarmLocationClearResult, "farm.location.clear"),
FarmFulfillmentUpdate => (FarmFulfillmentUpdateRequest, FarmFulfillmentUpdateResult, "farm.fulfillment.update"),
FarmReadinessCheck => (FarmReadinessCheckRequest, FarmReadinessCheckResult, "farm.readiness.check"),
FarmPublish => (FarmPublishRequest, FarmPublishResult, "farm.publish"),
@@ -224,21 +226,21 @@ target_operation_contracts! {
BasketAdjustmentRemove => (BasketAdjustmentRemoveRequest, BasketAdjustmentRemoveResult, "basket.adjustment.remove"),
BasketValidate => (BasketValidateRequest, BasketValidateResult, "basket.validate"),
BasketQuoteCreate => (BasketQuoteCreateRequest, BasketQuoteCreateResult, "basket.quote.create"),
- OrderSubmit => (OrderSubmitRequest, OrderSubmitResult, "order.submit"),
- OrderGet => (OrderGetRequest, OrderGetResult, "order.get"),
- OrderList => (OrderListRequest, OrderListResult, "order.list"),
- OrderAppList => (OrderAppListRequest, OrderAppListResult, "order.app.list"),
- OrderAppExport => (OrderAppExportRequest, OrderAppExportResult, "order.app.export"),
- OrderRebind => (OrderRebindRequest, OrderRebindResult, "order.rebind"),
- OrderAccept => (OrderAcceptRequest, OrderAcceptResult, "order.accept"),
- OrderDecline => (OrderDeclineRequest, OrderDeclineResult, "order.decline"),
- OrderCancel => (OrderCancelRequest, OrderCancelResult, "order.cancel"),
- OrderRevisionPropose => (OrderRevisionProposeRequest, OrderRevisionProposeResult, "order.revision.propose"),
- OrderRevisionAccept => (OrderRevisionAcceptRequest, OrderRevisionAcceptResult, "order.revision.accept"),
- OrderRevisionDecline => (OrderRevisionDeclineRequest, OrderRevisionDeclineResult, "order.revision.decline"),
- OrderStatusGet => (OrderStatusGetRequest, OrderStatusGetResult, "order.status.get"),
- OrderEventList => (OrderEventListRequest, OrderEventListResult, "order.event.list"),
- OrderEventWatch => (OrderEventWatchRequest, OrderEventWatchResult, "order.event.watch"),
+ TradeSubmit => (TradeSubmitRequest, TradeSubmitResult, "trade.submit"),
+ TradeGet => (TradeGetRequest, TradeGetResult, "trade.get"),
+ TradeList => (TradeListRequest, TradeListResult, "trade.list"),
+ TradeAppList => (TradeAppListRequest, TradeAppListResult, "trade.app.list"),
+ TradeAppExport => (TradeAppExportRequest, TradeAppExportResult, "trade.app.export"),
+ TradeRebind => (TradeRebindRequest, TradeRebindResult, "trade.rebind"),
+ TradeAccept => (TradeAcceptRequest, TradeAcceptResult, "trade.accept"),
+ TradeDecline => (TradeDeclineRequest, TradeDeclineResult, "trade.decline"),
+ TradeCancel => (TradeCancelRequest, TradeCancelResult, "trade.cancel"),
+ TradeRevisionPropose => (TradeRevisionProposeRequest, TradeRevisionProposeResult, "trade.revision.propose"),
+ TradeRevisionAccept => (TradeRevisionAcceptRequest, TradeRevisionAcceptResult, "trade.revision.accept"),
+ TradeRevisionDecline => (TradeRevisionDeclineRequest, TradeRevisionDeclineResult, "trade.revision.decline"),
+ TradeStatusGet => (TradeStatusGetRequest, TradeStatusGetResult, "trade.status.get"),
+ TradeEventList => (TradeEventListRequest, TradeEventListResult, "trade.event.list"),
+ TradeEventWatch => (TradeEventWatchRequest, TradeEventWatchResult, "trade.event.watch"),
ValidationReceiptGet => (ValidationReceiptGetRequest, ValidationReceiptGetResult, "validation.receipt.get"),
ValidationReceiptList => (ValidationReceiptListRequest, ValidationReceiptListResult, "validation.receipt.list"),
ValidationReceiptVerify => (ValidationReceiptVerifyRequest, ValidationReceiptVerifyResult, "validation.receipt.verify"),
diff --git a/src/out/envelope.rs b/src/out/envelope.rs
@@ -180,7 +180,7 @@ fn output_resource_from_value(value: &Value) -> Option<OutputResource> {
"listing",
"basket",
"quote",
- "order",
+ "trade",
];
nested_fields
.into_iter()
@@ -198,6 +198,7 @@ fn nested_output_resource(field: &str, value: &Value) -> Option<OutputResource>
if resource.kind == "resource" {
resource.kind = match field {
"resolved_account" | "default_account" | "bound_account" => "account",
+ "order" => "trade",
other => other,
}
.to_owned();
@@ -232,7 +233,7 @@ fn output_resource_from_fields(object: &serde_json::Map<String, Value>) -> Optio
("listing_address", "listing"),
("listing_addr", "listing"),
("basket_id", "basket"),
- ("order_id", "order"),
+ ("order_id", "trade"),
]
.into_iter()
.find_map(|(field, kind)| {
@@ -550,14 +551,14 @@ mod tests {
CliExitCode::ApprovalRequiredOrDenied,
);
let envelope = OutputEnvelope::failure(
- "order.submit",
+ "trade.submit",
error,
EnvelopeContext::new("req_order", false),
);
let value = serde_json::to_value(envelope).expect("serialize envelope");
assert_eq!(value["schema_version"], OUTPUT_SCHEMA_VERSION);
- assert_eq!(value["operation_id"], "order.submit");
+ assert_eq!(value["operation_id"], "trade.submit");
assert_eq!(value["status"], "error");
assert_eq!(value["reason_code"], "approval_required");
assert_eq!(value["result"], Value::Null);
@@ -575,22 +576,22 @@ mod tests {
);
error.detail = Some(json!({
"actions": [
- "radroots order list",
+ "radroots trade list",
"run radroots basket create"
]
}));
let envelope = OutputEnvelope::failure(
- "order.submit",
+ "trade.submit",
error,
EnvelopeContext::new("req_order", true),
);
assert_eq!(envelope.next_actions.len(), 2);
assert_eq!(envelope.next_actions[0].kind, NextActionKind::CliCommand);
- assert_eq!(envelope.next_actions[0].label, "order list");
+ assert_eq!(envelope.next_actions[0].label, "trade list");
assert_eq!(
envelope.next_actions[0].command.as_deref(),
- Some("radroots order list")
+ Some("radroots trade list")
);
assert_eq!(envelope.next_actions[1].kind, NextActionKind::CliCommand);
assert_eq!(envelope.next_actions[1].label, "basket create");
@@ -714,7 +715,7 @@ mod tests {
assert_eq!(frames[0].payload["output_format"], "human");
assert_eq!(frames[1].payload["status"], "error");
assert_eq!(frames[1].payload["reason_code"], "not_implemented");
- assert_eq!(frames[1].payload["resource"]["kind"], "order");
+ assert_eq!(frames[1].payload["resource"]["kind"], "trade");
assert_eq!(frames[1].payload["resource"]["id"], "ord_test");
assert_eq!(frames[1].errors[0].reason_code, "not_implemented");
}
diff --git a/src/registry/farm.rs b/src/registry/farm.rs
@@ -64,14 +64,14 @@ pub const FARM_PROFILE_UPDATE: OperationSpec = operation!(
true
);
-pub const FARM_LOCATION_UPDATE: OperationSpec = operation!(
- "farm.location.update",
- "radroots farm location update",
+pub const FARM_LOCATION_SET: OperationSpec = operation!(
+ "farm.location.set",
+ "radroots farm location set",
"farm",
- "farm_location_update",
- "FarmLocationUpdateRequest",
- "FarmLocationUpdateResult",
- "Update farm location fields.",
+ "farm_location_set",
+ "FarmLocationSetRequest",
+ "FarmLocationSetResult",
+ "Set private exact farm location and derived public locality.",
Seller,
true,
Conditional,
@@ -80,6 +80,38 @@ pub const FARM_LOCATION_UPDATE: OperationSpec = operation!(
true
);
+pub const FARM_LOCATION_GET: OperationSpec = operation!(
+ "farm.location.get",
+ "radroots farm location get",
+ "farm",
+ "farm_location_get",
+ "FarmLocationGetRequest",
+ "FarmLocationGetResult",
+ "Get private exact farm location for the configured farm.",
+ Seller,
+ false,
+ None,
+ Low,
+ false,
+ false
+);
+
+pub const FARM_LOCATION_CLEAR: OperationSpec = operation!(
+ "farm.location.clear",
+ "radroots farm location clear",
+ "farm",
+ "farm_location_clear",
+ "FarmLocationClearRequest",
+ "FarmLocationClearResult",
+ "Clear private exact farm location for the configured farm.",
+ Seller,
+ true,
+ Required,
+ High,
+ false,
+ true
+);
+
pub const FARM_FULFILLMENT_UPDATE: OperationSpec = operation!(
"farm.fulfillment.update",
"radroots farm fulfillment update",
diff --git a/src/registry/mod.rs b/src/registry/mod.rs
@@ -88,11 +88,11 @@ mod farm;
mod health;
mod listing;
mod market;
-mod order;
mod relay;
mod signer;
mod store;
mod sync;
+mod trade;
mod validation;
mod workspace;
@@ -126,7 +126,9 @@ pub const OPERATION_REGISTRY: &[OperationSpec] = &[
farm::FARM_GET,
farm::FARM_REBIND,
farm::FARM_PROFILE_UPDATE,
- farm::FARM_LOCATION_UPDATE,
+ farm::FARM_LOCATION_SET,
+ farm::FARM_LOCATION_GET,
+ farm::FARM_LOCATION_CLEAR,
farm::FARM_FULFILLMENT_UPDATE,
farm::FARM_READINESS_CHECK,
farm::FARM_PUBLISH,
@@ -153,21 +155,21 @@ pub const OPERATION_REGISTRY: &[OperationSpec] = &[
basket::BASKET_ADJUSTMENT_REMOVE,
basket::BASKET_VALIDATE,
basket::BASKET_QUOTE_CREATE,
- order::ORDER_SUBMIT,
- order::ORDER_GET,
- order::ORDER_LIST,
- order::ORDER_APP_LIST,
- order::ORDER_APP_EXPORT,
- order::ORDER_REBIND,
- order::ORDER_ACCEPT,
- order::ORDER_DECLINE,
- order::ORDER_CANCEL,
- order::ORDER_REVISION_PROPOSE,
- order::ORDER_REVISION_ACCEPT,
- order::ORDER_REVISION_DECLINE,
- order::ORDER_STATUS_GET,
- order::ORDER_EVENT_LIST,
- order::ORDER_EVENT_WATCH,
+ trade::TRADE_SUBMIT,
+ trade::TRADE_GET,
+ trade::TRADE_LIST,
+ trade::TRADE_APP_LIST,
+ trade::TRADE_APP_EXPORT,
+ trade::TRADE_REBIND,
+ trade::TRADE_ACCEPT,
+ trade::TRADE_DECLINE,
+ trade::TRADE_CANCEL,
+ trade::TRADE_REVISION_PROPOSE,
+ trade::TRADE_REVISION_ACCEPT,
+ trade::TRADE_REVISION_DECLINE,
+ trade::TRADE_STATUS_GET,
+ trade::TRADE_EVENT_LIST,
+ trade::TRADE_EVENT_WATCH,
validation::VALIDATION_RECEIPT_GET,
validation::VALIDATION_RECEIPT_LIST,
validation::VALIDATION_RECEIPT_VERIFY,
@@ -189,19 +191,19 @@ pub fn network_requirement(operation_id: &str) -> NetworkRequirement {
| "listing.publish"
| "listing.update"
| "listing.archive"
- | "order.submit"
- | "order.event.list"
+ | "trade.submit"
+ | "trade.event.list"
| "validation.receipt.get"
| "validation.receipt.list"
| "validation.receipt.verify" => NetworkRequirement::External {
dry_run_requires_network: false,
},
- "order.accept"
- | "order.decline"
- | "order.cancel"
- | "order.revision.propose"
- | "order.revision.accept"
- | "order.revision.decline" => NetworkRequirement::External {
+ "trade.accept"
+ | "trade.decline"
+ | "trade.cancel"
+ | "trade.revision.propose"
+ | "trade.revision.accept"
+ | "trade.revision.decline" => NetworkRequirement::External {
dry_run_requires_network: true,
},
_ => NetworkRequirement::Local,
@@ -212,13 +214,13 @@ pub fn requires_local_signer_mode(operation_id: &str) -> bool {
matches!(
operation_id,
"sync.push"
- | "order.submit"
- | "order.accept"
- | "order.decline"
- | "order.cancel"
- | "order.revision.propose"
- | "order.revision.accept"
- | "order.revision.decline"
+ | "trade.submit"
+ | "trade.accept"
+ | "trade.decline"
+ | "trade.cancel"
+ | "trade.revision.propose"
+ | "trade.revision.accept"
+ | "trade.revision.decline"
)
}
@@ -231,13 +233,13 @@ pub fn requires_direct_nostr_relay_publish_transport(operation_id: &str) -> bool
| "listing.publish"
| "listing.update"
| "listing.archive"
- | "order.submit"
- | "order.accept"
- | "order.decline"
- | "order.cancel"
- | "order.revision.propose"
- | "order.revision.accept"
- | "order.revision.decline"
+ | "trade.submit"
+ | "trade.accept"
+ | "trade.decline"
+ | "trade.cancel"
+ | "trade.revision.propose"
+ | "trade.revision.accept"
+ | "trade.revision.decline"
)
}
@@ -290,7 +292,9 @@ mod tests {
"farm.get",
"farm.rebind",
"farm.profile.update",
- "farm.location.update",
+ "farm.location.set",
+ "farm.location.get",
+ "farm.location.clear",
"farm.fulfillment.update",
"farm.readiness.check",
"farm.publish",
@@ -317,21 +321,21 @@ mod tests {
"basket.adjustment.remove",
"basket.validate",
"basket.quote.create",
- "order.submit",
- "order.get",
- "order.list",
- "order.app.list",
- "order.app.export",
- "order.rebind",
- "order.accept",
- "order.decline",
- "order.cancel",
- "order.revision.propose",
- "order.revision.accept",
- "order.revision.decline",
- "order.status.get",
- "order.event.list",
- "order.event.watch",
+ "trade.submit",
+ "trade.get",
+ "trade.list",
+ "trade.app.list",
+ "trade.app.export",
+ "trade.rebind",
+ "trade.accept",
+ "trade.decline",
+ "trade.cancel",
+ "trade.revision.propose",
+ "trade.revision.accept",
+ "trade.revision.decline",
+ "trade.status.get",
+ "trade.event.list",
+ "trade.event.watch",
"validation.receipt.get",
"validation.receipt.list",
"validation.receipt.verify",
@@ -353,7 +357,8 @@ mod tests {
"farm.create",
"farm.rebind",
"farm.profile.update",
- "farm.location.update",
+ "farm.location.set",
+ "farm.location.clear",
"farm.fulfillment.update",
"farm.publish",
"listing.create",
@@ -370,15 +375,15 @@ mod tests {
"basket.adjustment.add",
"basket.adjustment.remove",
"basket.quote.create",
- "order.submit",
- "order.app.export",
- "order.rebind",
- "order.accept",
- "order.decline",
- "order.cancel",
- "order.revision.propose",
- "order.revision.accept",
- "order.revision.decline",
+ "trade.submit",
+ "trade.app.export",
+ "trade.rebind",
+ "trade.accept",
+ "trade.decline",
+ "trade.cancel",
+ "trade.revision.propose",
+ "trade.revision.accept",
+ "trade.revision.decline",
];
const INTENTIONALLY_UNSUPPORTED_MUTATING_DRY_RUN_OPERATION_IDS: &[&str] = &[];
@@ -391,7 +396,7 @@ mod tests {
.copied()
.collect::<BTreeSet<_>>();
assert_eq!(actual, expected);
- assert_eq!(OPERATION_REGISTRY.len(), 74);
+ assert_eq!(OPERATION_REGISTRY.len(), 76);
}
#[test]
@@ -437,19 +442,20 @@ mod tests {
"account.remove",
"sync.push",
"farm.rebind",
+ "farm.location.clear",
"farm.publish",
"listing.rebind",
"listing.publish",
"listing.update",
"listing.archive",
- "order.submit",
- "order.rebind",
- "order.accept",
- "order.decline",
- "order.cancel",
- "order.revision.propose",
- "order.revision.accept",
- "order.revision.decline",
+ "trade.submit",
+ "trade.rebind",
+ "trade.accept",
+ "trade.decline",
+ "trade.cancel",
+ "trade.revision.propose",
+ "trade.revision.accept",
+ "trade.revision.decline",
]
.into_iter()
.collect::<BTreeSet<_>>();
@@ -515,8 +521,8 @@ mod tests {
"market.refresh",
"market.product.search",
"basket.list",
- "order.list",
- "order.event.list",
+ "trade.list",
+ "trade.event.list",
"validation.receipt.list",
]
.into_iter()
@@ -546,14 +552,14 @@ mod tests {
"listing.publish",
"listing.update",
"listing.archive",
- "order.submit",
- "order.accept",
- "order.decline",
- "order.cancel",
- "order.revision.propose",
- "order.revision.accept",
- "order.revision.decline",
- "order.event.list",
+ "trade.submit",
+ "trade.accept",
+ "trade.decline",
+ "trade.cancel",
+ "trade.revision.propose",
+ "trade.revision.accept",
+ "trade.revision.decline",
+ "trade.event.list",
"validation.receipt.get",
"validation.receipt.list",
"validation.receipt.verify",
@@ -573,13 +579,13 @@ mod tests {
.collect::<BTreeSet<_>>();
let expected = [
"sync.push",
- "order.submit",
- "order.accept",
- "order.decline",
- "order.cancel",
- "order.revision.propose",
- "order.revision.accept",
- "order.revision.decline",
+ "trade.submit",
+ "trade.accept",
+ "trade.decline",
+ "trade.cancel",
+ "trade.revision.propose",
+ "trade.revision.accept",
+ "trade.revision.decline",
]
.into_iter()
.collect::<BTreeSet<_>>();
@@ -602,13 +608,13 @@ mod tests {
"listing.publish",
"listing.update",
"listing.archive",
- "order.submit",
- "order.accept",
- "order.decline",
- "order.cancel",
- "order.revision.propose",
- "order.revision.accept",
- "order.revision.decline",
+ "trade.submit",
+ "trade.accept",
+ "trade.decline",
+ "trade.cancel",
+ "trade.revision.propose",
+ "trade.revision.accept",
+ "trade.revision.decline",
]
.into_iter()
.collect::<BTreeSet<_>>();
@@ -642,7 +648,7 @@ mod tests {
OperationRole::Buyer
);
assert_eq!(
- get_operation("order.list").unwrap().role,
+ get_operation("trade.list").unwrap().role,
OperationRole::Any
);
}
diff --git a/src/registry/order.rs b/src/registry/order.rs
@@ -1,241 +0,0 @@
-use super::{ApprovalPolicy, OperationRole, OperationSpec, RiskLevel};
-
-pub const ORDER_SUBMIT: OperationSpec = operation!(
- "order.submit",
- "radroots order submit",
- "order",
- "order_submit",
- "OrderSubmitRequest",
- "OrderSubmitResult",
- "Submit quoted basket as an order.",
- Buyer,
- true,
- Required,
- Critical,
- false,
- true
-);
-
-pub const ORDER_GET: OperationSpec = operation!(
- "order.get",
- "radroots order get",
- "order",
- "order_get",
- "OrderGetRequest",
- "OrderGetResult",
- "Get order details.",
- Buyer,
- false,
- None,
- Low,
- false,
- false
-);
-
-pub const ORDER_LIST: OperationSpec = operation!(
- "order.list",
- "radroots order list",
- "order",
- "order_list",
- "OrderListRequest",
- "OrderListResult",
- "List orders.",
- Any,
- false,
- None,
- Low,
- true,
- false
-);
-
-pub const ORDER_APP_LIST: OperationSpec = operation!(
- "order.app.list",
- "radroots order app list",
- "order",
- "order_app_list",
- "OrderAppListRequest",
- "OrderAppListResult",
- "List app-authored shared local order records.",
- Buyer,
- false,
- None,
- Low,
- false,
- false
-);
-
-pub const ORDER_APP_EXPORT: OperationSpec = operation!(
- "order.app.export",
- "radroots order app export",
- "order",
- "order_app_export",
- "OrderAppExportRequest",
- "OrderAppExportResult",
- "Export an app-authored shared order record as a CLI draft.",
- Buyer,
- true,
- None,
- Medium,
- false,
- true
-);
-
-pub const ORDER_REBIND: OperationSpec = operation!(
- "order.rebind",
- "radroots order rebind",
- "order",
- "order_rebind",
- "OrderRebindRequest",
- "OrderRebindResult",
- "Rebind a local order draft to an explicit buyer actor.",
- Buyer,
- true,
- Required,
- High,
- false,
- true
-);
-
-pub const ORDER_ACCEPT: OperationSpec = operation!(
- "order.accept",
- "radroots order accept",
- "order",
- "order_accept",
- "OrderAcceptRequest",
- "OrderAcceptResult",
- "Accept a buyer order request.",
- Seller,
- true,
- Required,
- Critical,
- false,
- true
-);
-
-pub const ORDER_DECLINE: OperationSpec = operation!(
- "order.decline",
- "radroots order decline",
- "order",
- "order_decline",
- "OrderDeclineRequest",
- "OrderDeclineResult",
- "Decline a buyer order request.",
- Seller,
- true,
- Required,
- High,
- false,
- true
-);
-
-pub const ORDER_CANCEL: OperationSpec = operation!(
- "order.cancel",
- "radroots order cancel",
- "order",
- "order_cancel",
- "OrderCancelRequest",
- "OrderCancelResult",
- "Withdraw a buyer order before agreement finalization.",
- Buyer,
- true,
- Required,
- High,
- false,
- true
-);
-
-pub const ORDER_REVISION_PROPOSE: OperationSpec = operation!(
- "order.revision.propose",
- "radroots order revision propose",
- "order",
- "order_revision_propose",
- "OrderRevisionProposeRequest",
- "OrderRevisionProposeResult",
- "Propose seller-authored order revision.",
- Seller,
- true,
- Required,
- High,
- false,
- true
-);
-
-pub const ORDER_REVISION_ACCEPT: OperationSpec = operation!(
- "order.revision.accept",
- "radroots order revision accept",
- "order",
- "order_revision_accept",
- "OrderRevisionAcceptRequest",
- "OrderRevisionAcceptResult",
- "Accept a seller-authored order revision.",
- Buyer,
- true,
- Required,
- High,
- false,
- true
-);
-
-pub const ORDER_REVISION_DECLINE: OperationSpec = operation!(
- "order.revision.decline",
- "radroots order revision decline",
- "order",
- "order_revision_decline",
- "OrderRevisionDeclineRequest",
- "OrderRevisionDeclineResult",
- "Decline a seller-authored order revision.",
- Buyer,
- true,
- Required,
- High,
- false,
- true
-);
-
-pub const ORDER_STATUS_GET: OperationSpec = operation!(
- "order.status.get",
- "radroots order status get",
- "order",
- "order_status_get",
- "OrderStatusGetRequest",
- "OrderStatusGetResult",
- "Get reducer-derived order status.",
- Any,
- false,
- None,
- Low,
- false,
- false
-);
-
-pub const ORDER_EVENT_LIST: OperationSpec = operation!(
- "order.event.list",
- "radroots order event list",
- "order",
- "order_event_list",
- "OrderEventListRequest",
- "OrderEventListResult",
- "List order events.",
- Any,
- false,
- None,
- Low,
- true,
- false
-);
-
-pub const ORDER_EVENT_WATCH: OperationSpec = operation!(
- "order.event.watch",
- "radroots order event watch",
- "order",
- "order_event_watch",
- "OrderEventWatchRequest",
- "OrderEventWatchResult",
- "Report deferred order event watch status.",
- Any,
- false,
- None,
- Low,
- false,
- false
-);
diff --git a/src/registry/trade.rs b/src/registry/trade.rs
@@ -0,0 +1,241 @@
+use super::{ApprovalPolicy, OperationRole, OperationSpec, RiskLevel};
+
+pub const TRADE_SUBMIT: OperationSpec = operation!(
+ "trade.submit",
+ "radroots trade submit",
+ "trade",
+ "trade_submit",
+ "TradeSubmitRequest",
+ "TradeSubmitResult",
+ "Submit quoted basket as a trade.",
+ Buyer,
+ true,
+ Required,
+ Critical,
+ false,
+ true
+);
+
+pub const TRADE_GET: OperationSpec = operation!(
+ "trade.get",
+ "radroots trade get",
+ "trade",
+ "trade_get",
+ "TradeGetRequest",
+ "TradeGetResult",
+ "Get trade details.",
+ Buyer,
+ false,
+ None,
+ Low,
+ false,
+ false
+);
+
+pub const TRADE_LIST: OperationSpec = operation!(
+ "trade.list",
+ "radroots trade list",
+ "trade",
+ "trade_list",
+ "TradeListRequest",
+ "TradeListResult",
+ "List trades.",
+ Any,
+ false,
+ None,
+ Low,
+ true,
+ false
+);
+
+pub const TRADE_APP_LIST: OperationSpec = operation!(
+ "trade.app.list",
+ "radroots trade app list",
+ "trade",
+ "trade_app_list",
+ "TradeAppListRequest",
+ "TradeAppListResult",
+ "List app-authored shared local trade records.",
+ Buyer,
+ false,
+ None,
+ Low,
+ false,
+ false
+);
+
+pub const TRADE_APP_EXPORT: OperationSpec = operation!(
+ "trade.app.export",
+ "radroots trade app export",
+ "trade",
+ "trade_app_export",
+ "TradeAppExportRequest",
+ "TradeAppExportResult",
+ "Export an app-authored shared trade record as a CLI draft.",
+ Buyer,
+ true,
+ None,
+ Medium,
+ false,
+ true
+);
+
+pub const TRADE_REBIND: OperationSpec = operation!(
+ "trade.rebind",
+ "radroots trade rebind",
+ "trade",
+ "trade_rebind",
+ "TradeRebindRequest",
+ "TradeRebindResult",
+ "Rebind a local trade draft to an explicit buyer actor.",
+ Buyer,
+ true,
+ Required,
+ High,
+ false,
+ true
+);
+
+pub const TRADE_ACCEPT: OperationSpec = operation!(
+ "trade.accept",
+ "radroots trade accept",
+ "trade",
+ "trade_accept",
+ "TradeAcceptRequest",
+ "TradeAcceptResult",
+ "Accept a buyer trade request.",
+ Seller,
+ true,
+ Required,
+ Critical,
+ false,
+ true
+);
+
+pub const TRADE_DECLINE: OperationSpec = operation!(
+ "trade.decline",
+ "radroots trade decline",
+ "trade",
+ "trade_decline",
+ "TradeDeclineRequest",
+ "TradeDeclineResult",
+ "Decline a buyer trade request.",
+ Seller,
+ true,
+ Required,
+ High,
+ false,
+ true
+);
+
+pub const TRADE_CANCEL: OperationSpec = operation!(
+ "trade.cancel",
+ "radroots trade cancel",
+ "trade",
+ "trade_cancel",
+ "TradeCancelRequest",
+ "TradeCancelResult",
+ "Withdraw a buyer trade before agreement finalization.",
+ Buyer,
+ true,
+ Required,
+ High,
+ false,
+ true
+);
+
+pub const TRADE_REVISION_PROPOSE: OperationSpec = operation!(
+ "trade.revision.propose",
+ "radroots trade revision propose",
+ "trade",
+ "trade_revision_propose",
+ "TradeRevisionProposeRequest",
+ "TradeRevisionProposeResult",
+ "Propose seller-authored trade revision.",
+ Seller,
+ true,
+ Required,
+ High,
+ false,
+ true
+);
+
+pub const TRADE_REVISION_ACCEPT: OperationSpec = operation!(
+ "trade.revision.accept",
+ "radroots trade revision accept",
+ "trade",
+ "trade_revision_accept",
+ "TradeRevisionAcceptRequest",
+ "TradeRevisionAcceptResult",
+ "Accept a seller-authored trade revision.",
+ Buyer,
+ true,
+ Required,
+ High,
+ false,
+ true
+);
+
+pub const TRADE_REVISION_DECLINE: OperationSpec = operation!(
+ "trade.revision.decline",
+ "radroots trade revision decline",
+ "trade",
+ "trade_revision_decline",
+ "TradeRevisionDeclineRequest",
+ "TradeRevisionDeclineResult",
+ "Decline a seller-authored trade revision.",
+ Buyer,
+ true,
+ Required,
+ High,
+ false,
+ true
+);
+
+pub const TRADE_STATUS_GET: OperationSpec = operation!(
+ "trade.status.get",
+ "radroots trade status get",
+ "trade",
+ "trade_status_get",
+ "TradeStatusGetRequest",
+ "TradeStatusGetResult",
+ "Get reducer-derived trade status.",
+ Any,
+ false,
+ None,
+ Low,
+ false,
+ false
+);
+
+pub const TRADE_EVENT_LIST: OperationSpec = operation!(
+ "trade.event.list",
+ "radroots trade event list",
+ "trade",
+ "trade_event_list",
+ "TradeEventListRequest",
+ "TradeEventListResult",
+ "List trade events.",
+ Any,
+ false,
+ None,
+ Low,
+ true,
+ false
+);
+
+pub const TRADE_EVENT_WATCH: OperationSpec = operation!(
+ "trade.event.watch",
+ "radroots trade event watch",
+ "trade",
+ "trade_event_watch",
+ "TradeEventWatchRequest",
+ "TradeEventWatchResult",
+ "Report deferred trade event watch status.",
+ Any,
+ false,
+ None,
+ Low,
+ false,
+ false
+);
diff --git a/src/runtime/config.rs b/src/runtime/config.rs
@@ -2103,6 +2103,7 @@ mod tests {
),
workspace_config_path: None,
app_data_root: PathBuf::from("/home/tester/.radroots/data/apps/cli"),
+ shared_cache_root: PathBuf::from("/home/tester/.radroots/cache"),
app_logs_root: PathBuf::from("/home/tester/.radroots/logs/apps/cli"),
shared_accounts_data_root: PathBuf::from(
"/home/tester/.radroots/data/shared/accounts"
diff --git a/src/runtime/farm.rs b/src/runtime/farm.rs
@@ -4,21 +4,23 @@ use std::time::{SystemTime, UNIX_EPOCH};
use radroots_authority::RadrootsActorContext;
use radroots_events::contract::RadrootsActorRole;
use radroots_events::farm::{RadrootsFarm, RadrootsFarmPublicLocation};
+use radroots_events::ids::RadrootsAddressableCoordinate;
use radroots_events::kinds::{KIND_FARM, KIND_PROFILE};
use radroots_events::listing::RadrootsListingPublicLocation;
use radroots_events::profile::{RadrootsProfile, RadrootsProfileType};
use radroots_events_codec::d_tag::is_d_tag_base64url;
use radroots_events_codec::profile::encode::to_wire_parts_with_profile_type;
use radroots_sdk::{
- FarmEnqueuePublishRequest, FarmEnqueueReceipt, FarmPreparePublishRequest, FarmPublishPlan,
- PushOutboxEventReceipt, PushOutboxEventState, PushOutboxReceipt, PushOutboxRelayOutcomeKind,
- PushOutboxRequest, SdkMutationState,
+ FarmEnqueuePublishRequest, FarmEnqueueReceipt, FarmPreparePublishRequest,
+ FarmPrivateLocationClearRequest, FarmPrivateLocationReceipt, FarmPrivateLocationUpsertRequest,
+ FarmPublishPlan, PushOutboxEventReceipt, PushOutboxEventState, PushOutboxReceipt,
+ PushOutboxRelayOutcomeKind, PushOutboxRequest, SdkExactLocation, SdkMutationState,
};
use serde_json::json;
use crate::cli::global::{
- FarmCreateArgs, FarmFieldArg, FarmPublishArgs, FarmRebindArgs, FarmScopeArg, FarmScopedArgs,
- FarmUpdateArgs,
+ FarmCreateArgs, FarmFieldArg, FarmPrivateLocationKeyArgs, FarmPrivateLocationSetArgs,
+ FarmPublishArgs, FarmRebindArgs, FarmScopeArg, FarmScopedArgs, FarmUpdateArgs,
};
use crate::runtime::RuntimeError;
use crate::runtime::account::{self, AccountRecordView};
@@ -35,6 +37,7 @@ use crate::runtime::sdk::{
use crate::runtime::signer::ActorWriteBindingError;
use crate::view::runtime::{
FarmConfigDocumentView, FarmConfigSummaryView, FarmGetView, FarmListingDefaultsView,
+ FarmPrivateExactLocationView, FarmPrivateLocationView, FarmPrivatePublicLocalityView,
FarmPublicationView, FarmPublishComponentView, FarmPublishEventView, FarmPublishView,
FarmRebindView, FarmSelectionView, FarmSetView, FarmSetupView, FarmStatusView,
RelayFailureView,
@@ -45,6 +48,7 @@ const FARM_SELLER_ACTOR_SOURCE: &str = "farm_config";
const SDK_FARM_WRITE_SOURCE: &str = "SDK farm publish · configured signer";
const SDK_PROFILE_NOT_SUBMITTED_METHOD: &str = "sdk.farm.profile.not_submitted";
const SDK_FARM_PUBLISH_METHOD: &str = "sdk.farm.publish.v1";
+const SDK_FARM_PRIVATE_LOCATION_SOURCE: &str = "SDK private farm location · local private store";
const SDK_PROFILE_NOT_SUBMITTED_REASON: &str =
"profile publish is not part of SDK farm.publish.v1; profile draft was not submitted";
@@ -338,6 +342,94 @@ pub fn set_preflight(
})
}
+pub fn private_location_set(
+ config: &RuntimeConfig,
+ args: &FarmPrivateLocationSetArgs,
+) -> Result<FarmPrivateLocationView, CliSdkAdapterError> {
+ let target = match private_location_target(config, args.farm_d_tag.as_deref())? {
+ Some(target) => target,
+ None => return Ok(private_location_unconfigured_view()),
+ };
+ let session = CliSdkSession::connect(config)?;
+ let geonames_status = session.sdk().geonames().ensure()?;
+ let request = FarmPrivateLocationUpsertRequest::new(
+ target.actor.clone(),
+ target.farm_d_tag.clone(),
+ SdkExactLocation::new(args.latitude, args.longitude),
+ );
+ let receipt = session.block_on(session.sdk().farms().upsert_private_location(request))?;
+ Ok(private_location_view_from_receipt(
+ "set",
+ target,
+ Some(receipt),
+ Some(geonames_status.path.display().to_string()),
+ None,
+ ))
+}
+
+pub fn private_location_get(
+ config: &RuntimeConfig,
+ args: &FarmPrivateLocationKeyArgs,
+) -> Result<FarmPrivateLocationView, CliSdkAdapterError> {
+ let target = match private_location_target(config, args.farm_d_tag.as_deref())? {
+ Some(target) => target,
+ None => return Ok(private_location_unconfigured_view()),
+ };
+ let session = CliSdkSession::connect(config)?;
+ let receipt = session.block_on(session.sdk().farms().private_location(&target.farm_addr))?;
+ let state = if receipt.is_some() {
+ "ready"
+ } else {
+ "missing"
+ };
+ let reason = receipt
+ .is_none()
+ .then(|| "no private exact farm location is stored for this farm".to_owned());
+ Ok(private_location_view_from_receipt(
+ state, target, receipt, None, reason,
+ ))
+}
+
+pub fn private_location_clear(
+ config: &RuntimeConfig,
+ args: &FarmPrivateLocationKeyArgs,
+) -> Result<FarmPrivateLocationView, CliSdkAdapterError> {
+ let target = match private_location_target(config, args.farm_d_tag.as_deref())? {
+ Some(target) => target,
+ None => return Ok(private_location_unconfigured_view()),
+ };
+ let session = CliSdkSession::connect(config)?;
+ let request =
+ FarmPrivateLocationClearRequest::new(target.actor.clone(), target.farm_d_tag.clone());
+ let receipt = session.block_on(session.sdk().farms().clear_private_location(request))?;
+ Ok(FarmPrivateLocationView {
+ state: if receipt.cleared {
+ "cleared"
+ } else {
+ "missing"
+ }
+ .to_owned(),
+ source: SDK_FARM_PRIVATE_LOCATION_SOURCE.to_owned(),
+ farm_addr: Some(receipt.farm_addr.to_string()),
+ farm_d_tag: Some(target.farm_d_tag),
+ seller_account_id: Some(target.seller_account_id),
+ seller_pubkey: Some(target.seller_pubkey),
+ exact_location: None,
+ public_locality: None,
+ geonames_feature_id: None,
+ geonames_country_id: None,
+ geonames_database_path: None,
+ cleared: Some(receipt.cleared),
+ reason: (!receipt.cleared)
+ .then(|| "no private exact farm location was stored for this farm".to_owned()),
+ actions: if receipt.cleared {
+ Vec::new()
+ } else {
+ vec!["radroots farm location set --lat <lat> --lng <lng>".to_owned()]
+ },
+ })
+}
+
pub fn status(
config: &RuntimeConfig,
args: &FarmScopedArgs,
@@ -741,6 +833,15 @@ struct SdkFarmPublishInput {
}
#[derive(Debug, Clone)]
+struct PrivateFarmLocationTarget {
+ actor: RadrootsActorContext,
+ farm_addr: RadrootsAddressableCoordinate,
+ farm_d_tag: String,
+ seller_account_id: String,
+ seller_pubkey: String,
+}
+
+#[derive(Debug, Clone)]
struct FarmPublishPreviews {
profile: FarmPublishEventDraft,
}
@@ -1243,6 +1344,42 @@ fn selected_account_for_draft(
account::resolve_account(config)
}
+fn private_location_target(
+ config: &RuntimeConfig,
+ farm_d_tag: Option<&str>,
+) -> Result<Option<PrivateFarmLocationTarget>, RuntimeError> {
+ let resolved_scope = farm_config::resolve_scope(&config.paths, None)?;
+ let Some(resolved) = farm_config::load(config, Some(resolved_scope))? else {
+ return Ok(None);
+ };
+ let Some(account) = configured_account(config, &resolved.document.selection.account)? else {
+ return Ok(None);
+ };
+ let seller_pubkey = account.record.public_identity.public_key_hex.clone();
+ let seller_account_id = account.record.account_id.to_string();
+ let farm_d_tag = farm_d_tag
+ .map(str::to_owned)
+ .unwrap_or_else(|| resolved.document.selection.farm_d_tag.clone());
+ let actor = RadrootsActorContext::local_account(
+ seller_pubkey.as_str(),
+ seller_account_id.clone(),
+ [RadrootsActorRole::Farmer],
+ )
+ .map_err(|error| {
+ RuntimeError::Config(format!("invalid farm private location actor: {error}"))
+ })?;
+ let farm_addr =
+ RadrootsAddressableCoordinate::parse(format!("{KIND_FARM}:{seller_pubkey}:{farm_d_tag}"))
+ .map_err(|error| RuntimeError::Config(format!("invalid farm address: {error}")))?;
+ Ok(Some(PrivateFarmLocationTarget {
+ actor,
+ farm_addr,
+ farm_d_tag,
+ seller_account_id,
+ seller_pubkey,
+ }))
+}
+
fn missing_selected_account_setup_view() -> FarmSetupView {
FarmSetupView {
state: "unconfigured".to_owned(),
@@ -1253,6 +1390,67 @@ fn missing_selected_account_setup_view() -> FarmSetupView {
}
}
+fn private_location_unconfigured_view() -> FarmPrivateLocationView {
+ FarmPrivateLocationView {
+ state: "unconfigured".to_owned(),
+ source: SDK_FARM_PRIVATE_LOCATION_SOURCE.to_owned(),
+ farm_addr: None,
+ farm_d_tag: None,
+ seller_account_id: None,
+ seller_pubkey: None,
+ exact_location: None,
+ public_locality: None,
+ geonames_feature_id: None,
+ geonames_country_id: None,
+ geonames_database_path: None,
+ cleared: None,
+ reason: Some("create and bind a farm before storing private location".to_owned()),
+ actions: vec![
+ "radroots account create".to_owned(),
+ "radroots farm create".to_owned(),
+ ],
+ }
+}
+
+fn private_location_view_from_receipt(
+ state: &str,
+ target: PrivateFarmLocationTarget,
+ receipt: Option<FarmPrivateLocationReceipt>,
+ geonames_database_path: Option<String>,
+ reason: Option<String>,
+) -> FarmPrivateLocationView {
+ let receipt_ref = receipt.as_ref();
+ FarmPrivateLocationView {
+ state: state.to_owned(),
+ source: SDK_FARM_PRIVATE_LOCATION_SOURCE.to_owned(),
+ farm_addr: Some(target.farm_addr.to_string()),
+ farm_d_tag: Some(target.farm_d_tag),
+ seller_account_id: Some(target.seller_account_id),
+ seller_pubkey: Some(target.seller_pubkey),
+ exact_location: receipt_ref.map(|receipt| FarmPrivateExactLocationView {
+ lat: receipt.exact_location.latitude,
+ lng: receipt.exact_location.longitude,
+ }),
+ public_locality: receipt_ref.map(|receipt| FarmPrivatePublicLocalityView {
+ primary: receipt.public_locality.primary.clone(),
+ city: receipt.public_locality.city.clone(),
+ region: receipt.public_locality.region.clone(),
+ country: receipt.public_locality.country.clone(),
+ geohash5: receipt.public_locality.geohash5.clone(),
+ }),
+ geonames_feature_id: receipt_ref.and_then(|receipt| receipt.geonames_feature_id),
+ geonames_country_id: receipt_ref.and_then(|receipt| receipt.geonames_country_id.clone()),
+ geonames_database_path,
+ cleared: None,
+ reason,
+ actions: if receipt.is_some() {
+ Vec::new()
+ } else {
+ vec!["radroots farm location set --lat <lat> --lng <lng>".to_owned()]
+ },
+ }
+}
+
fn init_document(
scope: FarmConfigScope,
account: &AccountRecordView,
diff --git a/src/runtime/farm_config.rs b/src/runtime/farm_config.rs
@@ -408,6 +408,7 @@ mod tests {
workspace_config_path: (profile == "repo_local")
.then(|| repo_local_root.join("config.toml")),
app_data_root: root.join("home/.radroots/data/apps/cli"),
+ shared_cache_root: root.join("home/.radroots/cache"),
app_logs_root: root.join("home/.radroots/logs/apps/cli"),
shared_accounts_data_root: root.join("home/.radroots/data/shared/accounts"),
shared_accounts_secrets_root: root.join("home/.radroots/secrets/shared/accounts"),
diff --git a/src/runtime/local_events.rs b/src/runtime/local_events.rs
@@ -168,6 +168,7 @@ mod tests {
),
workspace_config_path: None,
app_data_root: PathBuf::from("/repo/infra/local/runtime/radroots/data/apps/cli"),
+ shared_cache_root: PathBuf::from("/repo/infra/local/runtime/radroots/cache"),
app_logs_root: PathBuf::from("/repo/infra/local/runtime/radroots/logs/apps/cli"),
shared_accounts_data_root: PathBuf::from(shared_accounts_data_root),
shared_accounts_secrets_root: PathBuf::from(
diff --git a/src/runtime/order.rs b/src/runtime/order.rs
@@ -77,20 +77,18 @@ use radroots_sql_core::SqliteExecutor;
use radroots_trade::order::{
RadrootsListingInventoryAccountingInputs, RadrootsListingInventoryAccountingIssue,
RadrootsListingInventoryAccountingProjection, RadrootsListingInventoryBinAvailability,
- RadrootsOrderCancellationRecord, RadrootsOrderDecisionRecord, RadrootsOrderIssue,
- RadrootsOrderReductionInputs, RadrootsOrderRequestRecord, RadrootsOrderRevisionDecisionRecord,
- RadrootsOrderRevisionProposalRecord, canonicalize_order_decision_for_signer,
- canonicalize_order_request_for_signer, reduce_listing_inventory_accounting,
- reduce_order_events,
+ RadrootsOrderCancellationRecord, RadrootsOrderDecisionRecord, RadrootsOrderRequestRecord,
+ RadrootsOrderRevisionDecisionRecord, RadrootsOrderRevisionProposalRecord,
+ canonicalize_order_decision_for_signer, canonicalize_order_request_for_signer,
+ reduce_listing_inventory_accounting,
};
-use radroots_trade::workflow::RadrootsTradeWorkflowState;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use crate::cli::global::{
- OrderAppRecordExportArgs, OrderCancelArgs, OrderDecisionArg, OrderDecisionArgs,
- OrderDraftCreateArgs, OrderRebindArgs, OrderRevisionDecisionArg, OrderRevisionDecisionArgs,
- OrderRevisionProposeArgs, OrderStatusArgs, OrderSubmitArgs, RecordLookupArgs,
+ OrderDraftCreateArgs, RecordLookupArgs, TradeAppRecordExportArgs, TradeCancelArgs,
+ TradeDecisionArg, TradeDecisionArgs, TradeRebindArgs, TradeRevisionDecisionArg,
+ TradeRevisionDecisionArgs, TradeRevisionProposeArgs, TradeStatusArgs, TradeSubmitArgs,
};
use crate::runtime::RuntimeError;
use crate::runtime::account;
@@ -113,26 +111,24 @@ use crate::view::runtime::{
OrderCancellationView, OrderDecisionView, OrderDraftItemView, OrderEventListEntryView,
OrderEventListView, OrderGetView, OrderInventoryBinView, OrderInventoryView, OrderIssueView,
OrderListView, OrderNewView, OrderRebindView, OrderRevisionDecisionView,
- OrderRevisionProposalView, OrderStatusLifecycleCancellationView, OrderStatusLifecycleView,
- OrderStatusRevisionView, OrderStatusView, OrderSubmitView, OrderSummaryView, RelayFailureView,
+ OrderRevisionProposalView, OrderStatusView, OrderSubmitView, OrderSummaryView,
+ RelayFailureView,
};
use self::sdk_status::sdk_order_status_view;
const ORDER_DRAFT_KIND: &str = "order_draft_v1";
-const ORDER_SOURCE: &str = "local order drafts · local first";
-const ORDER_APP_RECORD_SOURCE: &str = "app-authored shared local order records";
-const ORDER_SUBMIT_SOURCE: &str = "SDK order submit · local key";
-const ORDER_DECISION_SOURCE: &str = "SDK order decision · local key";
-const ORDER_REVISION_PROPOSAL_SOURCE: &str = "SDK order revision proposal · local key";
-const ORDER_REVISION_DECISION_SOURCE: &str = "SDK order revision decision · local key";
-const ORDER_CANCELLATION_SOURCE: &str = "SDK order cancellation · local key";
+const ORDER_SOURCE: &str = "local trade drafts · local first";
+const ORDER_APP_RECORD_SOURCE: &str = "app-authored shared local trade records";
+const ORDER_SUBMIT_SOURCE: &str = "SDK trade submit · local key";
+const ORDER_DECISION_SOURCE: &str = "SDK trade decision · local key";
+const ORDER_REVISION_PROPOSAL_SOURCE: &str = "SDK trade revision proposal · local key";
+const ORDER_REVISION_DECISION_SOURCE: &str = "SDK trade revision decision · local key";
+const ORDER_CANCELLATION_SOURCE: &str = "SDK trade cancellation · local key";
const ORDER_EVENT_LIST_SOURCE: &str = "direct Nostr relay fetch · selected seller identity";
-const LEGACY_ORDER_PREFLIGHT_STATUS_SOURCE: &str =
- "legacy direct Nostr relay preflight status · active order reducer";
-const ORDER_STATUS_SDK_SOURCE: &str = "SDK local order projection";
+const ORDER_STATUS_SDK_SOURCE: &str = "SDK local trade projection";
const ORDER_EVENT_LIST_RELAY_ACTION: &str =
- "radroots --relay wss://relay.example.com order event list";
+ "radroots --relay wss://relay.example.com trade event list";
const ORDER_BUYER_ACTOR_SOURCE_RESOLVED_ACCOUNT: &str = "resolved_account";
const ORDER_BUYER_ACTOR_SOURCE_REBIND: &str = "order_rebind";
const ORDER_APP_RECORD_LIST_LIMIT: u32 = 500;
@@ -160,7 +156,7 @@ fn protocol_listing_addr(value: &str, field: &str) -> Result<RadrootsListingAddr
fn protocol_revision_id(value: &str, field: &str) -> Result<RadrootsOrderRevisionId, RuntimeError> {
value.parse().map_err(|error| {
- RuntimeError::Config(format!("{field} is not a valid order revision id: {error}"))
+ RuntimeError::Config(format!("{field} is not a valid trade revision id: {error}"))
})
}
@@ -200,10 +196,6 @@ fn protocol_pubkey(value: &str, field: &str) -> Result<RadrootsPublicKey, Runtim
.map_err(|error| RuntimeError::Config(format!("{field} is not a valid pubkey: {error}")))
}
-fn optional_string<T: ToString>(value: Option<T>) -> Option<String> {
- value.map(|value| value.to_string())
-}
-
fn required_order_context_event_id(
event_id: Option<RadrootsEventId>,
tag: &'static str,
@@ -503,7 +495,7 @@ pub fn scaffold(
)?
.into();
view.actions
- .insert(0, format!("radroots order get {}", view.order_id));
+ .insert(0, format!("radroots trade get {}", view.order_id));
Ok(view)
}
@@ -586,7 +578,7 @@ pub fn scaffold_preflight(
.into();
view.state = "dry_run".to_owned();
view.actions
- .insert(0, format!("radroots order get {}", view.order_id));
+ .insert(0, format!("radroots trade get {}", view.order_id));
Ok(view)
}
@@ -624,10 +616,10 @@ pub fn get(config: &RuntimeConfig, args: &RecordLookupArgs) -> Result<OrderGetVi
updated_at_unix: None,
job: None,
workflow: None,
- reason: Some(format!("order draft `{lookup}` was not found")),
+ reason: Some(format!("trade draft `{lookup}` was not found")),
issues: Vec::new(),
actions: vec![
- "radroots order list".to_owned(),
+ "radroots trade list".to_owned(),
"radroots basket create".to_owned(),
],
});
@@ -768,7 +760,7 @@ pub fn app_record_list(config: &RuntimeConfig) -> Result<OrderAppRecordListView,
pub fn app_record_export(
config: &RuntimeConfig,
- args: &OrderAppRecordExportArgs,
+ args: &TradeAppRecordExportArgs,
) -> Result<OrderAppRecordExportView, RuntimeError> {
let Some(record) = get_shared_record(config, args.record_id.as_str())? else {
return Ok(OrderAppRecordExportView {
@@ -792,10 +784,10 @@ pub fn app_record_export(
seller_pubkey: None,
issues: Vec::new(),
reason: Some(format!(
- "app-authored local order record `{}` was not found",
+ "app-authored local trade record `{}` was not found",
args.record_id
)),
- actions: vec!["radroots order app list".to_owned()],
+ actions: vec!["radroots trade app list".to_owned()],
});
};
@@ -827,7 +819,7 @@ pub fn app_record_export(
seller_pubkey: non_empty_string(app_order.loaded.document.order.seller_pubkey.clone()),
issues,
reason: Some(format!(
- "app-authored local order record `{}` is not ready as a CLI order draft",
+ "app-authored local trade record `{}` is not ready as a CLI trade draft",
args.record_id
)),
actions,
@@ -869,17 +861,17 @@ pub fn app_record_export(
seller_pubkey: non_empty_string(app_order.loaded.document.order.seller_pubkey.clone()),
issues,
reason: Some(if config.output.dry_run {
- "dry run requested; order draft was not written".to_owned()
+ "dry run requested; trade draft was not written".to_owned()
} else {
- "app-authored local order record exported as a CLI order draft".to_owned()
+ "app-authored local trade record exported as a CLI trade draft".to_owned()
}),
actions: vec![
format!(
- "radroots order get {}",
+ "radroots trade get {}",
app_order.loaded.document.order.order_id
),
format!(
- "radroots --relay wss://relay.example.com order submit {}",
+ "radroots --relay wss://relay.example.com trade submit {}",
app_order.loaded.document.order.order_id
),
],
@@ -888,7 +880,7 @@ pub fn app_record_export(
pub fn submit(
config: &RuntimeConfig,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
) -> Result<OrderSubmitView, CliSdkAdapterError> {
let file = draft_lookup_path(config, args.key.as_str());
let (loaded, source_issues) = if file.exists() {
@@ -955,11 +947,11 @@ pub fn submit(
failed_relays: Vec::new(),
idempotency_key: args.idempotency_key.clone(),
signer_mode: None,
- reason: Some(format!("order draft `{}` was not found", args.key)),
+ reason: Some(format!("trade draft `{}` was not found", args.key)),
job: None,
issues: Vec::new(),
actions: vec![
- "radroots order list".to_owned(),
+ "radroots trade list".to_owned(),
"radroots basket create".to_owned(),
],
});
@@ -973,7 +965,7 @@ pub fn submit(
if !issues.is_empty() {
let mut actions = actions_for_document(&loaded.document, loaded.file.as_path(), &issues);
actions.push(format!(
- "radroots order get {}",
+ "radroots trade get {}",
loaded.document.order.order_id
));
return Ok(OrderSubmitView {
@@ -1001,7 +993,7 @@ pub fn submit(
failed_relays: Vec::new(),
idempotency_key: args.idempotency_key.clone(),
signer_mode: None,
- reason: Some("order draft is not ready for submit".to_owned()),
+ reason: Some("trade draft is not ready for submit".to_owned()),
job: None,
issues,
actions,
@@ -1052,21 +1044,21 @@ pub fn submit(
pub fn rebind(
config: &RuntimeConfig,
- args: &OrderRebindArgs,
+ args: &TradeRebindArgs,
) -> Result<OrderRebindView, RuntimeError> {
rebind_inner(config, args, false)
}
pub fn rebind_preflight(
config: &RuntimeConfig,
- args: &OrderRebindArgs,
+ args: &TradeRebindArgs,
) -> Result<OrderRebindView, RuntimeError> {
rebind_inner(config, args, true)
}
fn rebind_inner(
config: &RuntimeConfig,
- args: &OrderRebindArgs,
+ args: &TradeRebindArgs,
dry_run: bool,
) -> Result<OrderRebindView, RuntimeError> {
let file = draft_lookup_path(config, args.key.as_str());
@@ -1089,9 +1081,9 @@ fn rebind_inner(
buyer_pubkey_changed: false,
existing_request_check: "not_checked".to_owned(),
existing_request_event_ids: Vec::new(),
- reason: Some(format!("order draft `{}` was not found", args.key)),
+ reason: Some(format!("trade draft `{}` was not found", args.key)),
actions: vec![
- "radroots order list".to_owned(),
+ "radroots trade list".to_owned(),
"radroots basket create".to_owned(),
],
});
@@ -1138,7 +1130,7 @@ fn rebind_inner(
.to_owned(),
),
actions: vec![
- format!("radroots order status get {from_order_id}"),
+ format!("radroots trade status get {from_order_id}"),
"radroots basket quote create <basket-id>".to_owned(),
],
});
@@ -1208,7 +1200,7 @@ fn rebind_inner(
args.key, args.selector
)]
} else {
- vec![format!("radroots order get {to_order_id}")]
+ vec![format!("radroots trade get {to_order_id}")]
},
})
}
@@ -1221,7 +1213,7 @@ pub fn event_list(
return Ok(order_event_list_unconfigured(
None,
ORDER_ACTOR_CONTEXT_NETWORK_ONLY,
- "order event list requires at least one configured relay".to_owned(),
+ "trade event list requires at least one configured relay".to_owned(),
Vec::new(),
vec![ORDER_EVENT_LIST_RELAY_ACTION.to_owned()],
));
@@ -1233,7 +1225,7 @@ pub fn event_list(
return Ok(order_event_list_unconfigured(
None,
ORDER_ACTOR_CONTEXT_NETWORK_ONLY,
- "order event list requires a selected seller account".to_owned(),
+ "trade event list requires a selected seller account".to_owned(),
config.relay.urls.clone(),
vec!["radroots account create".to_owned()],
));
@@ -1269,7 +1261,7 @@ pub fn event_list(
pub fn decide(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
) -> Result<OrderDecisionView, RuntimeError> {
if config.relay.urls.is_empty() {
let mut view =
@@ -1329,12 +1321,34 @@ pub fn decide(
}
if resolution.requests.len() == 1 {
let request = resolution.requests[0].clone();
- let status_view = legacy_order_preflight_relay_status(
+ let filter = order_status_filter(args.key.as_str())?;
+ let receipt = match fetch_events_from_relays(&config.relay.urls, filter) {
+ Ok(receipt) => receipt,
+ Err(DirectRelayFetchError::Connect {
+ reason,
+ target_relays,
+ failed_relays,
+ }) => {
+ let mut view =
+ order_decision_base_view(config, args, "unavailable", config.output.dry_run);
+ apply_order_decision_resolution(&mut view, &resolution);
+ apply_order_decision_request(&mut view, &request);
+ view.seller_pubkey = Some(seller_pubkey);
+ view.target_relays = target_relays;
+ view.failed_relays = relay_failures(failed_relays);
+ view.reason = Some(format!("direct relay connection failed: {reason}"));
+ return Ok(view);
+ }
+ Err(error) => return Err(RuntimeError::Network(error.to_string())),
+ };
+ let mut status_view = sdk_order_status_from_relay_receipt(
config,
- &OrderStatusArgs {
- key: args.key.clone(),
- },
- )?;
+ args.key.as_str(),
+ ORDER_ACTOR_CONTEXT_RESOLVED_ACCOUNT,
+ receipt,
+ )
+ .map_err(cli_sdk_error_to_runtime)?;
+ enrich_order_status_inventory(config, &mut status_view)?;
if let Some(view) = order_decision_preflight_view_from_status(
config,
args,
@@ -1405,7 +1419,7 @@ pub fn decide(
pub fn revision_propose(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
) -> Result<OrderRevisionProposalView, RuntimeError> {
if let Some(view) = order_revision_args_preflight_view(config, args) {
return Ok(view);
@@ -1414,7 +1428,7 @@ pub fn revision_propose(
let mut view =
order_revision_base_view(config, args, "unconfigured", config.output.dry_run);
view.reason =
- Some("order revision propose requires at least one configured relay".to_owned());
+ Some("trade revision propose requires at least one configured relay".to_owned());
return Ok(view);
}
@@ -1424,7 +1438,7 @@ pub fn revision_propose(
let mut view =
order_revision_base_view(config, args, "unconfigured", config.output.dry_run);
view.reason =
- Some("order revision propose requires a selected seller account".to_owned());
+ Some("trade revision propose requires a selected seller account".to_owned());
view.actions = vec!["radroots account create".to_owned()];
return Ok(view);
}
@@ -1452,17 +1466,13 @@ pub fn revision_propose(
let evidence_events = order_evidence_from_relay_events(receipt.events.as_slice());
let revision_candidates =
order_revision_proposals_from_events(args.key.as_str(), receipt.events.as_slice());
- let reduction = order_status_reduction_from_receipt_with_context(
- OrderStatusContext {
- order_id: args.key.as_str(),
- buyer_pubkey: None,
- seller_pubkey: None,
- selected_account_pubkey: Some(selected_pubkey.as_str()),
- actor_context_source: ORDER_ACTOR_CONTEXT_RESOLVED_ACCOUNT,
- },
+ let mut status_view = sdk_order_status_from_relay_receipt(
+ config,
+ args.key.as_str(),
+ ORDER_ACTOR_CONTEXT_RESOLVED_ACCOUNT,
receipt,
- );
- let mut status_view = reduction.view;
+ )
+ .map_err(cli_sdk_error_to_runtime)?;
enrich_order_status_inventory(config, &mut status_view)?;
if let Some(view) = order_revision_preflight_view_from_status(
config,
@@ -1497,7 +1507,7 @@ pub fn revision_propose(
args,
&status_view,
format!(
- "order revision propose inputs for `{}` are invalid",
+ "trade revision propose inputs for `{}` are invalid",
args.key
),
vec![issue_with_code(
@@ -1527,7 +1537,7 @@ pub fn revision_propose(
pub fn revision_decide(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
) -> Result<OrderRevisionDecisionView, RuntimeError> {
if let Some(view) = order_revision_decision_args_preflight_view(config, args) {
return Ok(view);
@@ -1536,7 +1546,7 @@ pub fn revision_decide(
let mut view =
order_revision_decision_base_view(config, args, "unconfigured", config.output.dry_run);
view.reason =
- Some("order revision decision requires at least one configured relay".to_owned());
+ Some("trade revision decision requires at least one configured relay".to_owned());
return Ok(view);
}
@@ -1550,7 +1560,7 @@ pub fn revision_decide(
config.output.dry_run,
);
view.reason =
- Some("order revision decision requires a selected buyer account".to_owned());
+ Some("trade revision decision requires a selected buyer account".to_owned());
view.actions = vec!["radroots account create".to_owned()];
return Ok(view);
}
@@ -1582,20 +1592,13 @@ pub fn revision_decide(
let evidence_events = order_evidence_from_relay_events(receipt.events.as_slice());
let revision_candidates =
order_revision_proposals_from_events(args.key.as_str(), receipt.events.as_slice());
- let reduction = order_status_reduction_from_receipt_with_context(
- OrderStatusContext {
- order_id: args.key.as_str(),
- buyer_pubkey: actor_context.status_buyer_pubkey.as_deref(),
- seller_pubkey: actor_context.status_seller_pubkey.as_deref(),
- selected_account_pubkey: actor_context
- .bound
- .is_none()
- .then_some(selected_pubkey.as_str()),
- actor_context_source: actor_context.status_context_source,
- },
+ let mut status_view = sdk_order_status_from_relay_receipt(
+ config,
+ args.key.as_str(),
+ actor_context.status_context_source,
receipt,
- );
- let mut status_view = reduction.view;
+ )
+ .map_err(cli_sdk_error_to_runtime)?;
enrich_order_status_inventory(config, &mut status_view)?;
if let Some(view) = order_revision_decision_preflight_view_from_status(
config,
@@ -1617,7 +1620,7 @@ pub fn revision_decide(
args,
&status_view,
format!(
- "order revision {} refused because revision `{}` is not the latest pending proposal",
+ "trade revision {} refused because revision `{}` is not the latest pending proposal",
args.decision.command(),
args.revision_id.trim()
),
@@ -1643,7 +1646,7 @@ pub fn revision_decide(
Some(bound) => resolve_local_order_bound_buyer_signing_identity(
config,
&bound.loaded,
- format!("order revision {}", args.decision.command()).as_str(),
+ format!("trade revision {}", args.decision.command()).as_str(),
),
None => resolve_local_order_revision_decision_signing_identity(config, buyer_pubkey, args),
};
@@ -1659,14 +1662,14 @@ pub fn revision_decide(
));
}
};
- if args.decision == OrderRevisionDecisionArg::Accept {
+ if args.decision == TradeRevisionDecisionArg::Accept {
let issues = order_revision_inventory_issues(&status_view, &proposal.payload);
if !issues.is_empty() {
let mut view = order_revision_decision_invalid_view(
config,
args,
&status_view,
- "order revision accept refused because visible inventory is unavailable for the revised items",
+ "trade revision accept refused because visible inventory is unavailable for the revised items",
issues,
);
apply_order_revision_decision_proposal(&mut view, proposal);
@@ -1697,12 +1700,12 @@ pub fn revision_decide(
pub fn cancel(
config: &RuntimeConfig,
- args: &OrderCancelArgs,
+ args: &TradeCancelArgs,
) -> Result<OrderCancellationView, RuntimeError> {
if config.relay.urls.is_empty() {
let mut view =
order_cancellation_base_view(config, args, "unconfigured", config.output.dry_run);
- view.reason = Some("order cancel requires at least one configured relay".to_owned());
+ view.reason = Some("trade cancel requires at least one configured relay".to_owned());
return Ok(view);
}
@@ -1711,7 +1714,7 @@ pub fn cancel(
None => {
let mut view =
order_cancellation_base_view(config, args, "unconfigured", config.output.dry_run);
- view.reason = Some("order cancel requires a selected buyer account".to_owned());
+ view.reason = Some("trade cancel requires a selected buyer account".to_owned());
view.actions = vec!["radroots account create".to_owned()];
return Ok(view);
}
@@ -1737,20 +1740,13 @@ pub fn cancel(
};
let evidence_events = order_evidence_from_relay_events(receipt.events.as_slice());
- let reduction = order_status_reduction_from_receipt_with_context(
- OrderStatusContext {
- order_id: args.key.as_str(),
- buyer_pubkey: actor_context.status_buyer_pubkey.as_deref(),
- seller_pubkey: actor_context.status_seller_pubkey.as_deref(),
- selected_account_pubkey: actor_context
- .bound
- .is_none()
- .then_some(selected_pubkey.as_str()),
- actor_context_source: actor_context.status_context_source,
- },
+ let status_view = sdk_order_status_from_relay_receipt(
+ config,
+ args.key.as_str(),
+ actor_context.status_context_source,
receipt,
- );
- let status_view = reduction.view;
+ )
+ .map_err(cli_sdk_error_to_runtime)?;
if let Some(view) = order_cancellation_preflight_view_from_status(
config,
args,
@@ -1766,7 +1762,7 @@ pub fn cancel(
.ok_or_else(|| RuntimeError::Config("order is missing buyer_pubkey".to_owned()))?;
let signing = match actor_context.bound.as_ref() {
Some(bound) => {
- resolve_local_order_bound_buyer_signing_identity(config, &bound.loaded, "order cancel")
+ resolve_local_order_bound_buyer_signing_identity(config, &bound.loaded, "trade cancel")
}
None => resolve_local_order_cancellation_signing_identity(config, buyer_pubkey),
};
@@ -1792,7 +1788,7 @@ pub fn cancel(
pub fn status(
config: &RuntimeConfig,
- args: &OrderStatusArgs,
+ args: &TradeStatusArgs,
) -> Result<OrderStatusView, CliSdkAdapterError> {
let request = OrderStatusRequest::parse(args.key.as_str())?;
let session = CliSdkSession::connect(config)?;
@@ -1800,96 +1796,63 @@ pub fn status(
Ok(sdk_order_status_view(receipt))
}
-fn legacy_order_preflight_relay_status(
+fn sdk_order_status_from_relay_receipt(
config: &RuntimeConfig,
- args: &OrderStatusArgs,
-) -> Result<OrderStatusView, RuntimeError> {
- if config.relay.urls.is_empty() {
- return Ok(OrderStatusView {
- state: "unconfigured".to_owned(),
- source: LEGACY_ORDER_PREFLIGHT_STATUS_SOURCE.to_owned(),
- order_id: args.key.clone(),
- actor_context_source: ORDER_ACTOR_CONTEXT_NETWORK_ONLY.to_owned(),
- request_event_id: None,
- decision_event_id: None,
- agreement_event_id: None,
- listing_event_id: None,
- listing_addr: None,
- buyer_pubkey: None,
- seller_pubkey: None,
- economics: None,
- last_event_id: None,
- revision: None,
- inventory: None,
- lifecycle: None,
- sdk_receipt: None,
- reducer_issues: Vec::new(),
- target_relays: Vec::new(),
- connected_relays: Vec::new(),
- failed_relays: Vec::new(),
- fetched_count: 0,
- decoded_count: 0,
- skipped_count: 0,
- reason: Some("order status get requires at least one configured relay".to_owned()),
- actions: vec![format!(
- "radroots --relay wss://relay.example.com order status get {}",
- args.key
- )],
- });
- }
+ order_id: &str,
+ actor_context_source: &'static str,
+ receipt: DirectRelayFetchReceipt,
+) -> Result<OrderStatusView, CliSdkAdapterError> {
+ let DirectRelayFetchReceipt {
+ target_relays,
+ connected_relays,
+ failed_relays,
+ events,
+ } = receipt;
+ let fetched_count = events.len();
+ let mut decoded_count = 0usize;
+ let mut skipped_count = 0usize;
+ let mut issues = Vec::new();
+ let session = CliSdkSession::connect(config)?;
- let filter = order_status_filter(args.key.as_str())?;
- let receipt = match fetch_events_from_relays(&config.relay.urls, filter) {
- Ok(receipt) => receipt,
- Err(DirectRelayFetchError::Connect {
- reason,
- target_relays,
- failed_relays,
- }) => {
- return Ok(OrderStatusView {
- state: "unavailable".to_owned(),
- source: LEGACY_ORDER_PREFLIGHT_STATUS_SOURCE.to_owned(),
- order_id: args.key.clone(),
- actor_context_source: ORDER_ACTOR_CONTEXT_NETWORK_ONLY.to_owned(),
- request_event_id: None,
- decision_event_id: None,
- agreement_event_id: None,
- listing_event_id: None,
- listing_addr: None,
- buyer_pubkey: None,
- seller_pubkey: None,
- economics: None,
- last_event_id: None,
- revision: None,
- inventory: None,
- lifecycle: None,
- sdk_receipt: None,
- reducer_issues: Vec::new(),
- target_relays,
- connected_relays: Vec::new(),
- failed_relays: relay_failures(failed_relays),
- fetched_count: 0,
- decoded_count: 0,
- skipped_count: 0,
- reason: Some(format!("direct relay connection failed: {reason}")),
- actions: Vec::new(),
- });
+ for event in events {
+ let event_id = event.id.to_string();
+ let event = radroots_event_from_nostr(&event);
+ match session.block_on(
+ session
+ .sdk()
+ .trades()
+ .ingest_evidence(OrderEvidenceIngestRequest::new(event)),
+ ) {
+ Ok(_) => decoded_count += 1,
+ Err(error) => {
+ skipped_count += 1;
+ issues.push(issue_with_events(
+ "invalid_trade_evidence",
+ "event_id",
+ format!("trade evidence event `{event_id}` failed SDK ingest: {error}"),
+ vec![event_id],
+ ));
+ }
}
- Err(error) => return Err(RuntimeError::Network(error.to_string())),
- };
+ }
- let actor_context = order_status_actor_context(config, args.key.as_str())?;
- let mut view = order_status_from_receipt_with_context(
- OrderStatusContext {
- order_id: args.key.as_str(),
- buyer_pubkey: actor_context.buyer_pubkey.as_deref(),
- seller_pubkey: actor_context.seller_pubkey.as_deref(),
- selected_account_pubkey: actor_context.selected_account_pubkey.as_deref(),
- actor_context_source: actor_context.source,
- },
- receipt,
- );
- enrich_order_status_inventory(config, &mut view)?;
+ let request = OrderStatusRequest::parse(order_id)?;
+ let receipt = session.block_on(session.sdk().trades().status(request))?;
+ let mut view = sdk_order_status_view(receipt);
+ view.actor_context_source = actor_context_source.to_owned();
+ view.target_relays = target_relays;
+ view.connected_relays = connected_relays;
+ view.failed_relays = relay_failures(failed_relays);
+ view.fetched_count = fetched_count;
+ view.decoded_count = decoded_count;
+ view.skipped_count = skipped_count;
+ if !issues.is_empty() {
+ view.state = "invalid".to_owned();
+ view.reason = Some(format!(
+ "relay trade evidence for `{order_id}` failed SDK ingest"
+ ));
+ view.reducer_issues.extend(issues);
+ }
Ok(view)
}
@@ -1913,318 +1876,12 @@ struct OrderRevisionProposalCandidates {
issues: Vec<OrderIssueView>,
}
-#[derive(Debug, Clone)]
-struct OrderStatusReduction {
- view: OrderStatusView,
-}
-
#[derive(Debug, Clone, Copy)]
struct OrderRequestCandidateContext<'a> {
order_id: &'a str,
seller_pubkey: Option<&'a str>,
}
-#[derive(Debug, Clone, Copy)]
-struct OrderStatusContext<'a> {
- order_id: &'a str,
- buyer_pubkey: Option<&'a str>,
- seller_pubkey: Option<&'a str>,
- selected_account_pubkey: Option<&'a str>,
- actor_context_source: &'static str,
-}
-
-#[cfg(test)]
-fn order_status_from_receipt(order_id: &str, receipt: DirectRelayFetchReceipt) -> OrderStatusView {
- order_status_from_receipt_with_context(
- OrderStatusContext {
- order_id,
- buyer_pubkey: None,
- seller_pubkey: None,
- selected_account_pubkey: None,
- actor_context_source: ORDER_ACTOR_CONTEXT_NETWORK_ONLY,
- },
- receipt,
- )
-}
-
-fn order_status_from_receipt_with_context(
- context: OrderStatusContext<'_>,
- receipt: DirectRelayFetchReceipt,
-) -> OrderStatusView {
- order_status_reduction_from_receipt_with_context(context, receipt).view
-}
-
-fn order_status_reduction_from_receipt_with_context(
- context: OrderStatusContext<'_>,
- receipt: DirectRelayFetchReceipt,
-) -> OrderStatusReduction {
- order_status_reduction_from_receipt_inner(context, receipt)
-}
-
-fn order_status_reduction_from_receipt_inner(
- context: OrderStatusContext<'_>,
- receipt: DirectRelayFetchReceipt,
-) -> OrderStatusReduction {
- let DirectRelayFetchReceipt {
- target_relays,
- connected_relays,
- failed_relays,
- events,
- } = receipt;
- let fetched_count = events.len();
- let mut decoded_count = 0usize;
- let mut skipped_count = 0usize;
- let mut requests = Vec::new();
- let mut decisions = Vec::new();
- let mut revision_proposals = Vec::new();
- let mut revision_decisions = Vec::new();
- let mut cancellations = Vec::new();
- let mut request_listing_events = Vec::new();
- let mut candidate_issues = Vec::new();
-
- for event in events {
- match order_status_record_from_event(&event) {
- Ok(OrderStatusRecord::Request {
- listing_event_id,
- record,
- }) => {
- if !order_status_request_matches_context(&record, context) {
- skipped_count += 1;
- continue;
- }
- decoded_count += 1;
- request_listing_events.push((record.event_id.clone(), listing_event_id));
- requests.push(record);
- }
- Ok(OrderStatusRecord::Decision(record)) => {
- decoded_count += 1;
- decisions.push(record);
- }
- Ok(OrderStatusRecord::RevisionProposal(record)) => {
- decoded_count += 1;
- revision_proposals.push(record);
- }
- Ok(OrderStatusRecord::RevisionDecision(record)) => {
- decoded_count += 1;
- revision_decisions.push(record);
- }
- Ok(OrderStatusRecord::Cancellation(record)) => {
- decoded_count += 1;
- cancellations.push(record);
- }
- Err(error) => {
- skipped_count += 1;
- if order_status_request_candidate(&event, context) {
- let event_id = event.id.to_string();
- candidate_issues.push(issue_with_events(
- "invalid_request_candidate",
- "request_event_id",
- format!(
- "request event `{event_id}` failed order status validation: {error}"
- ),
- vec![event_id],
- ));
- }
- }
- }
- }
- candidate_issues.sort_by(|left, right| {
- left.event_ids
- .cmp(&right.event_ids)
- .then_with(|| left.message.cmp(&right.message))
- });
-
- let reducer_order_id = match protocol_order_id(context.order_id, "order_id") {
- Ok(order_id) => order_id,
- Err(error) => {
- let message = error.to_string();
- let view = OrderStatusView {
- state: "invalid".to_owned(),
- source: LEGACY_ORDER_PREFLIGHT_STATUS_SOURCE.to_owned(),
- order_id: context.order_id.to_owned(),
- actor_context_source: context.actor_context_source.to_owned(),
- request_event_id: None,
- decision_event_id: None,
- agreement_event_id: None,
- listing_event_id: None,
- listing_addr: None,
- buyer_pubkey: None,
- seller_pubkey: None,
- economics: None,
- last_event_id: None,
- revision: None,
- inventory: None,
- lifecycle: None,
- sdk_receipt: None,
- reducer_issues: vec![issue("order_id", message.clone())],
- target_relays,
- connected_relays,
- failed_relays: relay_failures(failed_relays),
- fetched_count,
- decoded_count,
- skipped_count,
- reason: Some(message),
- actions: Vec::new(),
- };
- return OrderStatusReduction { view };
- }
- };
- let order_id = context.order_id;
- let revision_proposal_records = revision_proposals.clone();
- let revision_decision_records = revision_decisions.clone();
- let cancellation_records = cancellations.clone();
- let projection = reduce_order_events(
- &reducer_order_id,
- RadrootsOrderReductionInputs {
- requests,
- decisions: decisions.clone(),
- revision_proposals,
- revision_decisions,
- cancellations,
- },
- );
- let cancellation_root_event_id =
- projection
- .cancellation_event_id
- .as_ref()
- .and_then(|event_id| {
- cancellation_records
- .iter()
- .find(|record| &record.event_id == event_id)
- .map(|record| record.root_event_id.clone())
- });
- let cancellation_prev_event_id =
- projection
- .cancellation_event_id
- .as_ref()
- .and_then(|event_id| {
- cancellation_records
- .iter()
- .find(|record| &record.event_id == event_id)
- .map(|record| record.prev_event_id.clone())
- });
- let cancellation_reason = projection
- .cancellation_event_id
- .as_ref()
- .and_then(|event_id| {
- cancellation_records
- .iter()
- .find(|record| &record.event_id == event_id)
- .map(|record| record.payload.reason.clone())
- });
- let listing_event_id = projection
- .request_event_id
- .as_ref()
- .and_then(|request_event_id| {
- request_listing_events
- .iter()
- .find(|(event_id, _)| event_id == request_event_id)
- .and_then(|(_, listing_event_id)| listing_event_id.clone())
- });
- let mut state = active_order_status_state(&projection.status).to_owned();
- let mut reason = active_order_status_reason(&projection.status, order_id);
- let mut reducer_issues = projection
- .issues
- .into_iter()
- .map(active_order_reducer_issue_view)
- .collect::<Vec<_>>();
- if !candidate_issues.is_empty() {
- state = "invalid".to_owned();
- reason = Some(format!(
- "active order request candidates for `{order_id}` failed status validation"
- ));
- reducer_issues.extend(candidate_issues);
- }
- let inventory = order_status_inventory_view(
- &projection.status,
- listing_event_id.clone(),
- projection.decision_event_id.as_ref(),
- &decisions,
- reducer_issues.as_slice(),
- );
- let lifecycle = order_status_lifecycle_view(
- &projection.status,
- optional_string(projection.request_event_id.clone()),
- optional_string(projection.last_event_id.clone()),
- optional_string(projection.cancellation_event_id.clone()),
- optional_string(cancellation_root_event_id),
- optional_string(cancellation_prev_event_id),
- cancellation_reason,
- reducer_issues.as_slice(),
- );
- let revision = order_status_revision_view(
- projection.last_event_id.as_ref(),
- projection.agreement_event_id.as_ref(),
- &revision_proposal_records,
- &revision_decision_records,
- );
- let view = OrderStatusView {
- state,
- source: LEGACY_ORDER_PREFLIGHT_STATUS_SOURCE.to_owned(),
- order_id: projection.order_id.to_string(),
- actor_context_source: context.actor_context_source.to_owned(),
- request_event_id: optional_string(projection.request_event_id),
- decision_event_id: optional_string(projection.decision_event_id),
- agreement_event_id: optional_string(projection.agreement_event_id),
- listing_event_id,
- listing_addr: optional_string(projection.listing_addr),
- buyer_pubkey: optional_string(projection.buyer_pubkey),
- seller_pubkey: optional_string(projection.seller_pubkey),
- economics: projection.economics,
- last_event_id: optional_string(projection.last_event_id),
- revision,
- inventory,
- lifecycle: Some(lifecycle),
- sdk_receipt: None,
- reducer_issues,
- target_relays,
- connected_relays,
- failed_relays: relay_failures(failed_relays),
- fetched_count,
- decoded_count,
- skipped_count,
- reason,
- actions: Vec::new(),
- };
- OrderStatusReduction { view }
-}
-
-fn order_status_request_matches_context(
- record: &RadrootsOrderRequestRecord,
- context: OrderStatusContext<'_>,
-) -> bool {
- if record.payload.order_id.to_string() != context.order_id {
- return false;
- }
- order_status_context_is_network_only(context)
- || context.buyer_pubkey.is_some_and(|pubkey| {
- record
- .payload
- .buyer_pubkey
- .to_string()
- .eq_ignore_ascii_case(pubkey)
- })
- || context.seller_pubkey.is_some_and(|pubkey| {
- record
- .payload
- .seller_pubkey
- .to_string()
- .eq_ignore_ascii_case(pubkey)
- })
- || context.selected_account_pubkey.is_some_and(|pubkey| {
- record
- .payload
- .buyer_pubkey
- .to_string()
- .eq_ignore_ascii_case(pubkey)
- || record
- .payload
- .seller_pubkey
- .to_string()
- .eq_ignore_ascii_case(pubkey)
- })
-}
-
fn enrich_order_status_inventory(
config: &RuntimeConfig,
view: &mut OrderStatusView,
@@ -2504,34 +2161,6 @@ fn listing_inventory_issue_involves_order(
}
}
-fn order_status_request_candidate(
- event: &RadrootsNostrEvent,
- context: OrderStatusContext<'_>,
-) -> bool {
- if event_kind_u32(event) != KIND_ORDER_REQUEST
- || !event_matches_tag_value(event, "d", context.order_id)
- {
- return false;
- }
- order_status_context_is_network_only(context)
- || context
- .buyer_pubkey
- .is_some_and(|pubkey| event.pubkey.to_string().eq_ignore_ascii_case(pubkey))
- || context
- .seller_pubkey
- .is_some_and(|pubkey| event_matches_tag_value(event, "p", pubkey))
- || context.selected_account_pubkey.is_some_and(|pubkey| {
- event.pubkey.to_string().eq_ignore_ascii_case(pubkey)
- || event_matches_tag_value(event, "p", pubkey)
- })
-}
-
-fn order_status_context_is_network_only(context: OrderStatusContext<'_>) -> bool {
- context.buyer_pubkey.is_none()
- && context.seller_pubkey.is_none()
- && context.selected_account_pubkey.is_none()
-}
-
fn order_request_candidate_matches(
event: &RadrootsNostrEvent,
context: OrderRequestCandidateContext<'_>,
@@ -2634,7 +2263,7 @@ fn order_status_record_from_event(
let author_pubkey = protocol_pubkey(event.author.as_str(), "revision_author_pubkey")?;
let envelope = order_revision_proposal_from_event(&event).map_err(|error| {
RuntimeError::Config(format!(
- "decode active order revision proposal event: {error}"
+ "decode active trade revision proposal event: {error}"
))
})?;
let context = order_event_context_from_tags(
@@ -2643,7 +2272,7 @@ fn order_status_record_from_event(
)
.map_err(|error| {
RuntimeError::Config(format!(
- "decode active order revision proposal tags: {error}"
+ "decode active trade revision proposal tags: {error}"
))
})?;
Ok(OrderStatusRecord::RevisionProposal(
@@ -2654,12 +2283,12 @@ fn order_status_record_from_event(
root_event_id: required_order_context_event_id(
context.root_event_id,
"e_root",
- "active order revision proposal",
+ "active trade revision proposal",
)?,
prev_event_id: required_order_context_event_id(
context.prev_event_id,
"e_prev",
- "active order revision proposal",
+ "active trade revision proposal",
)?,
payload: envelope.payload,
},
@@ -2672,7 +2301,7 @@ fn order_status_record_from_event(
protocol_pubkey(event.author.as_str(), "revision_decision_author_pubkey")?;
let envelope = order_revision_decision_from_event(&event).map_err(|error| {
RuntimeError::Config(format!(
- "decode active order revision decision event: {error}"
+ "decode active trade revision decision event: {error}"
))
})?;
let context = order_event_context_from_tags(
@@ -2681,7 +2310,7 @@ fn order_status_record_from_event(
)
.map_err(|error| {
RuntimeError::Config(format!(
- "decode active order revision decision tags: {error}"
+ "decode active trade revision decision tags: {error}"
))
})?;
Ok(OrderStatusRecord::RevisionDecision(
@@ -2692,12 +2321,12 @@ fn order_status_record_from_event(
root_event_id: required_order_context_event_id(
context.root_event_id,
"e_root",
- "active order revision decision",
+ "active trade revision decision",
)?,
prev_event_id: required_order_context_event_id(
context.prev_event_id,
"e_prev",
- "active order revision decision",
+ "active trade revision decision",
)?,
payload: envelope.payload,
},
@@ -2709,13 +2338,13 @@ fn order_status_record_from_event(
let author_pubkey =
protocol_pubkey(event.author.as_str(), "cancellation_author_pubkey")?;
let envelope = order_cancellation_from_event(&event).map_err(|error| {
- RuntimeError::Config(format!("decode active order cancellation event: {error}"))
+ RuntimeError::Config(format!("decode active trade cancellation event: {error}"))
})?;
let context =
order_event_context_from_tags(RadrootsOrderEventType::OrderCancelled, &event.tags)
.map_err(|error| {
RuntimeError::Config(format!(
- "decode active order cancellation tags: {error}"
+ "decode active trade cancellation tags: {error}"
))
})?;
Ok(OrderStatusRecord::Cancellation(
@@ -2726,12 +2355,12 @@ fn order_status_record_from_event(
root_event_id: required_order_context_event_id(
context.root_event_id,
"e_root",
- "active order cancellation",
+ "active trade cancellation",
)?,
prev_event_id: required_order_context_event_id(
context.prev_event_id,
"e_prev",
- "active order cancellation",
+ "active trade cancellation",
)?,
payload: envelope.payload,
},
@@ -2777,647 +2406,6 @@ fn order_revision_proposals_from_events(
OrderRevisionProposalCandidates { records, issues }
}
-fn active_order_status_state(status: &RadrootsTradeWorkflowState) -> &'static str {
- match status {
- RadrootsTradeWorkflowState::Missing => "missing",
- RadrootsTradeWorkflowState::Requested => "requested",
- RadrootsTradeWorkflowState::RevisionProposed => "revision_proposed",
- RadrootsTradeWorkflowState::AgreedPendingRhi => "pending_rhi",
- RadrootsTradeWorkflowState::Committed => "committed",
- RadrootsTradeWorkflowState::Declined => "declined",
- RadrootsTradeWorkflowState::Cancelled => "cancelled",
- RadrootsTradeWorkflowState::Invalid => "invalid",
- }
-}
-
-fn active_order_status_reason(
- status: &RadrootsTradeWorkflowState,
- order_id: &str,
-) -> Option<String> {
- match status {
- RadrootsTradeWorkflowState::Missing => {
- Some(format!("no active order events matched `{order_id}`"))
- }
- RadrootsTradeWorkflowState::Invalid => Some(format!(
- "active order events for `{order_id}` failed reducer validation"
- )),
- _ => None,
- }
-}
-
-fn order_status_inventory_view(
- status: &RadrootsTradeWorkflowState,
- listing_event_id: Option<String>,
- decision_event_id: Option<&RadrootsEventId>,
- decisions: &[RadrootsOrderDecisionRecord],
- reducer_issues: &[OrderIssueView],
-) -> Option<OrderInventoryView> {
- let inventory_issues = reducer_issues
- .iter()
- .filter(|issue| {
- matches!(
- issue.code.as_str(),
- "missing_decision_inventory_commitments"
- | "decision_inventory_commitment_mismatch"
- | "decision_counterparty_mismatch"
- | "listing_inventory_arithmetic_overflow"
- | "unknown_inventory_bin"
- | "listing_inventory_over_reserved"
- | "invalid_inventory_order"
- )
- })
- .cloned()
- .collect::<Vec<_>>();
-
- match status {
- RadrootsTradeWorkflowState::AgreedPendingRhi | RadrootsTradeWorkflowState::Committed => {
- let bins = decision_event_id
- .and_then(|event_id| {
- decisions
- .iter()
- .find(|decision| decision.event_id == *event_id)
- })
- .map(|decision| inventory_bins_from_decision(&decision.payload.decision))
- .unwrap_or_default();
- Some(OrderInventoryView {
- state: if inventory_issues.is_empty() {
- match status {
- RadrootsTradeWorkflowState::Committed => "committed".to_owned(),
- _ => "reserved".to_owned(),
- }
- } else {
- "invalid".to_owned()
- },
- listing_event_id,
- commitment_valid: inventory_issues.is_empty(),
- bins,
- issues: inventory_issues,
- })
- }
- RadrootsTradeWorkflowState::Cancelled => Some(OrderInventoryView {
- state: if decision_event_id.is_some() {
- "released".to_owned()
- } else {
- "not_reserved".to_owned()
- },
- listing_event_id,
- commitment_valid: inventory_issues.is_empty(),
- bins: Vec::new(),
- issues: inventory_issues,
- }),
- RadrootsTradeWorkflowState::Declined => Some(OrderInventoryView {
- state: "not_reserved".to_owned(),
- listing_event_id,
- commitment_valid: true,
- bins: Vec::new(),
- issues: inventory_issues,
- }),
- RadrootsTradeWorkflowState::Invalid if !inventory_issues.is_empty() => {
- Some(OrderInventoryView {
- state: "invalid".to_owned(),
- listing_event_id,
- commitment_valid: false,
- bins: Vec::new(),
- issues: inventory_issues,
- })
- }
- _ => None,
- }
-}
-
-fn order_status_lifecycle_view(
- status: &RadrootsTradeWorkflowState,
- request_event_id: Option<String>,
- last_event_id: Option<String>,
- cancellation_event_id: Option<String>,
- cancellation_root_event_id: Option<String>,
- cancellation_prev_event_id: Option<String>,
- cancellation_reason: Option<String>,
- reducer_issues: &[OrderIssueView],
-) -> OrderStatusLifecycleView {
- let phase = order_status_lifecycle_phase(status).to_owned();
- let terminal = matches!(
- status,
- RadrootsTradeWorkflowState::Committed
- | RadrootsTradeWorkflowState::Declined
- | RadrootsTradeWorkflowState::Cancelled
- | RadrootsTradeWorkflowState::Invalid
- );
- let cancellation =
- cancellation_event_id
- .as_ref()
- .map(|event_id| OrderStatusLifecycleCancellationView {
- event_id: event_id.clone(),
- root_event_id: cancellation_root_event_id
- .clone()
- .or(request_event_id.clone()),
- prev_event_id: cancellation_prev_event_id.clone(),
- reason: cancellation_reason.clone(),
- });
- let event_id = cancellation_event_id;
- let prev_event_id = cancellation_prev_event_id.or(last_event_id);
- OrderStatusLifecycleView {
- phase,
- terminal,
- event_id,
- root_event_id: request_event_id,
- prev_event_id,
- cancellation,
- issues: reducer_issues.to_vec(),
- }
-}
-
-fn order_status_revision_view(
- last_event_id: Option<&RadrootsEventId>,
- agreement_event_id: Option<&RadrootsEventId>,
- proposals: &[RadrootsOrderRevisionProposalRecord],
- decisions: &[RadrootsOrderRevisionDecisionRecord],
-) -> Option<OrderStatusRevisionView> {
- if let Some(proposal) = last_event_id
- .and_then(|event_id| proposals.iter().find(|record| record.event_id == *event_id))
- {
- return Some(OrderStatusRevisionView {
- state: "pending".to_owned(),
- revision_id: Some(proposal.payload.revision_id.to_string()),
- proposal_event_id: Some(proposal.event_id.to_string()),
- decision_event_id: None,
- root_event_id: Some(proposal.root_event_id.to_string()),
- prev_event_id: Some(proposal.prev_event_id.to_string()),
- agreement_event_id: None,
- reason: Some(proposal.payload.reason.clone()),
- });
- }
-
- if let Some(decision) = last_event_id
- .and_then(|event_id| decisions.iter().find(|record| record.event_id == *event_id))
- {
- return Some(order_status_revision_view_from_decision(
- decision,
- agreement_event_id,
- ));
- }
-
- agreement_event_id
- .and_then(|event_id| decisions.iter().find(|record| record.event_id == *event_id))
- .map(|decision| order_status_revision_view_from_decision(decision, agreement_event_id))
-}
-
-fn order_status_revision_view_from_decision(
- decision: &RadrootsOrderRevisionDecisionRecord,
- agreement_event_id: Option<&RadrootsEventId>,
-) -> OrderStatusRevisionView {
- let (state, reason) = match &decision.payload.decision {
- RadrootsOrderRevisionOutcome::Accepted => ("accepted", None),
- RadrootsOrderRevisionOutcome::Declined { reason } => ("declined", Some(reason.clone())),
- };
- OrderStatusRevisionView {
- state: state.to_owned(),
- revision_id: Some(decision.payload.revision_id.to_string()),
- proposal_event_id: Some(decision.prev_event_id.to_string()),
- decision_event_id: Some(decision.event_id.to_string()),
- root_event_id: Some(decision.root_event_id.to_string()),
- prev_event_id: Some(decision.prev_event_id.to_string()),
- agreement_event_id: agreement_event_id.map(ToString::to_string),
- reason,
- }
-}
-
-fn order_status_lifecycle_phase(status: &RadrootsTradeWorkflowState) -> &'static str {
- match status {
- RadrootsTradeWorkflowState::Missing => "missing",
- RadrootsTradeWorkflowState::Requested => "requested",
- RadrootsTradeWorkflowState::RevisionProposed => "revision_proposed",
- RadrootsTradeWorkflowState::AgreedPendingRhi => "pending_rhi",
- RadrootsTradeWorkflowState::Committed => "committed",
- RadrootsTradeWorkflowState::Declined => "declined",
- RadrootsTradeWorkflowState::Cancelled => "cancelled",
- RadrootsTradeWorkflowState::Invalid => "invalid",
- }
-}
-
-fn inventory_bins_from_decision(
- decision: &RadrootsOrderDecisionOutcome,
-) -> Vec<OrderInventoryBinView> {
- match decision {
- RadrootsOrderDecisionOutcome::Accepted {
- inventory_commitments,
- } => {
- let mut bins = inventory_commitments
- .iter()
- .map(|commitment| OrderInventoryBinView {
- bin_id: commitment.bin_id.to_string(),
- committed_count: u64::from(commitment.bin_count),
- available_count: None,
- remaining_count: None,
- over_reserved: false,
- })
- .collect::<Vec<_>>();
- bins.sort_by(|left, right| left.bin_id.cmp(&right.bin_id));
- bins
- }
- RadrootsOrderDecisionOutcome::Declined { .. } => Vec::new(),
- }
-}
-
-fn active_order_reducer_issue_view(issue_value: RadrootsOrderIssue) -> OrderIssueView {
- match issue_value {
- RadrootsOrderIssue::MissingRequest => issue_with_code(
- "missing_request",
- "request_event_id",
- "active order reducer reported missing request",
- ),
- RadrootsOrderIssue::MultipleRequests { event_ids } => issue_with_events(
- "multiple_requests",
- "request_event_id",
- "active order reducer reported multiple request events",
- event_ids,
- ),
- RadrootsOrderIssue::RequestPayloadInvalid { event_id } => issue_with_events(
- "invalid_request_payload",
- "request_payload",
- "active order reducer reported invalid request payload",
- vec![event_id],
- ),
- RadrootsOrderIssue::RequestOrderIdMismatch { event_id } => issue_with_events(
- "request_order_id_mismatch",
- "order_id",
- "active order reducer reported request order id mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RequestAuthorMismatch { event_id } => issue_with_events(
- "request_author_mismatch",
- "buyer_pubkey",
- "active order reducer reported request author mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RequestListingAddressInvalid { event_id } => issue_with_events(
- "invalid_request_listing_address",
- "listing_addr",
- "active order reducer reported invalid request listing address",
- vec![event_id],
- ),
- RadrootsOrderIssue::RequestSellerListingMismatch { event_id } => issue_with_events(
- "request_seller_listing_mismatch",
- "seller_pubkey",
- "active order reducer reported request seller/listing mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionPayloadInvalid { event_id } => issue_with_events(
- "invalid_decision_payload",
- "decision_payload",
- "active order reducer reported invalid decision payload",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionOrderIdMismatch { event_id } => issue_with_events(
- "decision_order_id_mismatch",
- "order_id",
- "active order reducer reported decision order id mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionAuthorMismatch { event_id } => issue_with_events(
- "decision_author_mismatch",
- "seller_pubkey",
- "active order reducer reported decision author mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionCounterpartyMismatch { event_id } => issue_with_events(
- "decision_counterparty_mismatch",
- "buyer_pubkey",
- "active order reducer reported decision counterparty mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionBuyerMismatch { event_id } => issue_with_events(
- "decision_buyer_mismatch",
- "buyer_pubkey",
- "active order reducer reported decision buyer mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionSellerMismatch { event_id } => issue_with_events(
- "decision_seller_mismatch",
- "seller_pubkey",
- "active order reducer reported decision seller mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionListingAddressInvalid { event_id } => issue_with_events(
- "invalid_decision_listing_address",
- "listing_addr",
- "active order reducer reported invalid decision listing address",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionListingMismatch { event_id } => issue_with_events(
- "decision_listing_mismatch",
- "listing_addr",
- "active order reducer reported decision listing mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionRootMismatch { event_id } => issue_with_events(
- "decision_root_mismatch",
- "root_event_id",
- "active order reducer reported decision root mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionPreviousMismatch { event_id } => issue_with_events(
- "decision_previous_mismatch",
- "prev_event_id",
- "active order reducer reported decision previous mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionMissingInventoryCommitments { event_id } => issue_with_events(
- "missing_decision_inventory_commitments",
- "inventory_commitments",
- "active order reducer reported missing decision inventory commitments",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionInventoryCommitmentMismatch { event_id } => issue_with_events(
- "decision_inventory_commitment_mismatch",
- "inventory_commitments",
- "active order reducer reported decision inventory commitment mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::DecisionMissingReason { event_id } => issue_with_events(
- "missing_decision_decline_reason",
- "reason",
- "active order reducer reported missing decision decline reason",
- vec![event_id],
- ),
- RadrootsOrderIssue::ConflictingDecisions { event_ids } => issue_with_events(
- "conflicting_decisions",
- "decision_event_id",
- "active order reducer reported conflicting decisions",
- event_ids,
- ),
- RadrootsOrderIssue::RevisionProposalPayloadInvalid { event_id } => issue_with_events(
- "invalid_revision_proposal_payload",
- "revision_payload",
- "active order reducer reported invalid revision proposal payload",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionProposalOrderIdMismatch { event_id } => issue_with_events(
- "revision_proposal_order_id_mismatch",
- "order_id",
- "active order reducer reported revision proposal order id mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionProposalAuthorMismatch { event_id } => issue_with_events(
- "revision_proposal_author_mismatch",
- "seller_pubkey",
- "active order reducer reported revision proposal author mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionProposalCounterpartyMismatch { event_id } => issue_with_events(
- "revision_proposal_counterparty_mismatch",
- "buyer_pubkey",
- "active order reducer reported revision proposal counterparty mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionProposalBuyerMismatch { event_id } => issue_with_events(
- "revision_proposal_buyer_mismatch",
- "buyer_pubkey",
- "active order reducer reported revision proposal buyer mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionProposalSellerMismatch { event_id } => issue_with_events(
- "revision_proposal_seller_mismatch",
- "seller_pubkey",
- "active order reducer reported revision proposal seller mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionProposalListingAddressInvalid { event_id } => {
- issue_with_events(
- "invalid_revision_proposal_listing_address",
- "listing_addr",
- "active order reducer reported invalid revision proposal listing address",
- vec![event_id],
- )
- }
- RadrootsOrderIssue::RevisionProposalListingMismatch { event_id } => issue_with_events(
- "revision_proposal_listing_mismatch",
- "listing_addr",
- "active order reducer reported revision proposal listing mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionProposalRootMismatch { event_id } => issue_with_events(
- "revision_proposal_root_mismatch",
- "root_event_id",
- "active order reducer reported revision proposal root mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionProposalPreviousMismatch { event_id } => issue_with_events(
- "revision_proposal_previous_mismatch",
- "prev_event_id",
- "active order reducer reported revision proposal previous mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionWithoutProposal { event_id } => issue_with_events(
- "revision_decision_without_proposal",
- "revision_decision_event_id",
- "active order reducer reported revision decision without proposal",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionPayloadInvalid { event_id } => issue_with_events(
- "invalid_revision_decision_payload",
- "revision_decision_payload",
- "active order reducer reported invalid revision decision payload",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionOrderIdMismatch { event_id } => issue_with_events(
- "revision_decision_order_id_mismatch",
- "order_id",
- "active order reducer reported revision decision order id mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionAuthorMismatch { event_id } => issue_with_events(
- "revision_decision_author_mismatch",
- "buyer_pubkey",
- "active order reducer reported revision decision author mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionCounterpartyMismatch { event_id } => issue_with_events(
- "revision_decision_counterparty_mismatch",
- "seller_pubkey",
- "active order reducer reported revision decision counterparty mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionBuyerMismatch { event_id } => issue_with_events(
- "revision_decision_buyer_mismatch",
- "buyer_pubkey",
- "active order reducer reported revision decision buyer mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionSellerMismatch { event_id } => issue_with_events(
- "revision_decision_seller_mismatch",
- "seller_pubkey",
- "active order reducer reported revision decision seller mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionListingAddressInvalid { event_id } => {
- issue_with_events(
- "invalid_revision_decision_listing_address",
- "listing_addr",
- "active order reducer reported invalid revision decision listing address",
- vec![event_id],
- )
- }
- RadrootsOrderIssue::RevisionDecisionListingMismatch { event_id } => issue_with_events(
- "revision_decision_listing_mismatch",
- "listing_addr",
- "active order reducer reported revision decision listing mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionRootMismatch { event_id } => issue_with_events(
- "revision_decision_root_mismatch",
- "root_event_id",
- "active order reducer reported revision decision root mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionPreviousMismatch { event_id } => issue_with_events(
- "revision_decision_previous_mismatch",
- "prev_event_id",
- "active order reducer reported revision decision previous mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::RevisionDecisionRevisionIdMismatch { event_id } => issue_with_events(
- "revision_decision_revision_id_mismatch",
- "revision_id",
- "active order reducer reported revision decision revision id mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationWithoutCancellableOrder { event_id } => issue_with_events(
- "cancellation_without_cancellable_order",
- "cancellation_event_id",
- "active order reducer reported cancellation without cancellable order",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationPayloadInvalid { event_id } => issue_with_events(
- "invalid_cancellation_payload",
- "cancellation_payload",
- "active order reducer reported invalid cancellation payload",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationOrderIdMismatch { event_id } => issue_with_events(
- "cancellation_order_id_mismatch",
- "order_id",
- "active order reducer reported cancellation order id mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationAuthorMismatch { event_id } => issue_with_events(
- "cancellation_author_mismatch",
- "buyer_pubkey",
- "active order reducer reported cancellation author mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationCounterpartyMismatch { event_id } => issue_with_events(
- "cancellation_counterparty_mismatch",
- "seller_pubkey",
- "active order reducer reported cancellation counterparty mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationBuyerMismatch { event_id } => issue_with_events(
- "cancellation_buyer_mismatch",
- "buyer_pubkey",
- "active order reducer reported cancellation buyer mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationSellerMismatch { event_id } => issue_with_events(
- "cancellation_seller_mismatch",
- "seller_pubkey",
- "active order reducer reported cancellation seller mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationListingAddressInvalid { event_id } => issue_with_events(
- "invalid_cancellation_listing_address",
- "listing_addr",
- "active order reducer reported invalid cancellation listing address",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationListingMismatch { event_id } => issue_with_events(
- "cancellation_listing_mismatch",
- "listing_addr",
- "active order reducer reported cancellation listing mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationRootMismatch { event_id } => issue_with_events(
- "cancellation_root_mismatch",
- "root_event_id",
- "active order reducer reported cancellation root mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::CancellationPreviousMismatch { event_id } => issue_with_events(
- "cancellation_previous_mismatch",
- "prev_event_id",
- "active order reducer reported cancellation previous mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::ValidationReceiptWithoutPendingAgreement { event_id } => {
- issue_with_events(
- "validation_receipt_without_pending_agreement",
- "validation_receipt_event_id",
- "active order reducer reported validation receipt without pending agreement",
- vec![event_id],
- )
- }
- RadrootsOrderIssue::ValidationReceiptOrderIdMismatch { event_id } => issue_with_events(
- "validation_receipt_order_id_mismatch",
- "order_id",
- "active order reducer reported validation receipt order id mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::ValidationReceiptTypeMismatch { event_id } => issue_with_events(
- "validation_receipt_type_mismatch",
- "validation_receipt_type",
- "active order reducer reported validation receipt type mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::ValidationReceiptRootMismatch { event_id } => issue_with_events(
- "validation_receipt_root_mismatch",
- "root_event_id",
- "active order reducer reported validation receipt root mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::ValidationReceiptTargetMismatch { event_id } => issue_with_events(
- "validation_receipt_target_mismatch",
- "target_event_id",
- "active order reducer reported validation receipt target mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::ValidationReceiptListingMismatch { event_id } => issue_with_events(
- "validation_receipt_listing_mismatch",
- "listing_event_id",
- "active order reducer reported validation receipt listing mismatch",
- vec![event_id],
- ),
- RadrootsOrderIssue::ConflictingValidationReceipts { event_ids } => issue_with_events(
- "conflicting_validation_receipts",
- "validation_receipt_event_id",
- "active order reducer reported conflicting validation receipts",
- event_ids,
- ),
- RadrootsOrderIssue::DeterministicValidationFailure { event_id, reason } => {
- issue_with_events(
- "deterministic_validation_failure",
- "validation_event_id",
- format!("active order reducer reported deterministic validation failure: {reason}"),
- vec![event_id],
- )
- }
- RadrootsOrderIssue::StaleListingEvent {
- expected_event_id,
- current_event_id,
- } => issue_with_events(
- "stale_listing_event",
- "listing_event_id",
- "active order reducer reported stale listing event evidence",
- vec![expected_event_id, current_event_id],
- ),
- RadrootsOrderIssue::ForkedLifecycle { event_ids } => issue_with_events(
- "forked_lifecycle",
- "event_id",
- "active order reducer reported forked lifecycle events",
- event_ids,
- ),
- }
-}
-
fn order_event_list_unconfigured(
seller_pubkey: Option<String>,
actor_context_source: &'static str,
@@ -3535,7 +2523,7 @@ fn order_event_list_from_receipt(
fn order_decision_base_view(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
state: &str,
dry_run: bool,
) -> OrderDecisionView {
@@ -3572,7 +2560,7 @@ fn order_decision_base_view(
fn order_revision_base_view(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
state: &str,
dry_run: bool,
) -> OrderRevisionProposalView {
@@ -3611,7 +2599,7 @@ fn order_revision_base_view(
fn order_revision_decision_base_view(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
state: &str,
dry_run: bool,
) -> OrderRevisionDecisionView {
@@ -3651,7 +2639,7 @@ fn order_revision_decision_base_view(
fn order_cancellation_base_view(
config: &RuntimeConfig,
- args: &OrderCancelArgs,
+ args: &TradeCancelArgs,
state: &str,
dry_run: bool,
) -> OrderCancellationView {
@@ -3706,7 +2694,7 @@ fn apply_order_cancellation_status(view: &mut OrderCancellationView, status: &Or
fn order_cancellation_prev_event_id(status: &OrderStatusView) -> Option<String> {
match status.state.as_str() {
"requested" => status.request_event_id.clone(),
- "accepted" => status
+ "pending_rhi" => status
.last_event_id
.clone()
.or(status.decision_event_id.clone()),
@@ -3716,7 +2704,7 @@ fn order_cancellation_prev_event_id(status: &OrderStatusView) -> Option<String>
fn order_cancellation_preflight_view_from_status(
config: &RuntimeConfig,
- args: &OrderCancelArgs,
+ args: &TradeCancelArgs,
status: &OrderStatusView,
selected_pubkey: &str,
) -> Option<OrderCancellationView> {
@@ -3726,8 +2714,8 @@ fn order_cancellation_preflight_view_from_status(
.is_some_and(|buyer| buyer.eq_ignore_ascii_case(selected_pubkey));
let state = match status.state.as_str() {
"requested" if buyer_matches => return None,
- "accepted" if buyer_matches => "finalized",
- "cancelled" => "terminal",
+ "pending_rhi" if buyer_matches => "finalized",
+ "committed" | "cancelled" => "terminal",
"missing" | "declined" | "invalid" | "unavailable" | "unconfigured" => {
status.state.as_str()
}
@@ -3743,45 +2731,45 @@ fn order_cancellation_preflight_view_from_status(
view.event_kind = Some(KIND_ORDER_CANCELLATION);
}
view.reason = Some(match state {
- "missing" => format!("no active order events matched `{}`", args.key),
+ "missing" => format!("no active trade events matched `{}`", args.key),
"declined" => format!(
- "order cancel refused because order `{}` was declined",
+ "trade cancel refused because order `{}` was declined",
args.key
),
"terminal" => {
format!(
- "order cancel refused because order `{}` is already terminal",
+ "trade cancel refused because order `{}` is already terminal",
args.key
)
}
"finalized" => format!(
- "order cancel refused because order `{}` already has an accepted agreement",
+ "trade cancel refused because order `{}` already has an accepted agreement",
args.key
),
"invalid" if !buyer_matches && status.buyer_pubkey.is_some() => format!(
- "order cancel refused because selected account is not buyer for order `{}`",
+ "trade cancel refused because selected account is not buyer for order `{}`",
args.key
),
"invalid" => status.reason.clone().unwrap_or_else(|| {
format!(
- "order cancel refused because active order events for `{}` are invalid",
+ "trade cancel refused because active trade events for `{}` are invalid",
args.key
)
}),
_ => status.reason.clone().unwrap_or_else(|| {
format!(
- "order cancel status preflight failed with state `{}`",
+ "trade cancel status preflight failed with state `{}`",
status.state
)
}),
});
- view.actions = vec![format!("radroots order status get {}", args.key)];
+ view.actions = vec![format!("radroots trade status get {}", args.key)];
Some(view)
}
fn order_decision_view_from_resolution(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
seller_pubkey: String,
resolution: SellerOrderRequestResolution,
) -> OrderDecisionView {
@@ -3811,7 +2799,7 @@ fn order_decision_view_from_resolution(
"seller order request preflight found invalid request candidates for `{}`",
args.key
));
- view.actions = vec![format!("radroots order status get {}", args.key)];
+ view.actions = vec![format!("radroots trade status get {}", args.key)];
return view;
}
match requests.as_slice() {
@@ -3842,7 +2830,7 @@ fn order_decision_view_from_resolution(
),
event_ids,
)];
- view.actions = vec![format!("radroots order status get {}", args.key)];
+ view.actions = vec![format!("radroots trade status get {}", args.key)];
view
}
}
@@ -3931,14 +2919,14 @@ fn apply_order_revision_decision_status(
fn order_decision_preflight_view_from_status(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: &ResolvedSellerOrderRequest,
resolution: &SellerOrderRequestResolution,
status: &OrderStatusView,
) -> Option<OrderDecisionView> {
let state = match status.state.as_str() {
- "accepted" | "declined" => "already_decided",
- "cancelled" => "terminal",
+ "pending_rhi" | "declined" => "already_decided",
+ "committed" | "cancelled" => "terminal",
"invalid" => "invalid",
"unavailable" => "unavailable",
"unconfigured" => "unconfigured",
@@ -3953,20 +2941,20 @@ fn order_decision_preflight_view_from_status(
view.event_kind = Some(KIND_ORDER_DECISION);
}
view.reason = Some(match status.state.as_str() {
- "accepted" | "declined" => format!(
+ "pending_rhi" | "declined" => format!(
"order {} refused because order `{}` already has a visible `{}` seller decision",
args.decision.command(),
request.order_id,
status.state
),
- "cancelled" => format!(
+ "committed" | "cancelled" => format!(
"order {} refused because order `{}` is already terminal",
args.decision.command(),
request.order_id
),
"invalid" => status.reason.clone().unwrap_or_else(|| {
format!(
- "order {} refused because active order events for `{}` are invalid",
+ "order {} refused because active trade events for `{}` are invalid",
args.decision.command(),
request.order_id
)
@@ -3979,13 +2967,13 @@ fn order_decision_preflight_view_from_status(
)
}),
});
- view.actions = vec![format!("radroots order status get {}", request.order_id)];
+ view.actions = vec![format!("radroots trade status get {}", request.order_id)];
Some(view)
}
fn order_revision_args_preflight_view(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
) -> Option<OrderRevisionProposalView> {
let mut issues = Vec::new();
let has_bin_id = args.bin_id.as_deref().and_then(non_empty_ref).is_some();
@@ -4030,7 +3018,7 @@ fn order_revision_args_preflight_view(
issues.push(issue_with_code(
"revision_no_changes",
"revision",
- "order revision propose requires a bin-count change or revision adjustment",
+ "trade revision propose requires a bin-count change or revision adjustment",
));
}
@@ -4039,7 +3027,7 @@ fn order_revision_args_preflight_view(
}
let mut view = order_revision_base_view(config, args, "invalid", config.output.dry_run);
view.reason = Some(format!(
- "order revision propose inputs for `{}` failed validation",
+ "trade revision propose inputs for `{}` failed validation",
args.key
));
view.issues = issues;
@@ -4048,17 +3036,17 @@ fn order_revision_args_preflight_view(
fn order_revision_decision_args_preflight_view(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
) -> Option<OrderRevisionDecisionView> {
let mut issues = Vec::new();
if args.revision_id.trim().is_empty() {
issues.push(issue_with_code(
"revision_id_required",
"revision_id",
- "order revision decision requires --revision-id",
+ "trade revision decision requires --revision-id",
));
}
- if args.decision == OrderRevisionDecisionArg::Decline
+ if args.decision == TradeRevisionDecisionArg::Decline
&& args
.reason
.as_deref()
@@ -4069,7 +3057,7 @@ fn order_revision_decision_args_preflight_view(
issues.push(issue_with_code(
"revision_decline_reason_required",
"reason",
- "order revision decline requires a non-empty reason",
+ "trade revision decline requires a non-empty reason",
));
}
@@ -4079,7 +3067,7 @@ fn order_revision_decision_args_preflight_view(
let mut view =
order_revision_decision_base_view(config, args, "invalid", config.output.dry_run);
view.reason = Some(format!(
- "order revision {} inputs for `{}` failed validation",
+ "trade revision {} inputs for `{}` failed validation",
args.decision.command(),
args.key
));
@@ -4089,7 +3077,7 @@ fn order_revision_decision_args_preflight_view(
fn order_revision_preflight_view_from_status(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
status: &OrderStatusView,
selected_pubkey: &str,
candidates: &OrderRevisionProposalCandidates,
@@ -4100,15 +3088,15 @@ fn order_revision_preflight_view_from_status(
.as_deref()
.is_some_and(|seller| seller.eq_ignore_ascii_case(selected_pubkey));
let state = match status.state.as_str() {
- "accepted"
+ "pending_rhi"
if seller_matches && candidates.issues.is_empty() && pending_revision.is_none() =>
{
return None;
}
- "accepted" if !seller_matches => "invalid",
- "accepted" if !candidates.issues.is_empty() => "invalid",
- "accepted" if pending_revision.is_some() => "forked",
- "cancelled" => "terminal",
+ "pending_rhi" if !seller_matches => "invalid",
+ "pending_rhi" if !candidates.issues.is_empty() => "invalid",
+ "pending_rhi" if pending_revision.is_some() => "forked",
+ "committed" | "cancelled" => "terminal",
"missing" | "requested" | "declined" | "invalid" | "unavailable" | "unconfigured" => {
status.state.as_str()
}
@@ -4122,40 +3110,40 @@ fn order_revision_preflight_view_from_status(
view.revision_id = Some(record.payload.revision_id.to_string());
}
view.reason = Some(match state {
- "missing" => format!("no active order events matched `{}`", args.key),
+ "missing" => format!("no active trade events matched `{}`", args.key),
"requested" => format!(
- "order revision propose refused because order `{}` has no accepted seller decision",
+ "trade revision propose refused because order `{}` has no accepted seller decision",
args.key
),
"declined" => format!(
- "order revision propose refused because order `{}` was declined",
+ "trade revision propose refused because order `{}` was declined",
args.key
),
"terminal" => format!(
- "order revision propose refused because order `{}` is already terminal",
+ "trade revision propose refused because order `{}` is already terminal",
args.key
),
"forked" => format!(
- "order revision propose refused because order `{}` already has a pending revision proposal",
+ "trade revision propose refused because order `{}` already has a pending revision proposal",
args.key
),
"invalid" if !seller_matches && status.seller_pubkey.is_some() => format!(
- "order revision propose refused because selected account is not seller for order `{}`",
+ "trade revision propose refused because selected account is not seller for order `{}`",
args.key
),
"invalid" if !candidates.issues.is_empty() => format!(
- "order revision propose refused because revision proposal candidates for `{}` are invalid",
+ "trade revision propose refused because revision proposal candidates for `{}` are invalid",
args.key
),
"invalid" => status.reason.clone().unwrap_or_else(|| {
format!(
- "order revision propose refused because active order events for `{}` are invalid",
+ "trade revision propose refused because active trade events for `{}` are invalid",
args.key
)
}),
_ => status.reason.clone().unwrap_or_else(|| {
format!(
- "order revision propose status preflight failed with state `{}`",
+ "trade revision propose status preflight failed with state `{}`",
status.state
)
}),
@@ -4174,13 +3162,13 @@ fn order_revision_preflight_view_from_status(
));
}
view.issues.extend(candidates.issues.clone());
- view.actions = vec![format!("radroots order status get {}", args.key)];
+ view.actions = vec![format!("radroots trade status get {}", args.key)];
Some(view)
}
fn order_revision_decision_preflight_view_from_status(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
status: &OrderStatusView,
selected_pubkey: &str,
candidates: &OrderRevisionProposalCandidates,
@@ -4191,17 +3179,17 @@ fn order_revision_decision_preflight_view_from_status(
.as_deref()
.is_some_and(|buyer| buyer.eq_ignore_ascii_case(selected_pubkey));
let state = match status.state.as_str() {
- "accepted"
+ "revision_proposed"
if buyer_matches && candidates.issues.is_empty() && pending_revision.is_some() =>
{
return None;
}
- "accepted" if !buyer_matches => "invalid",
- "accepted" if !candidates.issues.is_empty() => "invalid",
- "accepted" => "missing",
- "cancelled" => "terminal",
+ "revision_proposed" if !buyer_matches => "invalid",
+ "revision_proposed" if !candidates.issues.is_empty() => "invalid",
+ "revision_proposed" => "missing",
+ "committed" | "cancelled" => "terminal",
"declined" => "order_declined",
- "missing" | "requested" | "invalid" | "unavailable" | "unconfigured" => {
+ "missing" | "requested" | "pending_rhi" | "invalid" | "unavailable" | "unconfigured" => {
status.state.as_str()
}
_ => "invalid",
@@ -4214,54 +3202,54 @@ fn order_revision_decision_preflight_view_from_status(
view.event_kind = Some(KIND_ORDER_REVISION_PROPOSAL);
}
view.reason = Some(match state {
- "missing" if status.state == "accepted" => format!(
- "order revision {} refused because order `{}` has no pending revision proposal",
+ "missing" if status.state == "revision_proposed" => format!(
+ "trade revision {} refused because order `{}` has no pending revision proposal",
args.decision.command(),
args.key
),
- "missing" => format!("no active order events matched `{}`", args.key),
+ "missing" => format!("no active trade events matched `{}`", args.key),
"requested" => format!(
- "order revision {} refused because order `{}` has no accepted seller decision",
+ "trade revision {} refused because order `{}` has no accepted seller decision",
args.decision.command(),
args.key
),
"order_declined" => format!(
- "order revision {} refused because order `{}` was declined",
+ "trade revision {} refused because order `{}` was declined",
args.decision.command(),
args.key
),
"terminal" => format!(
- "order revision {} refused because order `{}` is already terminal",
+ "trade revision {} refused because order `{}` is already terminal",
args.decision.command(),
args.key
),
"invalid" if !buyer_matches && status.buyer_pubkey.is_some() => format!(
- "order revision {} refused because selected account is not buyer for order `{}`",
+ "trade revision {} refused because selected account is not buyer for order `{}`",
args.decision.command(),
args.key
),
"invalid" if !candidates.issues.is_empty() => format!(
- "order revision {} refused because revision proposal candidates for `{}` are invalid",
+ "trade revision {} refused because revision proposal candidates for `{}` are invalid",
args.decision.command(),
args.key
),
"invalid" => status.reason.clone().unwrap_or_else(|| {
format!(
- "order revision {} refused because active order events for `{}` are invalid",
+ "trade revision {} refused because active trade events for `{}` are invalid",
args.decision.command(),
args.key
)
}),
_ => status.reason.clone().unwrap_or_else(|| {
format!(
- "order revision {} status preflight failed with state `{}`",
+ "trade revision {} status preflight failed with state `{}`",
args.decision.command(),
status.state
)
}),
});
view.issues.extend(candidates.issues.clone());
- view.actions = vec![format!("radroots order status get {}", args.key)];
+ view.actions = vec![format!("radroots trade status get {}", args.key)];
Some(view)
}
@@ -4278,12 +3266,12 @@ fn pending_revision_proposal_candidate<'a>(
fn order_accept_inventory_preflight_view(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: &ResolvedSellerOrderRequest,
resolution: &SellerOrderRequestResolution,
status: &OrderStatusView,
) -> Result<OrderDecisionInventoryPreflight, RuntimeError> {
- if args.decision != OrderDecisionArg::Accept {
+ if args.decision != TradeDecisionArg::Accept {
return Ok(OrderDecisionInventoryPreflight {
invalid_view: None,
inventory: Some(order_declined_inventory_view(request)),
@@ -4400,7 +3388,7 @@ fn order_accept_inventory_preflight_view(
fn order_accept_inventory_preflight_view_from_projection(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: &ResolvedSellerOrderRequest,
resolution: &SellerOrderRequestResolution,
status: &OrderStatusView,
@@ -4479,19 +3467,19 @@ fn order_declined_inventory_view(request: &ResolvedSellerOrderRequest) -> OrderI
}
fn order_decision_inventory_for_view(
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: &ResolvedSellerOrderRequest,
inventory: Option<OrderInventoryView>,
) -> Option<OrderInventoryView> {
match args.decision {
- OrderDecisionArg::Accept => inventory,
- OrderDecisionArg::Decline => Some(order_declined_inventory_view(request)),
+ TradeDecisionArg::Accept => inventory,
+ TradeDecisionArg::Decline => Some(order_declined_inventory_view(request)),
}
}
fn fetch_current_inventory_listing(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: &ResolvedSellerOrderRequest,
resolution: &SellerOrderRequestResolution,
status: &OrderStatusView,
@@ -4748,7 +3736,7 @@ fn proposed_accept_decision_record(
fn order_decision_inventory_invalid_view(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: &ResolvedSellerOrderRequest,
resolution: &SellerOrderRequestResolution,
status: &OrderStatusView,
@@ -4761,7 +3749,7 @@ fn order_decision_inventory_invalid_view(
apply_order_decision_status(&mut view, status);
view.reason = Some(reason.into());
view.issues.extend(issues);
- view.actions = vec![format!("radroots order status get {}", request.order_id)];
+ view.actions = vec![format!("radroots trade status get {}", request.order_id)];
view
}
@@ -4812,7 +3800,7 @@ fn listing_inventory_accounting_issue_view(
fn order_decision_dry_run_view(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: &ResolvedSellerOrderRequest,
status: &OrderStatusView,
inventory: Option<OrderInventoryView>,
@@ -4832,13 +3820,13 @@ fn order_decision_dry_run_view(
),
None => "dry run requested; seller order decision publication skipped".to_owned(),
});
- view.actions = vec![format!("radroots order status get {}", request.order_id)];
+ view.actions = vec![format!("radroots trade status get {}", request.order_id)];
view
}
fn order_revision_invalid_view(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
status: &OrderStatusView,
reason: impl Into<String>,
issues: Vec<OrderIssueView>,
@@ -4847,13 +3835,13 @@ fn order_revision_invalid_view(
apply_order_revision_status(&mut view, status);
view.reason = Some(reason.into());
view.issues.extend(issues);
- view.actions = vec![format!("radroots order status get {}", args.key)];
+ view.actions = vec![format!("radroots trade status get {}", args.key)];
view
}
fn order_revision_decision_invalid_view(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
status: &OrderStatusView,
reason: impl Into<String>,
issues: Vec<OrderIssueView>,
@@ -4863,13 +3851,13 @@ fn order_revision_decision_invalid_view(
apply_order_revision_decision_status(&mut view, status);
view.reason = Some(reason.into());
view.issues.extend(issues);
- view.actions = vec![format!("radroots order status get {}", args.key)];
+ view.actions = vec![format!("radroots trade status get {}", args.key)];
view
}
fn order_revision_dry_run_view(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
status: &OrderStatusView,
payload: &RadrootsOrderRevisionProposal,
) -> OrderRevisionProposalView {
@@ -4878,13 +3866,13 @@ fn order_revision_dry_run_view(
apply_order_revision_payload(&mut view, payload);
view.reason =
Some("dry run requested; seller revision proposal publication skipped".to_owned());
- view.actions = vec![format!("radroots order status get {}", status.order_id)];
+ view.actions = vec![format!("radroots trade status get {}", status.order_id)];
view
}
fn order_revision_decision_dry_run_view(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
status: &OrderStatusView,
proposal: &OrderRevisionProposalRecord,
payload: &RadrootsOrderRevisionDecision,
@@ -4896,25 +3884,25 @@ fn order_revision_decision_dry_run_view(
"dry run requested; buyer revision {} publication skipped",
args.decision.command()
));
- view.actions = vec![format!("radroots order status get {}", status.order_id)];
+ view.actions = vec![format!("radroots trade status get {}", status.order_id)];
view
}
fn order_cancellation_dry_run_view(
config: &RuntimeConfig,
- args: &OrderCancelArgs,
+ args: &TradeCancelArgs,
status: &OrderStatusView,
) -> OrderCancellationView {
let mut view = order_cancellation_base_view(config, args, "dry_run", true);
apply_order_cancellation_status(&mut view, status);
view.reason =
- Some("dry run requested; buyer order cancellation publication skipped".to_owned());
- view.actions = vec![format!("radroots order status get {}", status.order_id)];
+ Some("dry run requested; buyer trade cancellation publication skipped".to_owned());
+ view.actions = vec![format!("radroots trade status get {}", status.order_id)];
view
}
fn order_cancellation_payload_from_status(
- args: &OrderCancelArgs,
+ args: &TradeCancelArgs,
status: &OrderStatusView,
) -> Result<RadrootsOrderCancellation, RuntimeError> {
Ok(RadrootsOrderCancellation {
@@ -4942,7 +3930,7 @@ fn order_cancellation_payload_from_status(
}
fn order_revision_payload_from_status(
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
status: &OrderStatusView,
) -> Result<RadrootsOrderRevisionProposal, RuntimeError> {
let revision_id = protocol_revision_id(next_revision_id().as_str(), "revision_id")?;
@@ -5002,7 +3990,7 @@ fn order_revision_payload_from_status(
}
fn revised_order_economics(
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
revision_id: &str,
current: &RadrootsOrderEconomics,
) -> Result<RadrootsOrderEconomics, RuntimeError> {
@@ -5061,14 +4049,14 @@ fn revised_order_economics(
.map_err(|error| RuntimeError::Config(format!("build revision economics: {error}")))?;
if !changed {
return Err(RuntimeError::Config(
- "order revision propose requires a changed item count or adjustment".to_owned(),
+ "trade revision propose requires a changed item count or adjustment".to_owned(),
));
}
Ok(economics)
}
fn revision_adjustment_line(
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
expected_currency: RadrootsCoreCurrency,
) -> Result<Option<RadrootsOrderEconomicLine>, RuntimeError> {
let Some(id) = args.adjustment_id.as_deref().and_then(non_empty_ref) else {
@@ -5149,17 +4137,17 @@ fn order_revision_event_parts(
let prev_event_id = protocol_event_id(prev_event_id, "prev_event_id")?;
if payload.root_event_id != root_event_id || payload.prev_event_id != prev_event_id {
return Err(RuntimeError::Config(
- "order revision proposal payload chain does not match order status".to_owned(),
+ "trade revision proposal payload chain does not match order status".to_owned(),
));
}
order_revision_proposal_event_build(&root_event_id, &prev_event_id, payload).map_err(|error| {
- RuntimeError::Config(format!("encode order revision proposal event: {error}"))
+ RuntimeError::Config(format!("encode trade revision proposal event: {error}"))
})
}
fn order_revision_inventory_preflight_view(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
status: &OrderStatusView,
payload: &RadrootsOrderRevisionProposal,
) -> Option<OrderRevisionProposalView> {
@@ -5171,7 +4159,7 @@ fn order_revision_inventory_preflight_view(
config,
args,
status,
- "order revision propose refused because visible inventory is unavailable for the revised items",
+ "trade revision propose refused because visible inventory is unavailable for the revised items",
issues,
);
apply_order_revision_payload(&mut view, payload);
@@ -5302,12 +4290,12 @@ fn apply_order_revision_decision_payload(
}
fn order_revision_decision_payload_from_proposal(
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
proposal: &OrderRevisionProposalRecord,
) -> Result<RadrootsOrderRevisionDecision, RuntimeError> {
let decision = match args.decision {
- OrderRevisionDecisionArg::Accept => RadrootsOrderRevisionOutcome::Accepted,
- OrderRevisionDecisionArg::Decline => {
+ TradeRevisionDecisionArg::Accept => RadrootsOrderRevisionOutcome::Accepted,
+ TradeRevisionDecisionArg::Decline => {
let reason = args
.reason
.as_deref()
@@ -5315,7 +4303,7 @@ fn order_revision_decision_payload_from_proposal(
.filter(|reason| !reason.is_empty())
.ok_or_else(|| {
RuntimeError::Config(
- "order revision decline requires a non-empty reason".to_owned(),
+ "trade revision decline requires a non-empty reason".to_owned(),
)
})?;
RadrootsOrderRevisionOutcome::Declined {
@@ -5340,13 +4328,13 @@ fn order_revision_decision_event_parts(
) -> Result<WireEventParts, RuntimeError> {
order_revision_decision_event_build(&payload.root_event_id, &payload.prev_event_id, payload)
.map_err(|error| {
- RuntimeError::Config(format!("encode order revision decision event: {error}"))
+ RuntimeError::Config(format!("encode trade revision decision event: {error}"))
})
}
fn publish_order_revision(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
status: OrderStatusView,
signing: account::AccountSigningIdentity,
payload: RadrootsOrderRevisionProposal,
@@ -5358,7 +4346,7 @@ fn publish_order_revision(
fn publish_order_revision_decision(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
status: OrderStatusView,
proposal: &OrderRevisionProposalRecord,
signing: account::AccountSigningIdentity,
@@ -5379,7 +4367,7 @@ fn publish_order_revision_decision(
fn publish_order_cancellation(
config: &RuntimeConfig,
- args: &OrderCancelArgs,
+ args: &TradeCancelArgs,
status: OrderStatusView,
signing: account::AccountSigningIdentity,
payload: RadrootsOrderCancellation,
@@ -5469,7 +4457,7 @@ fn prepare_order_cancellation_dry_run_via_sdk(
fn enqueue_order_revision_proposal_via_sdk(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
status: OrderStatusView,
signing: account::AccountSigningIdentity,
payload: RadrootsOrderRevisionProposal,
@@ -5513,7 +4501,7 @@ fn enqueue_order_revision_proposal_via_sdk(
fn enqueue_order_revision_decision_via_sdk(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
status: OrderStatusView,
proposal: &OrderRevisionProposalRecord,
signing: account::AccountSigningIdentity,
@@ -5559,7 +4547,7 @@ fn enqueue_order_revision_decision_via_sdk(
fn enqueue_order_cancellation_via_sdk(
config: &RuntimeConfig,
- args: &OrderCancelArgs,
+ args: &TradeCancelArgs,
status: OrderStatusView,
signing: account::AccountSigningIdentity,
payload: RadrootsOrderCancellation,
@@ -5682,7 +4670,7 @@ fn push_one_sdk_outbox_event(
fn sdk_enqueued_order_revision_view(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
status: &OrderStatusView,
payload: &RadrootsOrderRevisionProposal,
enqueue: OrderRevisionProposalReceipt,
@@ -5711,14 +4699,14 @@ fn sdk_enqueued_order_revision_view(
.unwrap_or_default();
view.failed_relays = push_event.map(sdk_push_failed_relays).unwrap_or_default();
view.reason =
- sdk_order_lifecycle_reason("order revision proposal", &enqueue.workflow, push_event);
+ sdk_order_lifecycle_reason("trade revision proposal", &enqueue.workflow, push_event);
view.actions = sdk_order_lifecycle_actions(push_event);
view
}
fn sdk_enqueued_order_revision_decision_view(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
status: &OrderStatusView,
proposal: &OrderRevisionProposalRecord,
payload: &RadrootsOrderRevisionDecision,
@@ -5755,14 +4743,14 @@ fn sdk_enqueued_order_revision_decision_view(
.unwrap_or_default();
view.failed_relays = push_event.map(sdk_push_failed_relays).unwrap_or_default();
view.reason =
- sdk_order_lifecycle_reason("order revision decision", &enqueue.workflow, push_event);
+ sdk_order_lifecycle_reason("trade revision decision", &enqueue.workflow, push_event);
view.actions = sdk_order_lifecycle_actions(push_event);
view
}
fn sdk_enqueued_order_cancellation_view(
config: &RuntimeConfig,
- args: &OrderCancelArgs,
+ args: &TradeCancelArgs,
status: &OrderStatusView,
enqueue: OrderCancellationReceipt,
push: PushOutboxReceipt,
@@ -5788,7 +4776,7 @@ fn sdk_enqueued_order_cancellation_view(
.map(sdk_push_acknowledged_relays)
.unwrap_or_default();
view.failed_relays = push_event.map(sdk_push_failed_relays).unwrap_or_default();
- view.reason = sdk_order_lifecycle_reason("order cancellation", &enqueue.workflow, push_event);
+ view.reason = sdk_order_lifecycle_reason("trade cancellation", &enqueue.workflow, push_event);
view.actions = sdk_order_lifecycle_actions(push_event);
view
}
@@ -5914,7 +4902,7 @@ fn order_actor_write_binding_error_parts(
fn order_revision_binding_error_view(
config: &RuntimeConfig,
- args: &OrderRevisionProposeArgs,
+ args: &TradeRevisionProposeArgs,
status: &OrderStatusView,
error: ActorWriteBindingError,
) -> OrderRevisionProposalView {
@@ -5928,7 +4916,7 @@ fn order_revision_binding_error_view(
fn order_revision_decision_binding_error_view(
config: &RuntimeConfig,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
status: &OrderStatusView,
error: ActorWriteBindingError,
) -> OrderRevisionDecisionView {
@@ -5943,7 +4931,7 @@ fn order_revision_decision_binding_error_view(
fn order_cancellation_binding_error_view(
config: &RuntimeConfig,
- args: &OrderCancelArgs,
+ args: &TradeCancelArgs,
status: &OrderStatusView,
error: ActorWriteBindingError,
) -> OrderCancellationView {
@@ -6084,7 +5072,7 @@ fn seller_order_request_from_event(
fn publish_order_decision(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: ResolvedSellerOrderRequest,
resolution: SellerOrderRequestResolution,
signing: account::AccountSigningIdentity,
@@ -6167,7 +5155,7 @@ fn order_decision_relay_url_policy(target_relays: &[String]) -> SdkRelayUrlPolic
fn enqueue_order_decision_via_sdk(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request_context: ResolvedSellerOrderRequest,
resolution: SellerOrderRequestResolution,
signing: account::AccountSigningIdentity,
@@ -6230,7 +5218,7 @@ fn cli_sdk_error_to_runtime(error: CliSdkAdapterError) -> RuntimeError {
}
fn canonical_order_decision_payload(
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: &ResolvedSellerOrderRequest,
signer_pubkey: &str,
) -> Result<RadrootsOrderDecision, RuntimeError> {
@@ -6240,12 +5228,12 @@ fn canonical_order_decision_payload(
}
fn order_decision_payload_from_request(
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: &ResolvedSellerOrderRequest,
) -> Result<RadrootsOrderDecision, RuntimeError> {
match args.decision {
- OrderDecisionArg::Accept => Ok(accepted_order_decision_payload_from_request(request)),
- OrderDecisionArg::Decline => {
+ TradeDecisionArg::Accept => Ok(accepted_order_decision_payload_from_request(request)),
+ TradeDecisionArg::Decline => {
let reason = args
.reason
.as_deref()
@@ -6299,7 +5287,7 @@ fn declined_order_decision_payload_from_request(
fn sdk_enqueued_order_decision_view(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: ResolvedSellerOrderRequest,
resolution: SellerOrderRequestResolution,
enqueue: OrderDecisionReceipt,
@@ -6346,7 +5334,7 @@ fn sdk_push_event_for_order_decision<'a>(
}
fn sdk_order_decision_state(
- decision: OrderDecisionArg,
+ decision: TradeDecisionArg,
push_event: Option<&PushOutboxEventReceipt>,
) -> String {
match push_event.map(|event| event.final_state) {
@@ -6400,7 +5388,7 @@ fn sdk_order_decision_actions(push_event: Option<&PushOutboxEventReceipt>) -> Ve
fn order_decision_binding_error_view(
config: &RuntimeConfig,
- args: &OrderDecisionArgs,
+ args: &TradeDecisionArgs,
request: ResolvedSellerOrderRequest,
resolution: SellerOrderRequestResolution,
error: ActorWriteBindingError,
@@ -6421,7 +5409,7 @@ fn order_event_list_entry_from_event(
let event_kind = event_kind_u32(event);
if event_kind != KIND_ORDER_REQUEST {
return Err(RuntimeError::Config(format!(
- "order event list received unexpected kind `{event_kind}`"
+ "trade event list received unexpected kind `{event_kind}`"
)));
}
@@ -6566,7 +5554,7 @@ fn order_evidence_from_relay_events(events: &[RadrootsNostrEvent]) -> Vec<SdkRad
fn validate_scaffold_args(args: &OrderDraftCreateArgs) -> Result<(), RuntimeError> {
match (normalize_optional(args.bin_id.as_deref()), args.bin_count) {
(None, Some(_)) => Err(RuntimeError::Config(
- "`--qty` requires `--bin` when creating an order draft".to_owned(),
+ "`--qty` requires `--bin` when creating an trade draft".to_owned(),
)),
(Some(_), Some(0)) => Err(RuntimeError::Config(
"`--qty` must be greater than zero".to_owned(),
@@ -6624,7 +5612,7 @@ fn resolve_order_listing(
if !config.local.replica_db_path.exists() {
return Err(RuntimeError::Config(format!(
- "order listing lookup `{listing_lookup}` requires local market data; run `radroots store init` and `radroots market refresh` before creating an order from a listing"
+ "trade listing lookup `{listing_lookup}` requires local market data; run `radroots store init` and `radroots market refresh` before creating a trade from a listing"
)));
}
@@ -6660,7 +5648,7 @@ fn resolve_order_listing(
)?
.ok_or_else(|| {
RuntimeError::Config(format!(
- "listing `{listing_lookup}` is missing the latest listing event pointer; run `radroots market refresh` before creating an order from this listing"
+ "listing `{listing_lookup}` is missing the latest listing event pointer; run `radroots market refresh` before creating a trade from this listing"
))
})?;
let shared_provenance = resolve_shared_signed_listing_provenance(
@@ -7452,7 +6440,7 @@ fn load_app_order_record_from_record(
source_issues.push(issue_with_code(
"invalid_app_order_record",
"document",
- "app-authored order record is missing document",
+ "app-authored trade record is missing document",
));
placeholder_app_order_document(&record)
}
@@ -7480,14 +6468,14 @@ fn app_order_record_source_issues(
issues.push(issue_with_code(
"app_order_unsupported",
"source_runtime",
- "order record must come from radroots_studio_app",
+ "trade record must come from radroots_studio_app",
));
}
if record.family != LocalRecordFamily::LocalWork {
issues.push(issue_with_code(
"app_order_unsupported",
"family",
- "order record must be shared local work",
+ "trade record must be shared local work",
));
}
if record.status != LocalRecordStatus::LocalSaved {
@@ -7495,7 +6483,7 @@ fn app_order_record_source_issues(
"app_order_unsupported",
"status",
format!(
- "order record status `{}` is not consumable as local saved work",
+ "trade record status `{}` is not consumable as local saved work",
record.status.as_str()
),
));
@@ -7504,7 +6492,7 @@ fn app_order_record_source_issues(
issues.push(issue_with_code(
"invalid_app_order_record",
"local_work_json",
- "app-authored order record is missing local work payload",
+ "app-authored trade record is missing local work payload",
));
return Ok(issues);
};
@@ -7513,14 +6501,14 @@ fn app_order_record_source_issues(
issues.push(issue_with_code(
"app_order_stale",
"currentness.current",
- "app-authored order record is not marked current",
+ "app-authored trade record is not marked current",
));
}
if payload["currentness"]["record_id"].as_str() != Some(record.record_id.as_str()) {
issues.push(issue_with_code(
"invalid_app_order_record",
"currentness.record_id",
- "app-authored order record currentness id does not match the shared record id",
+ "app-authored trade record currentness id does not match the shared record id",
));
}
if current {
@@ -7536,7 +6524,7 @@ fn app_order_record_source_issues(
issues.push(issue_with_code(
"app_order_unsupported",
"support_status.state",
- "app-authored order record is not marked supported",
+ "app-authored trade record is not marked supported",
));
for support_issue in support_issues {
if let Some(support_issue) = support_issue.as_str() {
@@ -7564,7 +6552,7 @@ fn app_order_record_source_issues(
"app_order_stale",
"record_id",
format!(
- "app-authored local order record `{}` was superseded by `{}`",
+ "app-authored local trade record `{}` was superseded by `{}`",
record.record_id, current_record.record_id
),
));
@@ -7794,22 +6782,22 @@ fn app_order_record_summary(
};
let actions = if exportable {
vec![
- format!("radroots order get {}", document.order.order_id),
- format!("radroots order app export {}", record.record_id),
+ format!("radroots trade get {}", document.order.order_id),
+ format!("radroots trade app export {}", record.record_id),
format!(
- "radroots --relay wss://relay.example.com order submit {}",
+ "radroots --relay wss://relay.example.com trade submit {}",
document.order.order_id
),
]
} else if app_order_issue_present(&issues, APP_ORDER_ALREADY_SUBMITTED_ISSUE) {
vec![format!(
- "radroots order status get {}",
+ "radroots trade status get {}",
document.order.order_id
)]
} else if app_order_issue_present(&issues, APP_ORDER_SIGNED_EVIDENCE_CONFLICT_ISSUE) {
vec![
- format!("radroots order status get {}", document.order.order_id),
- "radroots order app list".to_owned(),
+ format!("radroots trade status get {}", document.order.order_id),
+ "radroots trade app list".to_owned(),
]
} else {
Vec::new()
@@ -7913,16 +6901,16 @@ fn app_order_export_failure_actions(
) -> Vec<String> {
if app_order_issue_present(issues, APP_ORDER_ALREADY_SUBMITTED_ISSUE) {
vec![format!(
- "radroots order status get {}",
+ "radroots trade status get {}",
document.order.order_id
)]
} else if app_order_issue_present(issues, APP_ORDER_SIGNED_EVIDENCE_CONFLICT_ISSUE) {
vec![
- format!("radroots order status get {}", document.order.order_id),
- "radroots order app list".to_owned(),
+ format!("radroots trade status get {}", document.order.order_id),
+ "radroots trade app list".to_owned(),
]
} else {
- vec!["radroots order app list".to_owned()]
+ vec!["radroots trade app list".to_owned()]
}
}
@@ -7939,14 +6927,14 @@ fn order_export_output_path(
fn validate_order_export_output_target(output_path: &Path) -> Result<(), RuntimeError> {
if output_path.exists() {
return Err(RuntimeError::Config(format!(
- "order draft output {} must not already exist",
+ "trade draft output {} must not already exist",
output_path.display()
)));
}
if let Some(parent) = output_path.parent() {
if parent.exists() && !parent.is_dir() {
return Err(RuntimeError::Config(format!(
- "order draft parent {} is not a directory",
+ "trade draft parent {} is not a directory",
parent.display()
)));
}
@@ -8102,7 +7090,7 @@ fn collect_issues(document: &OrderDraftDocument) -> Vec<OrderIssueView> {
Ok(parsed) => {
if parsed.kind != KIND_LISTING {
issues.push(issue(
- "order.listing_addr",
+ "trade.listing_addr",
"listing_addr must reference a public NIP-99 listing",
));
}
@@ -8117,13 +7105,13 @@ fn collect_issues(document: &OrderDraftDocument) -> Vec<OrderIssueView> {
}
}
Err(error) => issues.push(issue(
- "order.listing_addr",
+ "trade.listing_addr",
format!("listing_addr is invalid: {error}"),
)),
},
None => issues.push(issue(
- "order.listing_addr",
- "listing_addr is required before order submit",
+ "trade.listing_addr",
+ "listing_addr is required before trade submit",
)),
}
@@ -8131,27 +7119,27 @@ fn collect_issues(document: &OrderDraftDocument) -> Vec<OrderIssueView> {
Some(listing_event_id) => {
if !is_valid_event_id(listing_event_id.as_str()) {
issues.push(issue(
- "order.listing_event_id",
+ "trade.listing_event_id",
"listing_event_id must be a 64-character hex Nostr event id",
));
}
}
None => issues.push(issue(
- "order.listing_event_id",
- "latest active listing event id is required before order submit; run `radroots market refresh` and create the order from local market data",
+ "trade.listing_event_id",
+ "latest active listing event id is required before trade submit; run `radroots market refresh` and create the trade from local market data",
)),
}
match normalize_listing_relay_set(document.order.listing_relays.iter()) {
Ok(listing_relays) if listing_relays.is_empty() => issues.push(issue_with_code(
"listing_provenance_missing",
- "order.listing_relays",
- "listing relay provenance is required before order submit; run `radroots market refresh` and create the order from current local market data",
+ "trade.listing_relays",
+ "listing relay provenance is required before trade submit; run `radroots market refresh` and create the trade from current local market data",
)),
Ok(_) => {}
Err(error) => issues.push(issue_with_code(
"listing_provenance_invalid",
- "order.listing_relays",
+ "trade.listing_relays",
format!("listing relay provenance is invalid: {error}"),
)),
}
@@ -8159,7 +7147,7 @@ fn collect_issues(document: &OrderDraftDocument) -> Vec<OrderIssueView> {
if document.order.items.is_empty() {
issues.push(issue(
"order.items",
- "at least one order item is required before order submit",
+ "at least one order item is required before trade submit",
));
}
for (index, item) in document.order.items.iter().enumerate() {
@@ -8194,26 +7182,26 @@ fn collect_issues(document: &OrderDraftDocument) -> Vec<OrderIssueView> {
}
None => issues.push(issue(
"order.economics",
- "quote economics is required before order submit; run `radroots basket quote create` from current local market data",
+ "quote economics is required before trade submit; run `radroots basket quote create` from current local market data",
)),
}
if document.buyer_actor.account_id.trim().is_empty() {
issues.push(issue(
"buyer_actor.account_id",
- "buyer_actor account_id is required before order submit",
+ "buyer_actor account_id is required before trade submit",
));
}
if document.buyer_actor.pubkey.trim().is_empty() {
issues.push(issue(
"buyer_actor.pubkey",
- "buyer_actor pubkey is required before order submit",
+ "buyer_actor pubkey is required before trade submit",
));
}
if document.buyer_actor.source.trim().is_empty() {
issues.push(issue(
"buyer_actor.source",
- "buyer_actor source is required before order submit",
+ "buyer_actor source is required before trade submit",
));
} else if !matches!(
document.buyer_actor.source.as_str(),
@@ -8230,7 +7218,7 @@ fn collect_issues(document: &OrderDraftDocument) -> Vec<OrderIssueView> {
if document.order.buyer_pubkey.trim().is_empty() {
issues.push(issue(
"order.buyer_pubkey",
- "order buyer_pubkey is required before order submit",
+ "order buyer_pubkey is required before trade submit",
));
} else if !document
.order
@@ -8271,14 +7259,14 @@ fn actions_for_document(
) -> Vec<String> {
if app_order_issue_present(issues, APP_ORDER_ALREADY_SUBMITTED_ISSUE) {
return vec![format!(
- "radroots order status get {}",
+ "radroots trade status get {}",
document.order.order_id
)];
}
if app_order_issue_present(issues, APP_ORDER_SIGNED_EVIDENCE_CONFLICT_ISSUE) {
return vec![
- format!("radroots order status get {}", document.order.order_id),
- "radroots order app list".to_owned(),
+ format!("radroots trade status get {}", document.order.order_id),
+ "radroots trade app list".to_owned(),
];
}
@@ -8296,7 +7284,7 @@ fn actions_for_document(
.eq_ignore_ascii_case(document.buyer_actor.pubkey.as_str())
{
actions.push(format!(
- "radroots order rebind {} <selector>",
+ "radroots trade rebind {} <selector>",
document.order.order_id
));
}
@@ -8306,7 +7294,7 @@ fn actions_for_document(
{
actions.push("radroots account import <path>".to_owned());
actions.push(format!(
- "radroots order rebind {} <selector>",
+ "radroots trade rebind {} <selector>",
document.order.order_id
));
}
@@ -8318,11 +7306,11 @@ fn actions_for_document(
"radroots account attach-secret {} <path>",
document.buyer_actor.account_id
));
- actions.push(format!("radroots order get {}", document.order.order_id));
+ actions.push(format!("radroots trade get {}", document.order.order_id));
}
if issues.iter().any(|issue| issue.code == "account_mismatch") {
actions.push(format!(
- "radroots order rebind {} <selector>",
+ "radroots trade rebind {} <selector>",
document.order.order_id
));
}
@@ -8331,7 +7319,7 @@ fn actions_for_document(
.iter()
.any(|issue| issue.field.starts_with("order.items["))
{
- actions.push(format!("radroots order get {}", document.order.order_id));
+ actions.push(format!("radroots trade get {}", document.order.order_id));
}
let mut deduped = Vec::new();
for action in actions {
@@ -8496,7 +7484,7 @@ fn order_event_list_actor_context(
let seller_pubkey =
non_empty_string(loaded.document.order.seller_pubkey).ok_or_else(|| {
RuntimeError::Config(format!(
- "local order draft `{order_id}` is missing seller_pubkey"
+ "local trade draft `{order_id}` is missing seller_pubkey"
))
})?;
return Ok(Some(OrderEventListActorContext {
@@ -8556,16 +7544,16 @@ fn order_buyer_write_actor_context(
fn order_submit_listing_freshness_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
) -> Result<Option<OrderSubmitView>, RuntimeError> {
if !config.local.replica_db_path.exists() {
return Ok(Some(order_submit_unconfigured_view(
config,
loaded,
args,
- "order submit requires local market data to confirm the listing is still active; run `radroots store init` and `radroots market refresh` before submitting",
+ "trade submit requires local market data to confirm the listing is still active; run `radroots store init` and `radroots market refresh` before submitting",
vec![issue(
- "order.listing_addr",
+ "trade.listing_addr",
"local replica database is missing; run `radroots store init` and `radroots market refresh` before submitting",
)],
vec![
@@ -8577,7 +7565,7 @@ fn order_submit_listing_freshness_view(
let listing_addr = loaded.document.order.listing_addr.as_str();
let parsed = parse_listing_addr(listing_addr)
- .map_err(|error| RuntimeError::Config(format!("order listing_addr is invalid: {error}")))?;
+ .map_err(|error| RuntimeError::Config(format!("trade listing_addr is invalid: {error}")))?;
let active_event_id = match resolve_active_listing_event_id(config, listing_addr, &parsed)? {
Some(event_id) => event_id,
None => {
@@ -8585,9 +7573,9 @@ fn order_submit_listing_freshness_view(
config,
loaded,
args,
- "order listing is not active in the local replica; run `radroots market refresh` and create a new order from current market data",
+ "trade listing is not active in the local replica; run `radroots market refresh` and create a new trade from current market data",
vec![issue(
- "order.listing_addr",
+ "trade.listing_addr",
"listing is missing, archived, or superseded in the local replica",
)],
vec!["radroots market refresh".to_owned()],
@@ -8600,9 +7588,9 @@ fn order_submit_listing_freshness_view(
config,
loaded,
args,
- "order listing event is no longer current in the local replica; run `radroots market refresh` and create a new order from current market data",
+ "trade listing event is no longer current in the local replica; run `radroots market refresh` and create a new trade from current market data",
vec![issue(
- "order.listing_event_id",
+ "trade.listing_event_id",
format!(
"draft listing_event_id does not match latest local listing event `{active_event_id}`"
),
@@ -8617,16 +7605,16 @@ fn order_submit_listing_freshness_view(
fn order_submit_quantity_preflight_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
) -> Result<Option<OrderSubmitView>, RuntimeError> {
if !config.local.replica_db_path.exists() {
return Ok(Some(order_submit_unconfigured_view(
config,
loaded,
args,
- "order submit requires local market data to confirm current listing availability; run `radroots store init` and `radroots market refresh` before submitting",
+ "trade submit requires local market data to confirm current listing availability; run `radroots store init` and `radroots market refresh` before submitting",
vec![issue(
- "order.listing_addr",
+ "trade.listing_addr",
"local replica database is missing; run `radroots store init` and `radroots market refresh` before submitting",
)],
vec![
@@ -8673,9 +7661,9 @@ fn order_submit_quantity_preflight_view(
config,
loaded,
args,
- "order listing is not active in the local replica; run `radroots market refresh` and create a new order from current market data",
+ "trade listing is not active in the local replica; run `radroots market refresh` and create a new trade from current market data",
vec![issue(
- "order.listing_addr",
+ "trade.listing_addr",
"listing is missing, archived, or superseded in the local replica",
)],
vec!["radroots market refresh".to_owned()],
@@ -8695,7 +7683,7 @@ fn order_submit_quantity_preflight_view(
config,
loaded,
args,
- "order listing bin identity is missing in the local replica",
+ "trade listing bin identity is missing in the local replica",
vec![issue_with_code(
"listing_primary_bin_missing",
"inventory.primary_bin_id",
@@ -8712,7 +7700,7 @@ fn order_submit_quantity_preflight_view(
config,
loaded,
args,
- "order listing bin identity is not verified in the local replica",
+ "trade listing bin identity is not verified in the local replica",
vec![issue_with_code(
"listing_primary_bin_invalid",
"inventory.primary_bin_id",
@@ -8725,7 +7713,7 @@ fn order_submit_quantity_preflight_view(
config,
loaded,
args,
- "order listing bin identity is invalid in the local replica",
+ "trade listing bin identity is invalid in the local replica",
vec![issue_with_code(
"listing_primary_bin_invalid",
"inventory.primary_bin_id",
@@ -8754,7 +7742,7 @@ fn order_submit_quantity_preflight_view(
config,
loaded,
args,
- "order draft references a bin outside the current local listing",
+ "trade draft references a bin outside the current local listing",
bin_issues,
)));
}
@@ -8766,7 +7754,7 @@ fn order_submit_quantity_preflight_view(
config,
loaded,
args,
- "order listing availability is invalid in the local replica",
+ "trade listing availability is invalid in the local replica",
vec![issue_with_code(
"listing_inventory_availability_invalid",
"inventory.available",
@@ -8779,7 +7767,7 @@ fn order_submit_quantity_preflight_view(
config,
loaded,
args,
- "order listing availability is missing in the local replica",
+ "trade listing availability is missing in the local replica",
vec![issue_with_code(
"listing_inventory_availability_missing",
"inventory.available",
@@ -8811,13 +7799,13 @@ fn order_submit_quantity_preflight_view(
fn order_submit_unconfigured_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
reason: impl Into<String>,
issues: Vec<OrderIssueView>,
mut actions: Vec<String>,
) -> OrderSubmitView {
actions.push(format!(
- "radroots order get {}",
+ "radroots trade get {}",
loaded.document.order.order_id
));
@@ -8856,7 +7844,7 @@ fn order_submit_unconfigured_view(
fn order_submit_app_signed_evidence_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
issues: &[OrderIssueView],
) -> Option<OrderSubmitView> {
if let Some(issue) = app_order_issue(issues, APP_ORDER_ALREADY_SUBMITTED_ISSUE) {
@@ -8927,7 +7915,7 @@ fn order_submit_app_signed_evidence_view(
job: None,
issues: issues.to_vec(),
actions: vec![format!(
- "radroots order status get {}",
+ "radroots trade status get {}",
loaded.document.order.order_id
)],
});
@@ -8939,7 +7927,7 @@ fn order_submit_app_signed_evidence_view(
fn order_submit_invalid_quantity_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
reason: impl Into<String>,
issues: Vec<OrderIssueView>,
) -> OrderSubmitView {
@@ -8973,7 +7961,7 @@ fn order_submit_invalid_quantity_view(
issues,
actions: vec![
"radroots market refresh".to_owned(),
- format!("radroots order get {}", loaded.document.order.order_id),
+ format!("radroots trade get {}", loaded.document.order.order_id),
],
}
}
@@ -8981,7 +7969,7 @@ fn order_submit_invalid_quantity_view(
fn order_submit_listing_provenance_preflight_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
) -> Result<Option<OrderSubmitView>, RuntimeError> {
let listing_relays =
normalize_listing_relay_set(loaded.document.order.listing_relays.iter())
@@ -9004,13 +7992,13 @@ fn order_submit_listing_provenance_preflight_view(
.iter()
.map(|relay| {
format!(
- "radroots --relay {} order submit {}",
+ "radroots --relay {} trade submit {}",
relay, loaded.document.order.order_id
)
})
.collect::<Vec<_>>();
actions.push(format!(
- "radroots order get {}",
+ "radroots trade get {}",
loaded.document.order.order_id
));
Ok(Some(OrderSubmitView {
@@ -9039,13 +8027,13 @@ fn order_submit_listing_provenance_preflight_view(
idempotency_key: args.idempotency_key.clone(),
signer_mode: Some(config.signer.backend.as_str().to_owned()),
reason: Some(
- "order submit requires at least one configured relay that is known to carry the listing"
+ "trade submit requires at least one configured relay that is known to carry the listing"
.to_owned(),
),
job: None,
issues: vec![issue_with_code(
"listing_relay_target_mismatch",
- "order.listing_relays",
+ "trade.listing_relays",
format!(
"configured relays must include one of the listing provenance relays: {}",
loaded.document.order.listing_relays.join(", ")
@@ -9058,7 +8046,7 @@ fn order_submit_listing_provenance_preflight_view(
fn order_submit_market_freshness_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
) -> Result<Option<OrderSubmitView>, RuntimeError> {
if config.output.dry_run || config.relay.urls.is_empty() {
return Ok(None);
@@ -9077,11 +8065,11 @@ fn order_submit_market_freshness_view(
config,
loaded,
args,
- "order submit requires a current market refresh before signing; run `radroots market refresh` with the relays you trust, then submit again",
+ "trade submit requires a current market refresh before signing; run `radroots market refresh` with the relays you trust, then submit again",
vec![issue(
- "order.listing_addr",
+ "trade.listing_addr",
format!(
- "local market freshness is `{}`; current listing state must be refreshed before order submit",
+ "local market freshness is `{}`; current listing state must be refreshed before trade submit",
freshness.state
),
)],
@@ -9092,7 +8080,7 @@ fn order_submit_market_freshness_view(
fn order_submit_existing_request_view_from_receipt(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
payload: &RadrootsOrderRequest,
receipt: DirectRelayFetchReceipt,
) -> Result<Option<OrderSubmitView>, RuntimeError> {
@@ -9119,7 +8107,7 @@ fn order_submit_existing_request_view_from_receipt(
Err(error) => candidate_issues.push(issue_with_events(
"invalid_request_candidate",
"request_event_id",
- format!("request event `{event_id}` failed order submit preflight: {error}"),
+ format!("request event `{event_id}` failed trade submit preflight: {error}"),
vec![event_id],
)),
}
@@ -9165,12 +8153,12 @@ fn order_submit_existing_request_view_from_receipt(
config,
loaded,
args,
- "visible order request event conflicts with the local order draft; refusing to publish a second request for the same order id",
+ "visible order request event conflicts with the local trade draft; refusing to publish a second request for the same order id",
vec![issue_with_events(
"existing_request_conflict",
"request_event_id",
format!(
- "request event `{}` does not match the local order draft",
+ "request event `{}` does not match the local trade draft",
request.request_event_id
),
vec![request.request_event_id.clone()],
@@ -9254,7 +8242,7 @@ fn order_submit_request_matches_draft(
fn order_submit_deduplicated_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
request: &ResolvedOrderSubmitRequest,
target_relays: Vec<String>,
connected_relays: Vec<String>,
@@ -9298,7 +8286,7 @@ fn order_submit_deduplicated_view(
fn order_submit_dry_run_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
plan: OrderSubmitPlan,
target_relays: Vec<String>,
) -> OrderSubmitView {
@@ -9331,7 +8319,7 @@ fn order_submit_dry_run_view(
job: None,
issues: Vec::new(),
actions: vec![format!(
- "radroots order submit {}",
+ "radroots trade submit {}",
loaded.document.order.order_id
)],
}
@@ -9340,7 +8328,7 @@ fn order_submit_dry_run_view(
fn order_submit_invalid_existing_request_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
reason: impl Into<String>,
issues: Vec<OrderIssueView>,
target_relays: Vec<String>,
@@ -9375,7 +8363,7 @@ fn order_submit_invalid_existing_request_view(
job: None,
issues,
actions: vec![format!(
- "radroots order status get {}",
+ "radroots trade status get {}",
loaded.document.order.order_id
)],
}
@@ -9387,7 +8375,7 @@ fn canonical_order_request_payload_from_loaded(
) -> Result<RadrootsOrderRequest, RuntimeError> {
let economics =
loaded.document.order.economics.clone().ok_or_else(|| {
- RuntimeError::Config("order draft is missing quote economics".to_owned())
+ RuntimeError::Config("trade draft is missing quote economics".to_owned())
})?;
let items = loaded
.document
@@ -9499,7 +8487,7 @@ fn order_submit_relay_url_policy(target_relays: &[String]) -> SdkRelayUrlPolicy
fn prepare_order_submit_via_sdk(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
input: SdkOrderSubmitInput,
) -> Result<OrderSubmitView, CliSdkAdapterError> {
let target_relays = input.target_relays.clone();
@@ -9524,7 +8512,7 @@ fn prepare_order_submit_via_sdk(
fn submit_via_sdk(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
signing: account::AccountSigningIdentity,
input: SdkOrderSubmitInput,
) -> Result<OrderSubmitView, CliSdkAdapterError> {
@@ -9579,7 +8567,7 @@ fn enqueue_target_relays(
fn sdk_enqueued_order_submit_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
enqueue: OrderSubmitReceipt,
push: PushOutboxReceipt,
) -> OrderSubmitView {
@@ -9664,7 +8652,7 @@ fn sdk_order_submit_reason(
sdk_order_enqueue_retry_summary(enqueue)
)),
None => Some(format!(
- "{}; order submit queued in SDK outbox; no ready SDK outbox event was pushed; {}",
+ "{}; trade submit queued in SDK outbox; no ready SDK outbox event was pushed; {}",
sdk_order_enqueue_summary(enqueue),
sdk_order_enqueue_retry_summary(enqueue)
)),
@@ -9755,14 +8743,14 @@ fn sdk_relay_outcome_kind(kind: PushOutboxRelayOutcomeKind) -> &'static str {
fn order_binding_error_view(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
- args: &OrderSubmitArgs,
+ args: &TradeSubmitArgs,
error: ActorWriteBindingError,
) -> OrderSubmitView {
let (state, reason, actions) = order_actor_write_binding_error_parts(error);
let mut actions = actions;
actions.push(format!(
- "radroots order get {}",
+ "radroots trade get {}",
loaded.document.order.order_id
));
@@ -9821,8 +8809,8 @@ fn validate_bound_order_buyer_account(
json!({
"actions": [
"radroots account import <path>",
- format!("radroots order rebind {} <selector>", document.order.order_id),
- format!("radroots order get {}", document.order.order_id),
+ format!("radroots trade rebind {} <selector>", document.order.order_id),
+ format!("radroots trade get {}", document.order.order_id),
],
}),
),
@@ -9847,8 +8835,8 @@ fn validate_bound_order_buyer_account(
"attempted_buyer_account_id": account_id,
"attempted_buyer_pubkey": account_pubkey,
"actions": [
- format!("radroots order rebind {} <selector>", document.order.order_id),
- format!("radroots order get {}", document.order.order_id),
+ format!("radroots trade rebind {} <selector>", document.order.order_id),
+ format!("radroots trade get {}", document.order.order_id),
],
}),
),
@@ -9864,7 +8852,7 @@ fn validate_bound_order_buyer_account(
json!({
"actions": [
format!("radroots account attach-secret {account_id} <path>"),
- format!("radroots order get {}", document.order.order_id),
+ format!("radroots trade get {}", document.order.order_id),
],
}),
),
@@ -9882,7 +8870,7 @@ fn validate_bound_order_buyer_account(
"attempted_buyer_account_id": selector,
"actions": [
"radroots account list",
- format!("radroots order get {}", document.order.order_id),
+ format!("radroots trade get {}", document.order.order_id),
],
}),
),
@@ -9901,9 +8889,9 @@ fn validate_bound_order_buyer_account(
"attempted_buyer_account_id": attempted.record.account_id.to_string(),
"attempted_buyer_pubkey": attempted_pubkey,
"actions": [
- format!("radroots --account-id {account_id} order submit {}", document.order.order_id),
- format!("radroots order rebind {} <selector>", document.order.order_id),
- format!("radroots order get {}", document.order.order_id),
+ format!("radroots --account-id {account_id} trade submit {}", document.order.order_id),
+ format!("radroots trade rebind {} <selector>", document.order.order_id),
+ format!("radroots trade get {}", document.order.order_id),
],
}),
),
@@ -9924,7 +8912,7 @@ fn order_buyer_failure_detail(
"order_buyer_account_id": loaded.document.buyer_actor.account_id.as_str(),
"order_buyer_pubkey": loaded.document.buyer_actor.pubkey.as_str(),
"order_file": loaded.file.display().to_string(),
- "order_id": loaded.document.order.order_id.as_str(),
+ "trade_id": loaded.document.order.order_id.as_str(),
});
if let (Some(detail), Some(extra)) = (detail.as_object_mut(), extra.as_object_mut()) {
for (key, value) in std::mem::take(extra) {
@@ -9938,7 +8926,7 @@ fn resolve_local_order_signing_identity(
config: &RuntimeConfig,
loaded: &LoadedOrderDraft,
) -> Result<account::AccountSigningIdentity, ActorWriteBindingError> {
- resolve_local_order_bound_buyer_signing_identity(config, loaded, "order submit")
+ resolve_local_order_bound_buyer_signing_identity(config, loaded, "trade submit")
}
fn resolve_local_order_bound_buyer_signing_identity(
@@ -9973,8 +8961,8 @@ fn resolve_local_order_bound_buyer_signing_identity(
"attempted_buyer_account_id": signing.account.record.account_id.to_string(),
"attempted_buyer_pubkey": selected_pubkey,
"actions": [
- format!("radroots order rebind {} <selector>", loaded.document.order.order_id),
- format!("radroots order get {}", loaded.document.order.order_id),
+ format!("radroots trade rebind {} <selector>", loaded.document.order.order_id),
+ format!("radroots trade get {}", loaded.document.order.order_id),
],
}),
),
@@ -9987,7 +8975,7 @@ fn resolve_local_order_bound_buyer_signing_identity(
fn resolve_local_order_decision_signing_identity(
config: &RuntimeConfig,
seller_pubkey: &str,
- decision: OrderDecisionArg,
+ decision: TradeDecisionArg,
) -> Result<account::AccountSigningIdentity, ActorWriteBindingError> {
if !matches!(config.signer.backend, SignerBackend::Local) {
return Err(ActorWriteBindingError::Unconfigured(format!(
@@ -10019,7 +9007,7 @@ fn resolve_local_order_revision_signing_identity(
) -> Result<account::AccountSigningIdentity, ActorWriteBindingError> {
if !matches!(config.signer.backend, SignerBackend::Local) {
return Err(ActorWriteBindingError::Unconfigured(
- "order revision propose requires signer mode `local`".to_owned(),
+ "trade revision propose requires signer mode `local`".to_owned(),
));
}
let signing = account::resolve_local_signing_identity(config)
@@ -10046,7 +9034,7 @@ fn resolve_local_order_cancellation_signing_identity(
) -> Result<account::AccountSigningIdentity, ActorWriteBindingError> {
if !matches!(config.signer.backend, SignerBackend::Local) {
return Err(ActorWriteBindingError::Unconfigured(
- "order cancel requires signer mode `local`".to_owned(),
+ "trade cancel requires signer mode `local`".to_owned(),
));
}
let signing = account::resolve_local_signing_identity(config)
@@ -10070,11 +9058,11 @@ fn resolve_local_order_cancellation_signing_identity(
fn resolve_local_order_revision_decision_signing_identity(
config: &RuntimeConfig,
buyer_pubkey: &str,
- args: &OrderRevisionDecisionArgs,
+ args: &TradeRevisionDecisionArgs,
) -> Result<account::AccountSigningIdentity, ActorWriteBindingError> {
if !matches!(config.signer.backend, SignerBackend::Local) {
return Err(ActorWriteBindingError::Unconfigured(format!(
- "order revision {} requires signer mode `local`",
+ "trade revision {} requires signer mode `local`",
args.decision.command()
)));
}
@@ -10108,9 +9096,9 @@ fn relay_failures(failures: Vec<DirectRelayFailure>) -> Vec<RelayFailureView> {
fn load_draft(path: &Path) -> Result<LoadedOrderDraft, String> {
let contents = fs::read_to_string(path)
- .map_err(|error| format!("read order draft {}: {error}", path.display()))?;
+ .map_err(|error| format!("read trade draft {}: {error}", path.display()))?;
let document = toml::from_str::<OrderDraftDocument>(contents.as_str())
- .map_err(|error| format!("parse order draft {}: {error}", path.display()))?;
+ .map_err(|error| format!("parse trade draft {}: {error}", path.display()))?;
Ok(LoadedOrderDraft {
file: path.to_path_buf(),
updated_at_unix: modified_unix(path).unwrap_or_default(),
@@ -10128,9 +9116,9 @@ fn save_draft(path: &Path, draft: &OrderDraftDocument) -> Result<(), RuntimeErro
fn scaffold_contents(draft: &OrderDraftDocument) -> Result<String, RuntimeError> {
let toml = toml::to_string_pretty(draft)
- .map_err(|error| RuntimeError::Config(format!("render order draft: {error}")))?;
+ .map_err(|error| RuntimeError::Config(format!("render trade draft: {error}")))?;
Ok(format!(
- "# radroots order draft v1\n# fill listing_addr and any missing order items before submit\n\n{toml}"
+ "# radroots trade draft v1\n# fill listing_addr and any missing order items before submit\n\n{toml}"
))
}
diff --git a/src/runtime/order/sdk_status.rs b/src/runtime/order/sdk_status.rs
@@ -113,9 +113,9 @@ fn sdk_order_status_state(status: OrderStatusKind) -> &'static str {
fn sdk_order_status_reason(status: OrderStatusKind, order_id: &str) -> Option<String> {
match status {
- OrderStatusKind::Missing => Some(format!("no local SDK order events matched `{order_id}`")),
+ OrderStatusKind::Missing => Some(format!("no local SDK trade events matched `{order_id}`")),
OrderStatusKind::Invalid => Some(format!(
- "local SDK order events for `{order_id}` failed reducer validation"
+ "local SDK trade events for `{order_id}` failed reducer validation"
)),
_ => None,
}
@@ -167,7 +167,7 @@ fn sdk_order_status_issue_view(issue: &SdkOrderStatusIssue) -> OrderIssueView {
OrderIssueView {
code: code.clone(),
field: "sdk_order_status".to_owned(),
- message: format!("SDK order status reported `{code}`"),
+ message: format!("SDK trade status reported `{code}`"),
event_ids: issue
.event_ids
.iter()
diff --git a/src/runtime/paths.rs b/src/runtime/paths.rs
@@ -35,6 +35,7 @@ pub struct PathsConfig {
pub app_config_path: PathBuf,
pub workspace_config_path: Option<PathBuf>,
pub app_data_root: PathBuf,
+ pub shared_cache_root: PathBuf,
pub app_logs_root: PathBuf,
pub shared_accounts_data_root: PathBuf,
pub shared_accounts_secrets_root: PathBuf,
@@ -93,6 +94,7 @@ pub(crate) fn resolve_paths(
app_config_path: app_paths.config.join(DEFAULT_CONFIG_FILE_NAME),
workspace_config_path,
app_data_root: app_paths.data,
+ shared_cache_root: resolved.cache,
app_logs_root: app_paths.logs,
shared_accounts_data_root: shared_accounts_paths.data,
shared_accounts_secrets_root: shared_accounts_paths.secrets,
diff --git a/src/runtime/provider.rs b/src/runtime/provider.rs
@@ -300,6 +300,7 @@ mod tests {
app_config_path: PathBuf::from("/tmp/config/apps/cli/config.toml"),
workspace_config_path: None,
app_data_root: PathBuf::from("/tmp/data"),
+ shared_cache_root: PathBuf::from("/tmp/cache"),
app_logs_root: PathBuf::from("/tmp/logs"),
shared_accounts_data_root: PathBuf::from("/tmp/shared/accounts"),
shared_accounts_secrets_root: PathBuf::from("/tmp/shared/accounts-secrets"),
diff --git a/src/runtime/sdk.rs b/src/runtime/sdk.rs
@@ -52,6 +52,7 @@ pub enum CliSdkAdapterError {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CliSdkConfig {
pub storage_root: PathBuf,
+ pub geonames_cache_root: PathBuf,
pub relay_url_policy: SdkRelayUrlPolicy,
pub relay_urls: Vec<String>,
pub publish_transport: SdkPublishTransport,
@@ -61,6 +62,7 @@ impl CliSdkConfig {
pub fn from_runtime_config(config: &RuntimeConfig) -> Result<Self, RuntimeError> {
Ok(Self {
storage_root: sdk_storage_root(config),
+ geonames_cache_root: config.paths.shared_cache_root.clone(),
relay_url_policy: sdk_relay_url_policy(config),
relay_urls: config.relay.urls.clone(),
publish_transport: sdk_publish_transport(config)?,
@@ -73,6 +75,7 @@ impl CliSdkConfig {
.storage(RadrootsSdkStorageConfig::Directory(
self.storage_root.clone(),
))
+ .geonames_cache_root(self.geonames_cache_root.clone())
.relay_url_policy(self.relay_url_policy)
.publish_transport(self.publish_transport.clone()),
|builder, relay_url| builder.relay_url(relay_url.clone()),
@@ -590,6 +593,7 @@ pub(crate) fn sdk_runtime() -> Result<Runtime, RuntimeError> {
fn memory_builder(config: &CliSdkConfig) -> RadrootsClientBuilder {
config.relay_urls.iter().fold(
RadrootsClient::builder()
+ .geonames_cache_root(config.geonames_cache_root.clone())
.relay_url_policy(config.relay_url_policy)
.publish_transport(config.publish_transport.clone()),
|builder, relay_url| builder.relay_url(relay_url.clone()),
@@ -797,7 +801,7 @@ mod tests {
section: "dependencies",
name: "radroots_replica_db",
owner: "legacy-replica-and-market-projection",
- reason: "legacy derived replica status, export, market reads, sync pull, basket lookup, and order draft preflight",
+ reason: "legacy derived replica status, export, market reads, sync pull, basket lookup, and trade draft preflight",
lifecycle: "transitional until those derived projection surfaces migrate",
},
DirectRrRsDependency {
@@ -869,12 +873,12 @@ mod tests {
LegacyDirectRelayConsumer {
path: "src/runtime/order.rs",
required_tokens: &[
- "legacy_order_preflight_relay_status",
+ "sdk_order_status_from_relay_receipt",
"fetch_events_from_relays",
],
- owner: "order.status.relay-read",
- reason: "bounded order status and preflight reads still inspect configured relays outside SDK local storage",
- lifecycle: "retain until order relay reads migrate to SDK-backed query APIs",
+ owner: "trade.relay-evidence-acquisition",
+ reason: "bounded trade commands acquire configured-relay evidence before SDK local ingest and projection",
+ lifecycle: "retain until trade relay acquisition moves behind SDK query APIs",
},
LegacyDirectRelayConsumer {
path: "src/runtime/sync.rs",
@@ -933,7 +937,7 @@ mod tests {
label: "order status",
path: "src/runtime/order.rs",
start: "pub fn status(\n config: &RuntimeConfig",
- end: "fn legacy_order_preflight_relay_status(",
+ end: "fn sdk_order_status_from_relay_receipt(",
required_tokens: &["OrderStatusRequest::parse", "session.sdk().trades().status"],
},
MigratedCliPathGuard {
@@ -949,7 +953,7 @@ mod tests {
],
},
MigratedCliPathGuard {
- label: "order submit",
+ label: "trade submit",
path: "src/runtime/order.rs",
start: "fn prepare_order_submit_via_sdk(",
end: "fn enqueue_target_relays(",
@@ -1167,7 +1171,7 @@ mod tests {
("radroots store", "CLI command string"),
("radroots sync", "CLI command string"),
("radroots listing", "CLI command string"),
- ("radroots order", "CLI command string"),
+ ("radroots trade", "CLI command string"),
];
for file in files {
@@ -1340,6 +1344,7 @@ mod tests {
fn sample_config(root: &Path, relays: Vec<String>) -> RuntimeConfig {
let data = root.join("data");
+ let cache = root.join("cache");
let logs = root.join("logs");
let secrets = root.join("secrets");
RuntimeConfig {
@@ -1371,6 +1376,7 @@ mod tests {
app_config_path: root.join("config/apps/cli/config.toml"),
workspace_config_path: None,
app_data_root: data.join("apps/cli"),
+ shared_cache_root: cache.clone(),
app_logs_root: logs.join("apps/cli"),
shared_accounts_data_root: data.join("shared/accounts"),
shared_accounts_secrets_root: secrets.join("shared/accounts"),
diff --git a/src/runtime/signer.rs b/src/runtime/signer.rs
@@ -320,31 +320,31 @@ fn cli_write_kinds() -> [CliWriteKind; 12] {
event_kind: KIND_LISTING,
},
CliWriteKind {
- command: "order.submit",
+ command: "trade.submit",
event_kind: KIND_ORDER_REQUEST,
},
CliWriteKind {
- command: "order.accept",
+ command: "trade.accept",
event_kind: KIND_ORDER_DECISION,
},
CliWriteKind {
- command: "order.decline",
+ command: "trade.decline",
event_kind: KIND_ORDER_DECISION,
},
CliWriteKind {
- command: "order.cancel",
+ command: "trade.cancel",
event_kind: KIND_ORDER_CANCELLATION,
},
CliWriteKind {
- command: "order.revision.propose",
+ command: "trade.revision.propose",
event_kind: KIND_ORDER_REVISION_PROPOSAL,
},
CliWriteKind {
- command: "order.revision.accept",
+ command: "trade.revision.accept",
event_kind: KIND_ORDER_REVISION_DECISION,
},
CliWriteKind {
- command: "order.revision.decline",
+ command: "trade.revision.decline",
event_kind: KIND_ORDER_REVISION_DECISION,
},
]
@@ -623,13 +623,13 @@ mod tests {
"listing.publish",
"listing.update",
"listing.archive",
- "order.submit",
- "order.accept",
- "order.decline",
- "order.cancel",
- "order.revision.propose",
- "order.revision.accept",
- "order.revision.decline",
+ "trade.submit",
+ "trade.accept",
+ "trade.decline",
+ "trade.cancel",
+ "trade.revision.propose",
+ "trade.revision.accept",
+ "trade.revision.decline",
]
);
assert!(!commands.contains(&"signer.status.get"));
@@ -639,7 +639,7 @@ mod tests {
fn order_submit_readiness_uses_active_order_request_kind() {
let write_kind = cli_write_kinds()
.into_iter()
- .find(|kind| kind.command == "order.submit")
+ .find(|kind| kind.command == "trade.submit")
.expect("order submit readiness");
assert_eq!(write_kind.event_kind, KIND_ORDER_REQUEST);
@@ -648,7 +648,7 @@ mod tests {
#[test]
fn order_decision_readiness_uses_active_order_decision_kind() {
- for command in ["order.accept", "order.decline"] {
+ for command in ["trade.accept", "trade.decline"] {
let write_kind = cli_write_kinds()
.into_iter()
.find(|kind| kind.command == command)
@@ -663,13 +663,13 @@ mod tests {
fn order_revision_readiness_uses_active_revision_kinds() {
let proposal = cli_write_kinds()
.into_iter()
- .find(|kind| kind.command == "order.revision.propose")
+ .find(|kind| kind.command == "trade.revision.propose")
.expect("order revision propose readiness");
assert_eq!(proposal.event_kind, KIND_ORDER_REVISION_PROPOSAL);
assert_ne!(proposal.event_kind, RESERVED_ORDER_KIND_3431);
- for command in ["order.revision.accept", "order.revision.decline"] {
+ for command in ["trade.revision.accept", "trade.revision.decline"] {
let write_kind = cli_write_kinds()
.into_iter()
.find(|kind| kind.command == command)
@@ -684,7 +684,7 @@ mod tests {
fn order_cancel_readiness_uses_order_cancellation_kind() {
let cancel = cli_write_kinds()
.into_iter()
- .find(|kind| kind.command == "order.cancel")
+ .find(|kind| kind.command == "trade.cancel")
.expect("order cancel readiness");
assert_eq!(cancel.event_kind, KIND_ORDER_CANCELLATION);
assert_ne!(cancel.event_kind, RESERVED_ORDER_KIND_3431);
@@ -709,7 +709,7 @@ mod tests {
for (command, event_kind) in [
("farm.publish", KIND_FARM),
("listing.publish", KIND_LISTING),
- ("order.submit", KIND_ORDER_REQUEST),
+ ("trade.submit", KIND_ORDER_REQUEST),
] {
let entry = readiness
.iter()
diff --git a/src/runtime/sync.rs b/src/runtime/sync.rs
@@ -2216,6 +2216,7 @@ mod tests {
fn sample_config(root: &Path, relays: Vec<String>) -> RuntimeConfig {
let data = root.join("data");
+ let cache = root.join("cache");
let logs = root.join("logs");
let secrets = root.join("secrets");
RuntimeConfig {
@@ -2247,6 +2248,7 @@ mod tests {
app_config_path: root.join("config/apps/cli/config.toml"),
workspace_config_path: None,
app_data_root: data.join("apps/cli"),
+ shared_cache_root: cache.clone(),
app_logs_root: logs.join("apps/cli"),
shared_accounts_data_root: data.join("shared/accounts"),
shared_accounts_secrets_root: secrets.join("shared/accounts"),
diff --git a/src/view/runtime.rs b/src/view/runtime.rs
@@ -248,6 +248,7 @@ pub struct PathsRuntimeView {
pub workspace_config_enabled: bool,
pub workspace_config_path: Option<String>,
pub app_data_root: String,
+ pub shared_cache_root: String,
pub app_logs_root: String,
pub shared_accounts_data_root: String,
pub shared_accounts_secrets_root: String,
@@ -811,6 +812,60 @@ impl FarmSetView {
}
#[derive(Debug, Clone, Serialize)]
+pub struct FarmPrivateLocationView {
+ pub state: String,
+ pub source: String,
+ pub farm_addr: Option<String>,
+ pub farm_d_tag: Option<String>,
+ pub seller_account_id: Option<String>,
+ pub seller_pubkey: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub exact_location: Option<FarmPrivateExactLocationView>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub public_locality: Option<FarmPrivatePublicLocalityView>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub geonames_feature_id: Option<i64>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub geonames_country_id: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub geonames_database_path: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub cleared: Option<bool>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub reason: Option<String>,
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
+ pub actions: Vec<String>,
+}
+
+#[derive(Debug, Clone, Serialize)]
+pub struct FarmPrivateExactLocationView {
+ pub lat: f64,
+ pub lng: f64,
+}
+
+#[derive(Debug, Clone, Serialize)]
+pub struct FarmPrivatePublicLocalityView {
+ pub primary: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub city: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub region: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub country: Option<String>,
+ pub geohash5: String,
+}
+
+impl FarmPrivateLocationView {
+ pub fn disposition(&self) -> CommandDisposition {
+ match self.state.as_str() {
+ "unconfigured" => CommandDisposition::Unconfigured,
+ "missing" => CommandDisposition::NotFound,
+ _ => CommandDisposition::Success,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize)]
pub struct FarmRebindView {
pub state: String,
pub source: String,
@@ -1114,7 +1169,7 @@ pub struct FindHyfView {
pub struct MarketReadinessView {
pub protocol_valid: bool,
pub marketplace_eligible: bool,
- pub order_request_enabled: bool,
+ pub checkout_enabled: bool,
pub primary_bin_verified: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub reason_codes: Vec<String>,
@@ -1125,7 +1180,7 @@ impl MarketReadinessView {
Self {
protocol_valid: false,
marketplace_eligible: false,
- order_request_enabled: false,
+ checkout_enabled: false,
primary_bin_verified: false,
reason_codes: vec![reason_code.into()],
}
@@ -1169,7 +1224,7 @@ impl MarketReadinessView {
&& !price_currency.trim().is_empty()
&& price_per_amount.is_finite()
&& price_per_amount > 0.0;
- let order_request_enabled =
+ let checkout_enabled =
marketplace_eligible && inventory_available && primary_bin_verified && price_available;
let mut reason_codes = Vec::new();
if !protocol_valid {
@@ -1178,8 +1233,8 @@ impl MarketReadinessView {
if protocol_valid && !marketplace_eligible {
reason_codes.push("listing_marketplace_ineligible".to_owned());
}
- if marketplace_eligible && !order_request_enabled {
- reason_codes.push("listing_order_request_disabled".to_owned());
+ if marketplace_eligible && !checkout_enabled {
+ reason_codes.push("listing_checkout_disabled".to_owned());
if !inventory_available {
reason_codes.push("listing_inventory_unavailable".to_owned());
}
@@ -1195,7 +1250,7 @@ impl MarketReadinessView {
Self {
protocol_valid,
marketplace_eligible,
- order_request_enabled,
+ checkout_enabled,
primary_bin_verified,
reason_codes,
}
@@ -1223,7 +1278,7 @@ mod market_readiness_tests {
);
assert!(enabled.protocol_valid);
assert!(enabled.marketplace_eligible);
- assert!(enabled.order_request_enabled);
+ assert!(enabled.checkout_enabled);
assert!(enabled.primary_bin_verified);
assert!(enabled.reason_codes.is_empty());
@@ -1240,7 +1295,7 @@ mod market_readiness_tests {
);
assert!(!invalid.protocol_valid);
assert!(!invalid.marketplace_eligible);
- assert!(!invalid.order_request_enabled);
+ assert!(!invalid.checkout_enabled);
assert!(!invalid.primary_bin_verified);
assert_eq!(invalid.reason_codes, vec!["listing_protocol_invalid"]);
@@ -1257,7 +1312,7 @@ mod market_readiness_tests {
);
assert!(ineligible.protocol_valid);
assert!(!ineligible.marketplace_eligible);
- assert!(!ineligible.order_request_enabled);
+ assert!(!ineligible.checkout_enabled);
assert!(ineligible.primary_bin_verified);
assert_eq!(
ineligible.reason_codes,
@@ -1277,14 +1332,11 @@ mod market_readiness_tests {
);
assert!(order_request_disabled.protocol_valid);
assert!(order_request_disabled.marketplace_eligible);
- assert!(!order_request_disabled.order_request_enabled);
+ assert!(!order_request_disabled.checkout_enabled);
assert!(order_request_disabled.primary_bin_verified);
assert_eq!(
order_request_disabled.reason_codes,
- vec![
- "listing_order_request_disabled",
- "listing_inventory_unavailable"
- ]
+ vec!["listing_checkout_disabled", "listing_inventory_unavailable"]
);
let primary_bin_missing = MarketReadinessView::from_market_projection(
@@ -1300,14 +1352,11 @@ mod market_readiness_tests {
);
assert!(primary_bin_missing.protocol_valid);
assert!(primary_bin_missing.marketplace_eligible);
- assert!(!primary_bin_missing.order_request_enabled);
+ assert!(!primary_bin_missing.checkout_enabled);
assert!(!primary_bin_missing.primary_bin_verified);
assert_eq!(
primary_bin_missing.reason_codes,
- vec![
- "listing_order_request_disabled",
- "listing_primary_bin_missing"
- ]
+ vec!["listing_checkout_disabled", "listing_primary_bin_missing"]
);
let primary_bin_blank = MarketReadinessView::from_market_projection(
@@ -1323,10 +1372,7 @@ mod market_readiness_tests {
);
assert_eq!(
primary_bin_blank.reason_codes,
- vec![
- "listing_order_request_disabled",
- "listing_primary_bin_missing"
- ]
+ vec!["listing_checkout_disabled", "listing_primary_bin_missing"]
);
let primary_bin_invalid = MarketReadinessView::from_market_projection(
@@ -1342,14 +1388,11 @@ mod market_readiness_tests {
);
assert!(primary_bin_invalid.protocol_valid);
assert!(primary_bin_invalid.marketplace_eligible);
- assert!(!primary_bin_invalid.order_request_enabled);
+ assert!(!primary_bin_invalid.checkout_enabled);
assert!(!primary_bin_invalid.primary_bin_verified);
assert_eq!(
primary_bin_invalid.reason_codes,
- vec![
- "listing_order_request_disabled",
- "listing_primary_bin_invalid"
- ]
+ vec!["listing_checkout_disabled", "listing_primary_bin_invalid"]
);
}
}
@@ -1449,6 +1492,7 @@ pub struct JobWatchFrameView {
pub struct OrderNewView {
pub state: String,
pub source: String,
+ #[serde(rename = "trade_id")]
pub order_id: String,
pub file: String,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -1497,6 +1541,7 @@ pub struct OrderGetView {
pub source: String,
pub lookup: String,
#[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(rename = "trade_id")]
pub order_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
@@ -1554,6 +1599,7 @@ pub struct OrderListView {
pub source: String,
pub count: usize,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
+ #[serde(rename = "trades")]
pub orders: Vec<OrderSummaryView>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub actions: Vec<String>,
@@ -1614,6 +1660,7 @@ pub struct OrderAppRecordSummaryView {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub listing_relays: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(rename = "trade_id")]
pub order_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub buyer_account_id: Option<String>,
@@ -1638,6 +1685,7 @@ pub struct OrderAppRecordExportView {
pub file: String,
pub valid: bool,
#[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(rename = "trade_id")]
pub order_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub listing_addr: Option<String>,
@@ -1678,6 +1726,7 @@ impl OrderAppRecordExportView {
pub struct OrderSubmitView {
pub state: String,
pub source: String,
+ #[serde(rename = "trade_id")]
pub order_id: String,
pub file: String,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -1750,8 +1799,11 @@ pub struct OrderRebindView {
pub lookup: String,
pub file: String,
pub dry_run: bool,
+ #[serde(rename = "from_trade_id")]
pub from_order_id: String,
+ #[serde(rename = "to_trade_id")]
pub to_order_id: String,
+ #[serde(rename = "trade_id_changed")]
pub order_id_changed: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub from_buyer_account_id: Option<String>,
@@ -1787,6 +1839,7 @@ impl OrderRebindView {
pub struct OrderDecisionView {
pub state: String,
pub source: String,
+ #[serde(rename = "trade_id")]
pub order_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub listing_addr: Option<String>,
@@ -1854,6 +1907,7 @@ impl OrderDecisionView {
pub struct OrderCancellationView {
pub state: String,
pub source: String,
+ #[serde(rename = "trade_id")]
pub order_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub listing_addr: Option<String>,
@@ -1922,6 +1976,7 @@ impl OrderCancellationView {
pub struct OrderRevisionProposalView {
pub state: String,
pub source: String,
+ #[serde(rename = "trade_id")]
pub order_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub revision_id: Option<String>,
@@ -1995,6 +2050,7 @@ impl OrderRevisionProposalView {
pub struct OrderRevisionDecisionView {
pub state: String,
pub source: String,
+ #[serde(rename = "trade_id")]
pub order_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub revision_id: Option<String>,
@@ -2071,6 +2127,7 @@ impl OrderRevisionDecisionView {
pub struct OrderStatusView {
pub state: String,
pub source: String,
+ #[serde(rename = "trade_id")]
pub order_id: String,
pub actor_context_source: String,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -2233,6 +2290,7 @@ impl OrderStatusView {
pub struct OrderWorkflowView {
pub state: String,
pub source: String,
+ #[serde(rename = "trade_id")]
pub order_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub listing_addr: Option<String>,
@@ -2269,6 +2327,7 @@ pub struct OrderEventListView {
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
+ #[serde(rename = "trades")]
pub orders: Vec<OrderEventListEntryView>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub actions: Vec<String>,
diff --git a/tests/signer_runtime_modes.rs b/tests/signer_runtime_modes.rs
@@ -2134,7 +2134,7 @@ fn local_seller_publish_commands_attempt_configured_relay() {
"create",
"direct_order",
]);
- let order_id = quote["result"]["quote"]["order_id"]
+ let order_id = quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id");
let (order_output, order_value) = sandbox.json_output(&[
@@ -2144,24 +2144,24 @@ fn local_seller_publish_commands_attempt_configured_relay() {
relay,
"--approval-token",
"approve",
- "order",
+ "trade",
"submit",
order_id,
]);
assert!(!order_output.status.success());
- assert_eq!(order_value["operation_id"], "order.submit");
+ assert_eq!(order_value["operation_id"], "trade.submit");
assert_eq!(order_value["result"], serde_json::Value::Null);
assert_eq!(order_value["errors"][0]["code"], "operation_unavailable");
assert_eq!(
order_value["errors"][0]["detail"]["issues"][0]["field"],
- "order.listing_addr"
+ "trade.listing_addr"
);
assert_contains(
&order_value["errors"][0]["detail"]["issues"][0]["message"],
"local market freshness",
);
- assert_no_removed_command_reference(&order_value, &["order", "submit"]);
- assert_no_daemon_runtime_reference(&order_value, &["order", "submit"]);
+ assert_no_removed_command_reference(&order_value, &["trade", "submit"]);
+ assert_no_daemon_runtime_reference(&order_value, &["trade", "submit"]);
}
#[test]
@@ -2171,11 +2171,11 @@ fn local_order_event_list_attempts_configured_direct_relay() {
let relay = "ws://127.0.0.1:9";
let (output, value) = sandbox.json_output(&[
- "--format", "json", "--relay", relay, "order", "event", "list",
+ "--format", "json", "--relay", relay, "trade", "event", "list",
]);
assert!(!output.status.success());
- assert_direct_relay_connection_failure(&value, "order.event.list", &["order", "event", "list"]);
+ assert_direct_relay_connection_failure(&value, "trade.event.list", &["trade", "event", "list"]);
assert_eq!(value["errors"][0]["detail"]["state"], "unavailable");
assert_eq!(value["errors"][0]["detail"]["target_relays"][0], relay);
assert_eq!(
@@ -2204,17 +2204,17 @@ fn local_order_event_list_attempts_configured_direct_relay() {
#[test]
fn local_order_failure_envelopes_are_structured_and_actionable() {
let sandbox = RadrootsCliSandbox::new();
- let watch_args = ["--format", "json", "order", "event", "watch", "ord_missing"];
+ let watch_args = ["--format", "json", "trade", "event", "watch", "ord_missing"];
let (watch_output, watch) = sandbox.json_output(&watch_args);
assert!(!watch_output.status.success());
- assert_eq!(watch["operation_id"], "order.event.watch");
+ assert_eq!(watch["operation_id"], "trade.event.watch");
assert_eq!(watch["result"], Value::Null);
assert_eq!(watch["errors"][0]["code"], "not_implemented");
assert_eq!(watch["errors"][0]["detail"]["state"], "not_implemented");
- assert_eq!(watch["errors"][0]["detail"]["order_id"], "ord_missing");
+ assert_eq!(watch["errors"][0]["detail"]["trade_id"], "ord_missing");
assert_eq!(
watch["next_actions"][0]["command"],
- "radroots order status get ord_missing"
+ "radroots trade status get ord_missing"
);
assert_no_daemon_runtime_reference(&watch, &watch_args);
@@ -2224,7 +2224,7 @@ fn local_order_failure_envelopes_are_structured_and_actionable() {
"--publish-transport",
"direct_nostr_relay",
"--dry-run",
- "order",
+ "trade",
"submit",
"ord_missing",
];
@@ -2232,33 +2232,33 @@ fn local_order_failure_envelopes_are_structured_and_actionable() {
assert!(!submit_output.status.success());
assert_eq!(submit["errors"][0]["code"], "not_found");
assert_eq!(submit["errors"][0]["detail"]["state"], "missing");
- assert_eq!(submit["errors"][0]["detail"]["order_id"], "ord_missing");
- assert_eq!(submit["next_actions"][0]["command"], "radroots order list");
+ assert_eq!(submit["errors"][0]["detail"]["trade_id"], "ord_missing");
+ assert_eq!(submit["next_actions"][0]["command"], "radroots trade list");
assert_eq!(
submit["next_actions"][1]["command"],
"radroots basket create"
);
assert_no_daemon_runtime_reference(&submit, &submit_args);
- let status_args = ["--format", "json", "order", "status", "get", "ord_missing"];
+ let status_args = ["--format", "json", "trade", "status", "get", "ord_missing"];
let status = sandbox.json_success(&status_args);
- assert_eq!(status["operation_id"], "order.status.get");
+ assert_eq!(status["operation_id"], "trade.status.get");
assert_eq!(status["result"]["state"], "missing");
- assert_eq!(status["result"]["source"], "SDK local order projection");
+ assert_eq!(status["result"]["source"], "SDK local trade projection");
assert_eq!(
status["result"]["actor_context_source"],
"sdk_local_projection"
);
- assert_eq!(status["result"]["order_id"], "ord_missing");
+ assert_eq!(status["result"]["trade_id"], "ord_missing");
assert_eq!(status["result"]["fetched_count"], 0);
assert_eq!(status["result"]["decoded_count"], 0);
assert_eq!(
status["result"]["reason"],
- "no local SDK order events matched `ord_missing`"
+ "no local SDK trade events matched `ord_missing`"
);
assert_no_daemon_runtime_reference(&status, &status_args);
- let event_list_no_relay_args = ["--format", "json", "order", "event", "list"];
+ let event_list_no_relay_args = ["--format", "json", "trade", "event", "list"];
let (event_list_no_relay_output, event_list_no_relay) =
sandbox.json_output(&event_list_no_relay_args);
assert!(!event_list_no_relay_output.status.success());
@@ -2272,7 +2272,7 @@ fn local_order_failure_envelopes_are_structured_and_actionable() {
);
assert_eq!(
event_list_no_relay["next_actions"][0]["command"],
- "radroots --relay wss://relay.example.com order event list"
+ "radroots --relay wss://relay.example.com trade event list"
);
assert_no_daemon_runtime_reference(&event_list_no_relay, &event_list_no_relay_args);
@@ -2281,7 +2281,7 @@ fn local_order_failure_envelopes_are_structured_and_actionable() {
"json",
"--relay",
"ws://127.0.0.1:9",
- "order",
+ "trade",
"event",
"list",
];
@@ -2308,7 +2308,7 @@ fn local_order_failure_envelopes_are_structured_and_actionable() {
"--publish-transport",
"direct_nostr_relay",
"--dry-run",
- "order",
+ "trade",
"accept",
"ord_missing",
];
@@ -2316,7 +2316,7 @@ fn local_order_failure_envelopes_are_structured_and_actionable() {
assert!(!accept_output.status.success());
assert_eq!(accept["errors"][0]["code"], "operation_unavailable");
assert_eq!(accept["errors"][0]["detail"]["state"], "unconfigured");
- assert_eq!(accept["errors"][0]["detail"]["order_id"], "ord_missing");
+ assert_eq!(accept["errors"][0]["detail"]["trade_id"], "ord_missing");
assert_eq!(accept["errors"][0]["detail"]["decision"], "accepted");
assert_no_daemon_runtime_reference(&accept, &accept_args);
@@ -2326,7 +2326,7 @@ fn local_order_failure_envelopes_are_structured_and_actionable() {
"--publish-transport",
"direct_nostr_relay",
"--dry-run",
- "order",
+ "trade",
"decline",
"ord_missing",
"--reason",
@@ -2336,7 +2336,7 @@ fn local_order_failure_envelopes_are_structured_and_actionable() {
assert!(!decline_output.status.success());
assert_eq!(decline["errors"][0]["code"], "operation_unavailable");
assert_eq!(decline["errors"][0]["detail"]["state"], "unconfigured");
- assert_eq!(decline["errors"][0]["detail"]["order_id"], "ord_missing");
+ assert_eq!(decline["errors"][0]["detail"]["trade_id"], "ord_missing");
assert_eq!(decline["errors"][0]["detail"]["decision"], "declined");
assert_no_daemon_runtime_reference(&decline, &decline_args);
}
diff --git a/tests/support/mod.rs b/tests/support/mod.rs
@@ -193,11 +193,11 @@ pub fn assert_no_removed_command_reference(value: &Value, args: &[&str]) {
"radroots market search",
"radroots market view",
"radroots market update",
- "radroots order ls",
- "radroots order history",
- "radroots order watch",
- "radroots order new",
- "radroots order create",
+ "radroots trade ls",
+ "radroots trade history",
+ "radroots trade watch",
+ "radroots trade new",
+ "radroots trade create",
"radroots farm init",
"radroots farm check",
"radroots relay ls",
diff --git a/tests/target_cli.rs b/tests/target_cli.rs
@@ -1262,7 +1262,7 @@ fn root_help_exposes_only_target_namespaces() {
"listing",
"market",
"basket",
- "order",
+ "trade",
] {
assert!(
help_lists(&stdout, namespace),
@@ -2352,9 +2352,9 @@ fn listing_update_publish_attempts_direct_relay_with_approval() {
#[test]
fn removed_order_submit_watch_flag_is_rejected_publicly() {
let output = radroots()
- .args(["order", "submit", "--watch"])
+ .args(["trade", "submit", "--watch"])
.output()
- .expect("run removed order submit watch flag");
+ .expect("run removed trade submit watch flag");
assert!(!output.status.success());
let stderr = String::from_utf8(output.stderr).expect("utf8 stderr");
@@ -2382,24 +2382,24 @@ fn removed_command_families_are_rejected_publicly() {
fn seller_order_decision_and_status_commands_are_public() {
for (operation_id, args) in [
(
- "order.accept",
+ "trade.accept",
[
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"accept",
"ord_public",
]
.as_slice(),
),
(
- "order.decline",
+ "trade.decline",
[
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"decline",
"ord_public",
"--reason",
@@ -2408,12 +2408,12 @@ fn seller_order_decision_and_status_commands_are_public() {
.as_slice(),
),
(
- "order.cancel",
+ "trade.cancel",
[
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"cancel",
"ord_public",
"--reason",
@@ -2422,8 +2422,8 @@ fn seller_order_decision_and_status_commands_are_public() {
.as_slice(),
),
(
- "order.status.get",
- ["--format", "json", "order", "status", "get", "ord_public"].as_slice(),
+ "trade.status.get",
+ ["--format", "json", "trade", "status", "get", "ord_public"].as_slice(),
),
] {
let output = radroots()
@@ -2440,7 +2440,7 @@ fn seller_order_decision_and_status_commands_are_public() {
}
let output = radroots()
- .args(["order", "decision", "accept", "ord_deferred"])
+ .args(["trade", "decision", "accept", "ord_deferred"])
.output()
.expect("run removed nested decision command");
@@ -2452,11 +2452,11 @@ fn seller_order_decision_and_status_commands_are_public() {
#[test]
fn removed_order_post_agreement_subcommands_are_rejected_publicly() {
for args in [
- ["order", "fulfillment", "update", "ord_public"].as_slice(),
- ["order", "receipt", "record", "ord_public"].as_slice(),
- ["order", "payment", "record", "ord_public"].as_slice(),
- ["order", "settlement", "accept", "ord_public"].as_slice(),
- ["order", "settlement", "reject", "ord_public"].as_slice(),
+ ["trade", "fulfillment", "update", "ord_public"].as_slice(),
+ ["trade", "receipt", "record", "ord_public"].as_slice(),
+ ["trade", "payment", "record", "ord_public"].as_slice(),
+ ["trade", "settlement", "accept", "ord_public"].as_slice(),
+ ["trade", "settlement", "reject", "ord_public"].as_slice(),
] {
let output = radroots()
.args(args)
@@ -2481,7 +2481,7 @@ fn target_outputs_do_not_suggest_removed_command_families() {
[
"--format",
"json",
- "order",
+ "trade",
"get",
"ord_AAAAAAAAAAAAAAAAAAAAAA",
]
@@ -2840,15 +2840,15 @@ fn human_market_refresh_missing_store_shows_action() {
#[test]
fn human_failure_output_preserves_error_code_and_message() {
let output = radroots()
- .args(["--format", "human", "order", "submit"])
+ .args(["--format", "human", "trade", "submit"])
.output()
- .expect("run order submit");
+ .expect("run trade submit");
assert_eq!(output.status.code(), Some(6));
let stdout = String::from_utf8(output.stdout).expect("utf8 stdout");
- assert!(stdout.starts_with("order.submit: error\n"));
- assert!(stdout.contains("request_id: req_order_submit_"));
+ assert!(stdout.starts_with("trade.submit: error\n"));
+ assert!(stdout.contains("request_id: req_trade_submit_"));
assert!(stdout.contains("error: approval_required"));
assert!(stdout.contains("message: missing required `approval_token` input"));
assert!(serde_json::from_str::<Value>(&stdout).is_err());
@@ -2860,7 +2860,7 @@ fn human_failure_output_renders_structured_error_detail() {
.args([
"--format",
"human",
- "order",
+ "trade",
"event",
"watch",
"ord_missing",
@@ -2871,12 +2871,12 @@ fn human_failure_output_renders_structured_error_detail() {
assert_eq!(output.status.code(), Some(3));
let stdout = String::from_utf8(output.stdout).expect("utf8 stdout");
- assert!(stdout.starts_with("order.event.watch: error\n"));
- assert!(stdout.contains("request_id: req_order_event_watch_"));
+ assert!(stdout.starts_with("trade.event.watch: error\n"));
+ assert!(stdout.contains("request_id: req_trade_event_watch_"));
assert!(stdout.contains("error: not_implemented"));
assert!(stdout.contains("state: not_implemented"));
- assert!(stdout.contains("reason: relay-backed order event watch is not implemented"));
- assert!(stdout.contains("- radroots order status get ord_missing"));
+ assert!(stdout.contains("reason: relay-backed trade event watch is not implemented"));
+ assert!(stdout.contains("- radroots trade status get ord_missing"));
assert!(serde_json::from_str::<Value>(&stdout).is_err());
}
@@ -2973,7 +2973,7 @@ fn unsupported_ndjson_returns_structured_invalid_input() {
.args([
"--format",
"ndjson",
- "order",
+ "trade",
"event",
"watch",
"ord_missing",
@@ -2984,9 +2984,9 @@ fn unsupported_ndjson_returns_structured_invalid_input() {
assert_eq!(watch_output.status.code(), Some(2));
let watch_frames = ndjson_from_stdout(&watch_output);
assert_eq!(watch_frames.len(), 2);
- assert_eq!(watch_frames[0]["operation_id"], "order.event.watch");
+ assert_eq!(watch_frames[0]["operation_id"], "trade.event.watch");
assert_eq!(watch_frames[0]["frame_type"], "started");
- assert_eq!(watch_frames[1]["operation_id"], "order.event.watch");
+ assert_eq!(watch_frames[1]["operation_id"], "trade.event.watch");
assert_eq!(watch_frames[1]["frame_type"], "error");
assert_eq!(watch_frames[1]["errors"][0]["code"], "invalid_input");
assert_eq!(watch_frames[1]["errors"][0]["exit_code"], 2);
@@ -3048,16 +3048,16 @@ fn offline_forbids_external_network_operations() {
["--format", "json", "--offline", "market", "refresh"].as_slice(),
),
(
- "order.submit",
- ["--format", "json", "--offline", "order", "submit"].as_slice(),
+ "trade.submit",
+ ["--format", "json", "--offline", "trade", "submit"].as_slice(),
),
(
- "order.cancel",
+ "trade.cancel",
[
"--format",
"json",
"--offline",
- "order",
+ "trade",
"cancel",
"ord_offline_cancel",
"--reason",
@@ -3066,14 +3066,14 @@ fn offline_forbids_external_network_operations() {
.as_slice(),
),
(
- "order.revision.propose",
+ "trade.revision.propose",
[
"--format",
"json",
"--offline",
"--approval-token",
"approve",
- "order",
+ "trade",
"revision",
"propose",
"ord_offline_revision",
@@ -3087,14 +3087,14 @@ fn offline_forbids_external_network_operations() {
.as_slice(),
),
(
- "order.revision.accept",
+ "trade.revision.accept",
[
"--format",
"json",
"--offline",
"--approval-token",
"approve",
- "order",
+ "trade",
"revision",
"accept",
"ord_offline_revision",
@@ -3104,14 +3104,14 @@ fn offline_forbids_external_network_operations() {
.as_slice(),
),
(
- "order.revision.decline",
+ "trade.revision.decline",
[
"--format",
"json",
"--offline",
"--approval-token",
"approve",
- "order",
+ "trade",
"revision",
"decline",
"ord_offline_revision",
@@ -3328,26 +3328,26 @@ fn listing_publish_idempotency_conflict_maps_sdk_partial_mutation_recovery() {
fn offline_rejects_order_decision_dry_run() {
for (operation_id, args) in [
(
- "order.accept",
+ "trade.accept",
[
"--format",
"json",
"--offline",
"--dry-run",
- "order",
+ "trade",
"accept",
"ord_offline_decision",
]
.as_slice(),
),
(
- "order.decline",
+ "trade.decline",
[
"--format",
"json",
"--offline",
"--dry-run",
- "order",
+ "trade",
"decline",
"ord_offline_decision",
"--reason",
@@ -3356,13 +3356,13 @@ fn offline_rejects_order_decision_dry_run() {
.as_slice(),
),
(
- "order.cancel",
+ "trade.cancel",
[
"--format",
"json",
"--offline",
"--dry-run",
- "order",
+ "trade",
"cancel",
"ord_offline_decision",
"--reason",
@@ -3371,13 +3371,13 @@ fn offline_rejects_order_decision_dry_run() {
.as_slice(),
),
(
- "order.revision.propose",
+ "trade.revision.propose",
[
"--format",
"json",
"--offline",
"--dry-run",
- "order",
+ "trade",
"revision",
"propose",
"ord_offline_revision",
@@ -3391,13 +3391,13 @@ fn offline_rejects_order_decision_dry_run() {
.as_slice(),
),
(
- "order.revision.accept",
+ "trade.revision.accept",
[
"--format",
"json",
"--offline",
"--dry-run",
- "order",
+ "trade",
"revision",
"accept",
"ord_offline_revision",
@@ -3407,13 +3407,13 @@ fn offline_rejects_order_decision_dry_run() {
.as_slice(),
),
(
- "order.revision.decline",
+ "trade.revision.decline",
[
"--format",
"json",
"--offline",
"--dry-run",
- "order",
+ "trade",
"revision",
"decline",
"ord_offline_revision",
@@ -3502,16 +3502,16 @@ fn online_requires_relay_for_external_network_operations() {
["--format", "json", "--online", "market", "refresh"].as_slice(),
),
(
- "order.event.list",
- ["--format", "json", "--online", "order", "event", "list"].as_slice(),
+ "trade.event.list",
+ ["--format", "json", "--online", "trade", "event", "list"].as_slice(),
),
(
- "order.cancel",
+ "trade.cancel",
[
"--format",
"json",
"--online",
- "order",
+ "trade",
"cancel",
"ord_missing",
"--reason",
@@ -3520,14 +3520,14 @@ fn online_requires_relay_for_external_network_operations() {
.as_slice(),
),
(
- "order.revision.propose",
+ "trade.revision.propose",
[
"--format",
"json",
"--online",
"--approval-token",
"approve",
- "order",
+ "trade",
"revision",
"propose",
"ord_missing",
@@ -3541,13 +3541,13 @@ fn online_requires_relay_for_external_network_operations() {
.as_slice(),
),
(
- "order.revision.accept",
+ "trade.revision.accept",
[
"--format",
"json",
"--online",
"--dry-run",
- "order",
+ "trade",
"revision",
"accept",
"ord_missing",
@@ -3557,13 +3557,13 @@ fn online_requires_relay_for_external_network_operations() {
.as_slice(),
),
(
- "order.revision.decline",
+ "trade.revision.decline",
[
"--format",
"json",
"--online",
"--dry-run",
- "order",
+ "trade",
"revision",
"decline",
"ord_missing",
@@ -3603,15 +3603,15 @@ fn order_status_get_uses_sdk_local_projection_without_relay_fetch() {
"--format",
"json",
"--online",
- "order",
+ "trade",
"status",
"get",
"ord_missing",
]);
- assert_eq!(local["operation_id"], "order.status.get");
+ assert_eq!(local["operation_id"], "trade.status.get");
assert_eq!(local["result"]["state"], "missing");
- assert_eq!(local["result"]["source"], "SDK local order projection");
+ assert_eq!(local["result"]["source"], "SDK local trade projection");
assert_eq!(
local["result"]["actor_context_source"],
"sdk_local_projection"
@@ -3627,17 +3627,17 @@ fn order_status_get_uses_sdk_local_projection_without_relay_fetch() {
"json",
"--relay",
closed_relay.as_str(),
- "order",
+ "trade",
"status",
"get",
"ord_missing",
]);
- assert_eq!(with_closed_relay["operation_id"], "order.status.get");
+ assert_eq!(with_closed_relay["operation_id"], "trade.status.get");
assert_eq!(with_closed_relay["result"]["state"], "missing");
assert_eq!(
with_closed_relay["result"]["source"],
- "SDK local order projection"
+ "SDK local trade projection"
);
assert_eq!(with_closed_relay["result"]["fetched_count"], 0);
assert_eq!(with_closed_relay["result"]["decoded_count"], 0);
@@ -3647,10 +3647,10 @@ fn order_status_get_uses_sdk_local_projection_without_relay_fetch() {
fn order_status_get_invalid_order_id_uses_sdk_error_contract() {
let sandbox = RadrootsCliSandbox::new();
let (output, value) =
- sandbox.json_output(&["--format", "json", "order", "status", "get", "bad order id"]);
+ sandbox.json_output(&["--format", "json", "trade", "status", "get", "bad order id"]);
assert!(!output.status.success());
- assert_eq!(value["operation_id"], "order.status.get");
+ assert_eq!(value["operation_id"], "trade.status.get");
assert_eq!(value["result"], Value::Null);
assert_eq!(value["errors"][0]["code"], "invalid_order_id");
assert_eq!(value["errors"][0]["exit_code"], 2);
@@ -3684,7 +3684,7 @@ fn online_order_event_watch_returns_deferred_without_relay_preflight() {
"--format",
"json",
"--online",
- "order",
+ "trade",
"event",
"watch",
"ord_missing",
@@ -3692,14 +3692,14 @@ fn online_order_event_watch_returns_deferred_without_relay_preflight() {
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(3));
- assert_eq!(value["operation_id"], "order.event.watch");
+ assert_eq!(value["operation_id"], "trade.event.watch");
assert_eq!(value["result"], Value::Null);
assert_eq!(value["errors"][0]["code"], "not_implemented");
assert_eq!(value["errors"][0]["detail"]["state"], "not_implemented");
- assert_eq!(value["errors"][0]["detail"]["order_id"], "ord_missing");
+ assert_eq!(value["errors"][0]["detail"]["trade_id"], "ord_missing");
assert_eq!(
value["next_actions"][0]["command"],
- "radroots order status get ord_missing"
+ "radroots trade status get ord_missing"
);
assert!(
!value["errors"][0]["message"]
@@ -3707,7 +3707,7 @@ fn online_order_event_watch_returns_deferred_without_relay_preflight() {
.expect("message")
.contains("configured relay")
);
- assert_no_daemon_runtime_reference(&value, &["order", "event", "watch"]);
+ assert_no_daemon_runtime_reference(&value, &["trade", "event", "watch"]);
}
#[test]
@@ -4984,43 +4984,43 @@ fn order_app_records_list_export_get_and_submit_supported_app_order() {
listing_event_id.as_str(),
);
- let app_list = sandbox.json_success(&["--format", "json", "order", "app", "list"]);
- assert_eq!(app_list["operation_id"], "order.app.list");
+ let app_list = sandbox.json_success(&["--format", "json", "trade", "app", "list"]);
+ assert_eq!(app_list["operation_id"], "trade.app.list");
assert_eq!(app_list["result"]["state"], "ready");
assert_eq!(app_list["result"]["count"], 1);
assert_eq!(app_list["result"]["records"][0]["record_id"], record_id);
- assert_eq!(app_list["result"]["records"][0]["order_id"], order_id);
+ assert_eq!(app_list["result"]["records"][0]["trade_id"], order_id);
assert_eq!(app_list["result"]["records"][0]["ready_for_submit"], true);
assert_eq!(app_list["result"]["records"][0]["exportable"], true);
- assert_no_removed_command_reference(&app_list, &["order", "app", "list"]);
- assert_no_daemon_runtime_reference(&app_list, &["order", "app", "list"]);
+ assert_no_removed_command_reference(&app_list, &["trade", "app", "list"]);
+ assert_no_daemon_runtime_reference(&app_list, &["trade", "app", "list"]);
- let orders = sandbox.json_success(&["--format", "json", "order", "list"]);
- assert_eq!(orders["operation_id"], "order.list");
+ let orders = sandbox.json_success(&["--format", "json", "trade", "list"]);
+ assert_eq!(orders["operation_id"], "trade.list");
assert_eq!(orders["result"]["state"], "ready");
assert_eq!(orders["result"]["count"], 1);
- assert_eq!(orders["result"]["orders"][0]["id"], order_id);
- assert_eq!(orders["result"]["orders"][0]["ready_for_submit"], true);
+ assert_eq!(orders["result"]["trades"][0]["id"], order_id);
+ assert_eq!(orders["result"]["trades"][0]["ready_for_submit"], true);
assert_eq!(
- orders["result"]["orders"][0]["listing_event_id"],
+ orders["result"]["trades"][0]["listing_event_id"],
listing_event_id
);
assert_eq!(
- orders["result"]["orders"][0]["buyer_account_id"],
+ orders["result"]["trades"][0]["buyer_account_id"],
account_id
);
assert_eq!(
- orders["result"]["orders"][0]["file"],
+ orders["result"]["trades"][0]["file"],
format!("shared-local-events/{record_id}")
);
- assert_no_removed_command_reference(&orders, &["order", "list"]);
- assert_no_daemon_runtime_reference(&orders, &["order", "list"]);
+ assert_no_removed_command_reference(&orders, &["trade", "list"]);
+ assert_no_daemon_runtime_reference(&orders, &["trade", "list"]);
let get_by_record =
- sandbox.json_success(&["--format", "json", "order", "get", record_id.as_str()]);
- assert_eq!(get_by_record["operation_id"], "order.get");
+ sandbox.json_success(&["--format", "json", "trade", "get", record_id.as_str()]);
+ assert_eq!(get_by_record["operation_id"], "trade.get");
assert_eq!(get_by_record["result"]["state"], "ready");
- assert_eq!(get_by_record["result"]["order_id"], order_id);
+ assert_eq!(get_by_record["result"]["trade_id"], order_id);
assert_eq!(get_by_record["result"]["ready_for_submit"], true);
let export_path = sandbox.root().join("app-order.toml");
@@ -5029,14 +5029,14 @@ fn order_app_records_list_export_get_and_submit_supported_app_order() {
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"app",
"export",
record_id.as_str(),
"--output",
export_path_arg.as_ref(),
]);
- assert_eq!(dry_run["operation_id"], "order.app.export");
+ assert_eq!(dry_run["operation_id"], "trade.app.export");
assert_eq!(dry_run["result"]["state"], "dry_run");
assert_eq!(dry_run["result"]["valid"], true);
assert!(!export_path.exists());
@@ -5044,18 +5044,18 @@ fn order_app_records_list_export_get_and_submit_supported_app_order() {
let export = sandbox.json_success(&[
"--format",
"json",
- "order",
+ "trade",
"app",
"export",
record_id.as_str(),
"--output",
export_path_arg.as_ref(),
]);
- assert_eq!(export["operation_id"], "order.app.export");
+ assert_eq!(export["operation_id"], "trade.app.export");
assert_eq!(export["result"]["state"], "exported");
- assert_eq!(export["result"]["order_id"], order_id);
+ assert_eq!(export["result"]["trade_id"], order_id);
assert!(export_path.exists());
- let exported_contents = fs::read_to_string(&export_path).expect("exported order draft");
+ let exported_contents = fs::read_to_string(&export_path).expect("exported trade draft");
assert!(exported_contents.contains("kind = \"order_draft_v1\""));
assert!(exported_contents.contains(format!("order_id = \"{order_id}\"").as_str()));
assert!(exported_contents.contains("source = \"resolved_account\""));
@@ -5064,13 +5064,13 @@ fn order_app_records_list_export_get_and_submit_supported_app_order() {
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"submit",
record_id.as_str(),
]);
- assert_eq!(submit["operation_id"], "order.submit");
+ assert_eq!(submit["operation_id"], "trade.submit");
assert_eq!(submit["result"]["state"], "dry_run");
- assert_eq!(submit["result"]["source"], "SDK order submit · local key");
+ assert_eq!(submit["result"]["source"], "SDK trade submit · local key");
assert_eq!(submit["result"]["event_kind"], 3422);
assert_eq!(
submit["result"]["target_relays"][0],
@@ -5148,7 +5148,7 @@ fn order_app_records_treat_matching_signed_evidence_as_submitted() {
&signed_event,
);
- let app_list = sandbox.json_success(&["--format", "json", "order", "app", "list"]);
+ let app_list = sandbox.json_success(&["--format", "json", "trade", "app", "list"]);
let listed = &app_list["result"]["records"][0];
assert_eq!(listed["record_id"], record_id);
assert_eq!(listed["status"], "submitted");
@@ -5156,16 +5156,16 @@ fn order_app_records_treat_matching_signed_evidence_as_submitted() {
assert_eq!(listed["exportable"], false);
assert_eq!(
listed["actions"].as_array().expect("actions"),
- &vec![json!(format!("radroots order status get {order_id}"))]
+ &vec![json!(format!("radroots trade status get {order_id}"))]
);
- let orders = sandbox.json_success(&["--format", "json", "order", "list"]);
+ let orders = sandbox.json_success(&["--format", "json", "trade", "list"]);
assert_eq!(orders["result"]["state"], "ready");
- assert_eq!(orders["result"]["orders"][0]["state"], "submitted");
- assert_eq!(orders["result"]["orders"][0]["ready_for_submit"], false);
+ assert_eq!(orders["result"]["trades"][0]["state"], "submitted");
+ assert_eq!(orders["result"]["trades"][0]["ready_for_submit"], false);
let get_by_record =
- sandbox.json_success(&["--format", "json", "order", "get", record_id.as_str()]);
+ sandbox.json_success(&["--format", "json", "trade", "get", record_id.as_str()]);
assert_eq!(get_by_record["result"]["state"], "submitted");
assert_eq!(get_by_record["result"]["ready_for_submit"], false);
assert_eq!(
@@ -5180,7 +5180,7 @@ fn order_app_records_treat_matching_signed_evidence_as_submitted() {
get_by_record["result"]["actions"]
.as_array()
.expect("actions"),
- &vec![json!(format!("radroots order status get {order_id}"))]
+ &vec![json!(format!("radroots trade status get {order_id}"))]
);
let export_path = sandbox.root().join("submitted-app-order.toml");
@@ -5188,7 +5188,7 @@ fn order_app_records_treat_matching_signed_evidence_as_submitted() {
let (export_output, export) = sandbox.json_output(&[
"--format",
"json",
- "order",
+ "trade",
"app",
"export",
record_id.as_str(),
@@ -5196,7 +5196,7 @@ fn order_app_records_treat_matching_signed_evidence_as_submitted() {
export_path_arg.as_ref(),
]);
assert!(!export_output.status.success());
- assert_eq!(export["operation_id"], "order.app.export");
+ assert_eq!(export["operation_id"], "trade.app.export");
assert_eq!(export["errors"][0]["detail"]["state"], "already_submitted");
assert_eq!(export["errors"][0]["detail"]["valid"], false);
assert!(!export_path.exists());
@@ -5205,11 +5205,11 @@ fn order_app_records_treat_matching_signed_evidence_as_submitted() {
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"submit",
record_id.as_str(),
]);
- assert_eq!(submit["operation_id"], "order.submit");
+ assert_eq!(submit["operation_id"], "trade.submit");
assert_eq!(submit["result"]["state"], "submitted");
assert_eq!(submit["result"]["deduplicated"], true);
assert_eq!(submit["result"]["event_id"], signed_event_id);
@@ -5282,7 +5282,7 @@ fn order_app_records_fail_closed_when_signed_evidence_conflicts() {
&signed_event,
);
- let app_list = sandbox.json_success(&["--format", "json", "order", "app", "list"]);
+ let app_list = sandbox.json_success(&["--format", "json", "trade", "app", "list"]);
let listed = &app_list["result"]["records"][0];
assert_eq!(listed["record_id"], record_id);
assert_eq!(listed["status"], "conflict");
@@ -5310,7 +5310,7 @@ fn order_app_records_fail_closed_when_signed_evidence_conflicts() {
let (export_output, export) = sandbox.json_output(&[
"--format",
"json",
- "order",
+ "trade",
"app",
"export",
record_id.as_str(),
@@ -5318,7 +5318,7 @@ fn order_app_records_fail_closed_when_signed_evidence_conflicts() {
export_path_arg.as_ref(),
]);
assert!(!export_output.status.success());
- assert_eq!(export["operation_id"], "order.app.export");
+ assert_eq!(export["operation_id"], "trade.app.export");
assert_eq!(export["errors"][0]["detail"]["state"], "conflict");
assert_eq!(
export["errors"][0]["detail"]["issues"][0]["code"],
@@ -5330,12 +5330,12 @@ fn order_app_records_fail_closed_when_signed_evidence_conflicts() {
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"submit",
record_id.as_str(),
]);
assert!(!submit_output.status.success());
- assert_eq!(submit["operation_id"], "order.submit");
+ assert_eq!(submit["operation_id"], "trade.submit");
assert_eq!(submit["errors"][0]["detail"]["state"], "invalid");
assert_eq!(
submit["errors"][0]["detail"]["issues"][0]["code"],
@@ -5371,7 +5371,7 @@ fn order_app_records_fail_closed_when_not_current_or_supported() {
Vec::new(),
);
- let app_list = sandbox.json_success(&["--format", "json", "order", "app", "list"]);
+ let app_list = sandbox.json_success(&["--format", "json", "trade", "app", "list"]);
assert_eq!(
app_list["result"]["records"][0]["record_id"],
stale_record_id
@@ -5390,7 +5390,7 @@ fn order_app_records_fail_closed_when_not_current_or_supported() {
let (output, stale_export) = sandbox.json_output(&[
"--format",
"json",
- "order",
+ "trade",
"app",
"export",
stale_record_id.as_str(),
@@ -5398,7 +5398,7 @@ fn order_app_records_fail_closed_when_not_current_or_supported() {
export_path_arg.as_ref(),
]);
assert!(!output.status.success());
- assert_eq!(stale_export["operation_id"], "order.app.export");
+ assert_eq!(stale_export["operation_id"], "trade.app.export");
assert_eq!(stale_export["result"], Value::Null);
assert_eq!(stale_export["errors"][0]["detail"]["state"], "stale");
assert_eq!(stale_export["errors"][0]["detail"]["valid"], false);
@@ -5408,13 +5408,13 @@ fn order_app_records_fail_closed_when_not_current_or_supported() {
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"submit",
stale_record_id.as_str(),
]);
assert!(!submit_output.status.success());
assert_eq!(submit_output.status.code(), Some(3));
- assert_eq!(submit["operation_id"], "order.submit");
+ assert_eq!(submit["operation_id"], "trade.submit");
assert_eq!(submit["errors"][0]["code"], "operation_unavailable");
assert_eq!(
submit["errors"][0]["detail"]["issues"][0]["code"],
@@ -5450,7 +5450,7 @@ fn order_app_records_fail_closed_when_unsupported() {
vec!["seller_pubkey_required"],
);
- let app_list = sandbox.json_success(&["--format", "json", "order", "app", "list"]);
+ let app_list = sandbox.json_success(&["--format", "json", "trade", "app", "list"]);
assert_eq!(app_list["result"]["records"][0]["record_id"], record_id);
assert_eq!(app_list["result"]["records"][0]["ready_for_submit"], false);
assert_eq!(app_list["result"]["records"][0]["exportable"], false);
@@ -5466,7 +5466,7 @@ fn order_app_records_fail_closed_when_unsupported() {
let (export_output, export) = sandbox.json_output(&[
"--format",
"json",
- "order",
+ "trade",
"app",
"export",
record_id.as_str(),
@@ -5474,7 +5474,7 @@ fn order_app_records_fail_closed_when_unsupported() {
export_path_arg.as_ref(),
]);
assert!(!export_output.status.success());
- assert_eq!(export["operation_id"], "order.app.export");
+ assert_eq!(export["operation_id"], "trade.app.export");
assert_eq!(export["errors"][0]["detail"]["state"], "unsupported");
assert_eq!(
export["errors"][0]["detail"]["issues"][0]["code"],
@@ -5483,7 +5483,7 @@ fn order_app_records_fail_closed_when_unsupported() {
assert!(!export_path.exists());
let (submit_output, submit) =
- sandbox.json_output(&["--format", "json", "--dry-run", "order", "submit", order_id]);
+ sandbox.json_output(&["--format", "json", "--dry-run", "trade", "submit", order_id]);
assert!(!submit_output.status.success());
assert_eq!(
submit["errors"][0]["detail"]["issues"][0]["code"],
@@ -5519,7 +5519,7 @@ fn order_app_records_fail_closed_when_supported_record_is_malformed() {
vec!["unit_price_required"],
);
- let app_list = sandbox.json_success(&["--format", "json", "order", "app", "list"]);
+ let app_list = sandbox.json_success(&["--format", "json", "trade", "app", "list"]);
assert_eq!(app_list["result"]["records"][0]["record_id"], record_id);
assert_eq!(app_list["result"]["records"][0]["ready_for_submit"], false);
assert_eq!(app_list["result"]["records"][0]["exportable"], false);
@@ -5535,7 +5535,7 @@ fn order_app_records_fail_closed_when_supported_record_is_malformed() {
let (export_output, export) = sandbox.json_output(&[
"--format",
"json",
- "order",
+ "trade",
"app",
"export",
record_id.as_str(),
@@ -5543,7 +5543,7 @@ fn order_app_records_fail_closed_when_supported_record_is_malformed() {
export_path_arg.as_ref(),
]);
assert!(!export_output.status.success());
- assert_eq!(export["operation_id"], "order.app.export");
+ assert_eq!(export["operation_id"], "trade.app.export");
assert_eq!(export["errors"][0]["detail"]["state"], "invalid");
assert_eq!(
export["errors"][0]["detail"]["issues"][0]["code"],
@@ -5552,9 +5552,9 @@ fn order_app_records_fail_closed_when_supported_record_is_malformed() {
assert!(!export_path.exists());
let (submit_output, submit) =
- sandbox.json_output(&["--format", "json", "--dry-run", "order", "submit", order_id]);
+ sandbox.json_output(&["--format", "json", "--dry-run", "trade", "submit", order_id]);
assert!(!submit_output.status.success());
- assert_eq!(submit["operation_id"], "order.submit");
+ assert_eq!(submit["operation_id"], "trade.submit");
assert_eq!(
submit["errors"][0]["detail"]["issues"][0]["code"],
"invalid_app_order_record"
@@ -5600,7 +5600,7 @@ fn order_app_records_fail_closed_when_order_id_conflicts() {
Vec::new(),
);
- let app_list = sandbox.json_success(&["--format", "json", "order", "app", "list"]);
+ let app_list = sandbox.json_success(&["--format", "json", "trade", "app", "list"]);
assert_eq!(app_list["result"]["count"], 1);
assert_eq!(
app_list["result"]["records"][0]["record_id"],
@@ -5620,7 +5620,7 @@ fn order_app_records_fail_closed_when_order_id_conflicts() {
let (export_output, export) = sandbox.json_output(&[
"--format",
"json",
- "order",
+ "trade",
"app",
"export",
conflicting_record_id.as_str(),
@@ -5628,7 +5628,7 @@ fn order_app_records_fail_closed_when_order_id_conflicts() {
export_path_arg.as_ref(),
]);
assert!(!export_output.status.success());
- assert_eq!(export["operation_id"], "order.app.export");
+ assert_eq!(export["operation_id"], "trade.app.export");
assert_eq!(export["errors"][0]["detail"]["state"], "conflict");
assert_eq!(
export["errors"][0]["detail"]["issues"][0]["code"],
@@ -5637,7 +5637,7 @@ fn order_app_records_fail_closed_when_order_id_conflicts() {
assert!(!export_path.exists());
let (submit_output, submit) =
- sandbox.json_output(&["--format", "json", "--dry-run", "order", "submit", order_id]);
+ sandbox.json_output(&["--format", "json", "--dry-run", "trade", "submit", order_id]);
assert!(!submit_output.status.success());
assert_eq!(
submit["errors"][0]["detail"]["issues"][0]["code"],
@@ -6099,7 +6099,7 @@ fn market_order_request_readiness_gates_buyer_intent_actions() {
let result = &search["result"]["results"][0];
assert_eq!(result["protocol_valid"], true);
assert_eq!(result["marketplace_eligible"], true);
- assert_eq!(result["order_request_enabled"], true);
+ assert_eq!(result["checkout_enabled"], true);
assert_eq!(result["primary_bin_verified"], true);
assert!(result.get("reason_codes").is_none());
assert!(
@@ -6121,7 +6121,7 @@ fn market_order_request_readiness_gates_buyer_intent_actions() {
assert_eq!(listing["operation_id"], "market.listing.get");
assert_eq!(listing["result"]["protocol_valid"], true);
assert_eq!(listing["result"]["marketplace_eligible"], true);
- assert_eq!(listing["result"]["order_request_enabled"], true);
+ assert_eq!(listing["result"]["checkout_enabled"], true);
assert_eq!(listing["result"]["primary_bin_verified"], true);
assert!(
listing["result"]["actions"]
@@ -6138,11 +6138,11 @@ fn market_order_request_readiness_gates_buyer_intent_actions() {
let disabled_result = &disabled_search["result"]["results"][0];
assert_eq!(disabled_result["protocol_valid"], true);
assert_eq!(disabled_result["marketplace_eligible"], true);
- assert_eq!(disabled_result["order_request_enabled"], false);
+ assert_eq!(disabled_result["checkout_enabled"], false);
assert_eq!(disabled_result["primary_bin_verified"], true);
assert_eq!(
disabled_result["reason_codes"][0],
- "listing_order_request_disabled"
+ "listing_checkout_disabled"
);
assert_eq!(
disabled_result["reason_codes"][1],
@@ -6166,11 +6166,11 @@ fn market_order_request_readiness_gates_buyer_intent_actions() {
]);
assert_eq!(disabled_listing["result"]["protocol_valid"], true);
assert_eq!(disabled_listing["result"]["marketplace_eligible"], true);
- assert_eq!(disabled_listing["result"]["order_request_enabled"], false);
+ assert_eq!(disabled_listing["result"]["checkout_enabled"], false);
assert_eq!(disabled_listing["result"]["primary_bin_verified"], true);
assert_eq!(
disabled_listing["result"]["reason_codes"][0],
- "listing_order_request_disabled"
+ "listing_checkout_disabled"
);
assert!(
disabled_listing["result"]
@@ -6186,11 +6186,11 @@ fn market_order_request_readiness_gates_buyer_intent_actions() {
sandbox.json_success(&["--format", "json", "market", "product", "search", "eggs"]);
let no_bin_result = &no_bin_search["result"]["results"][0];
assert_eq!(no_bin_result["primary_bin_id"], Value::Null);
- assert_eq!(no_bin_result["order_request_enabled"], false);
+ assert_eq!(no_bin_result["checkout_enabled"], false);
assert_eq!(no_bin_result["primary_bin_verified"], false);
assert_eq!(
no_bin_result["reason_codes"][0],
- "listing_order_request_disabled"
+ "listing_checkout_disabled"
);
assert_eq!(
no_bin_result["reason_codes"][1],
@@ -6213,7 +6213,7 @@ fn market_order_request_readiness_gates_buyer_intent_actions() {
"pasture-eggs",
]);
assert_eq!(no_bin_listing["result"]["primary_bin_id"], Value::Null);
- assert_eq!(no_bin_listing["result"]["order_request_enabled"], false);
+ assert_eq!(no_bin_listing["result"]["checkout_enabled"], false);
assert_eq!(no_bin_listing["result"]["primary_bin_verified"], false);
assert_eq!(
no_bin_listing["result"]["reason_codes"][1],
@@ -6232,7 +6232,7 @@ fn market_order_request_readiness_gates_buyer_intent_actions() {
sandbox.json_success(&["--format", "json", "market", "product", "search", "eggs"]);
let invalid_bin_result = &invalid_bin_search["result"]["results"][0];
assert_eq!(invalid_bin_result["primary_bin_id"], "missing-bin");
- assert_eq!(invalid_bin_result["order_request_enabled"], false);
+ assert_eq!(invalid_bin_result["checkout_enabled"], false);
assert_eq!(invalid_bin_result["primary_bin_verified"], false);
assert_eq!(
invalid_bin_result["reason_codes"][1],
@@ -6258,10 +6258,7 @@ fn market_order_request_readiness_gates_buyer_intent_actions() {
invalid_bin_listing["result"]["primary_bin_id"],
"missing-bin"
);
- assert_eq!(
- invalid_bin_listing["result"]["order_request_enabled"],
- false
- );
+ assert_eq!(invalid_bin_listing["result"]["checkout_enabled"], false);
assert_eq!(invalid_bin_listing["result"]["primary_bin_verified"], false);
assert_eq!(
invalid_bin_listing["result"]["reason_codes"][1],
@@ -6285,7 +6282,7 @@ fn market_order_request_readiness_gates_buyer_intent_actions() {
"pasture-eggs",
]);
assert_eq!(restored_listing["result"]["primary_bin_id"], "bin-1");
- assert_eq!(restored_listing["result"]["order_request_enabled"], true);
+ assert_eq!(restored_listing["result"]["checkout_enabled"], true);
assert_eq!(restored_listing["result"]["primary_bin_verified"], true);
assert!(
restored_listing["result"]
@@ -6345,28 +6342,28 @@ fn required_approval_token_rejects_absent_empty_and_whitespace_values() {
"sync.push",
&["--relay", "ws://127.0.0.1:9", "sync", "push"],
);
- assert_required_approval_token_rejected(&sandbox, "order.submit", &["order", "submit"]);
+ assert_required_approval_token_rejected(&sandbox, "trade.submit", &["trade", "submit"]);
assert_required_approval_token_rejected(
&sandbox,
- "order.rebind",
- &["order", "rebind", "ord_missing", "acct_missing"],
+ "trade.rebind",
+ &["trade", "rebind", "ord_missing", "acct_missing"],
);
- assert_required_approval_token_rejected(&sandbox, "order.accept", &["order", "accept"]);
+ assert_required_approval_token_rejected(&sandbox, "trade.accept", &["trade", "accept"]);
assert_required_approval_token_rejected(
&sandbox,
- "order.decline",
- &["order", "decline", "--reason", "out_of_stock"],
+ "trade.decline",
+ &["trade", "decline", "--reason", "out_of_stock"],
);
assert_required_approval_token_rejected(
&sandbox,
- "order.cancel",
- &["order", "cancel", "--reason", "changed plans"],
+ "trade.cancel",
+ &["trade", "cancel", "--reason", "changed plans"],
);
assert_required_approval_token_rejected(
&sandbox,
- "order.revision.accept",
+ "trade.revision.accept",
&[
- "order",
+ "trade",
"revision",
"accept",
"ord_pending",
@@ -6376,9 +6373,9 @@ fn required_approval_token_rejects_absent_empty_and_whitespace_values() {
);
assert_required_approval_token_rejected(
&sandbox,
- "order.revision.decline",
+ "trade.revision.decline",
&[
- "order",
+ "trade",
"revision",
"decline",
"ord_pending",
@@ -6420,11 +6417,11 @@ fn order_submit_missing_order_returns_not_found_while_read_view_stays_successful
let get = sandbox.json_success(&[
"--format",
"json",
- "order",
+ "trade",
"get",
"ord_missing_submit_target",
]);
- assert_eq!(get["operation_id"], "order.get");
+ assert_eq!(get["operation_id"], "trade.get");
assert_eq!(get["result"]["state"], "missing");
assert_eq!(get["errors"].as_array().expect("errors").len(), 0);
@@ -6433,18 +6430,18 @@ fn order_submit_missing_order_returns_not_found_while_read_view_stays_successful
"json",
"--approval-token",
"approve",
- "order",
+ "trade",
"submit",
"ord_missing_submit_target",
]);
assert_eq!(output.status.code(), Some(4));
- assert_eq!(submit["operation_id"], "order.submit");
+ assert_eq!(submit["operation_id"], "trade.submit");
assert_eq!(submit["errors"][0]["code"], "not_found");
assert_eq!(submit["errors"][0]["exit_code"], 4);
assert_eq!(submit["errors"][0]["detail"]["class"], "resource");
- assert_no_removed_command_reference(&submit, &["order", "submit"]);
- assert_no_daemon_runtime_reference(&submit, &["order", "submit"]);
+ assert_no_removed_command_reference(&submit, &["trade", "submit"]);
+ assert_no_daemon_runtime_reference(&submit, &["trade", "submit"]);
}
fn create_ready_order(sandbox: &RadrootsCliSandbox, basket_id: &str) -> String {
@@ -6466,7 +6463,7 @@ fn create_ready_order(sandbox: &RadrootsCliSandbox, basket_id: &str) -> String {
"2",
]);
let quote = sandbox.json_success(&["--format", "json", "basket", "quote", "create", basket_id]);
- quote["result"]["quote"]["order_id"]
+ quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id")
.to_owned()
@@ -6477,13 +6474,13 @@ fn rewrite_order_bin(sandbox: &RadrootsCliSandbox, order_id: &str, bin_id: &str)
.root()
.join("data/apps/cli/orders/drafts")
.join(format!("{order_id}.toml"));
- let contents = fs::read_to_string(&path).expect("read order draft");
+ let contents = fs::read_to_string(&path).expect("read trade draft");
let updated = contents.replace(
"bin_id = \"bin-1\"",
format!("bin_id = \"{bin_id}\"").as_str(),
);
assert_ne!(updated, contents);
- fs::write(path, updated).expect("rewrite order draft bin");
+ fs::write(path, updated).expect("rewrite trade draft bin");
}
fn rewrite_order_buyer_actor_pubkey(sandbox: &RadrootsCliSandbox, order_id: &str, pubkey: &str) {
@@ -6491,7 +6488,7 @@ fn rewrite_order_buyer_actor_pubkey(sandbox: &RadrootsCliSandbox, order_id: &str
.root()
.join("data/apps/cli/orders/drafts")
.join(format!("{order_id}.toml"));
- let contents = fs::read_to_string(&path).expect("read order draft");
+ let contents = fs::read_to_string(&path).expect("read trade draft");
let mut in_buyer_actor = false;
let mut replaced = false;
let updated = contents
@@ -6511,7 +6508,7 @@ fn rewrite_order_buyer_actor_pubkey(sandbox: &RadrootsCliSandbox, order_id: &str
.collect::<Vec<_>>()
.join("\n");
assert!(replaced, "buyer_actor pubkey field");
- fs::write(path, format!("{updated}\n")).expect("rewrite order draft buyer actor");
+ fs::write(path, format!("{updated}\n")).expect("rewrite trade draft buyer actor");
}
fn line_indent(line: &str) -> &str {
@@ -6614,11 +6611,11 @@ fn buyer_target_flow_acceptance_uses_target_operations() {
assert_eq!(quote["operation_id"], "basket.quote.create");
assert_eq!(quote["result"]["state"], "quoted");
assert_no_removed_command_reference("e, &["basket", "quote", "create"]);
- let order_id = quote["result"]["quote"]["order_id"]
+ let order_id = quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id");
let quote_economics = "e["result"]["quote"]["economics"];
- let order_file = quote["result"]["order"]["file"]
+ let order_file = quote["result"]["trade"]["file"]
.as_str()
.expect("order file");
assert_eq!(quote["result"]["quote"]["ready_for_submit"], true);
@@ -6635,56 +6632,56 @@ fn buyer_target_flow_acceptance_uses_target_operations() {
assert_eq!(quote_economics["discounts"], Value::Array(Vec::new()));
assert_eq!(quote_economics["adjustments"], Value::Array(Vec::new()));
assert_eq!(
- quote["result"]["order"]["economics"],
+ quote["result"]["trade"]["economics"],
quote_economics.clone()
);
- let order_draft = fs::read_to_string(order_file).expect("read order draft");
+ let order_draft = fs::read_to_string(order_file).expect("read trade draft");
assert!(order_draft.contains("[buyer_actor]"));
assert!(order_draft.contains("source = \"resolved_account\""));
assert!(order_draft.contains("[order.economics]"));
assert!(order_draft.contains("pricing_basis = \"listing_event\""));
- assert_eq!(quote["result"]["order"]["buyer_account_id"], account_id);
+ assert_eq!(quote["result"]["trade"]["buyer_account_id"], account_id);
assert_eq!(
- quote["result"]["order"]["buyer_actor_source"],
+ quote["result"]["trade"]["buyer_actor_source"],
"resolved_account"
);
assert_eq!(
- quote["result"]["order"]["listing_event_id"],
+ quote["result"]["trade"]["listing_event_id"],
listing_event_id
);
- let orders = sandbox.json_success(&["--format", "json", "order", "list"]);
- assert_eq!(orders["operation_id"], "order.list");
+ let orders = sandbox.json_success(&["--format", "json", "trade", "list"]);
+ assert_eq!(orders["operation_id"], "trade.list");
assert_eq!(orders["result"]["state"], "ready");
assert_eq!(orders["result"]["count"], 1);
- assert_eq!(orders["result"]["orders"][0]["id"], order_id);
- assert_eq!(orders["result"]["orders"][0]["ready_for_submit"], true);
+ assert_eq!(orders["result"]["trades"][0]["id"], order_id);
+ assert_eq!(orders["result"]["trades"][0]["ready_for_submit"], true);
assert_eq!(
- orders["result"]["orders"][0]["listing_event_id"],
+ orders["result"]["trades"][0]["listing_event_id"],
listing_event_id
);
assert_eq!(
- orders["result"]["orders"][0]["buyer_account_id"],
+ orders["result"]["trades"][0]["buyer_account_id"],
account_id
);
assert_eq!(
- orders["result"]["orders"][0]["buyer_actor_source"],
+ orders["result"]["trades"][0]["buyer_actor_source"],
"resolved_account"
);
assert_eq!(
- orders["result"]["orders"][0]["economics"],
+ orders["result"]["trades"][0]["economics"],
quote_economics.clone()
);
- assert_eq!(orders["result"]["orders"][0]["issues"], Value::Null);
- assert_no_removed_command_reference(&orders, &["order", "list"]);
- assert_no_daemon_runtime_reference(&orders, &["order", "list"]);
+ assert_eq!(orders["result"]["trades"][0]["issues"], Value::Null);
+ assert_no_removed_command_reference(&orders, &["trade", "list"]);
+ assert_no_daemon_runtime_reference(&orders, &["trade", "list"]);
let submit =
- sandbox.json_success(&["--format", "json", "--dry-run", "order", "submit", order_id]);
- assert_eq!(submit["operation_id"], "order.submit");
+ sandbox.json_success(&["--format", "json", "--dry-run", "trade", "submit", order_id]);
+ assert_eq!(submit["operation_id"], "trade.submit");
assert_eq!(submit["dry_run"], true);
assert_eq!(submit["result"]["state"], "dry_run");
- assert_eq!(submit["result"]["source"], "SDK order submit · local key");
+ assert_eq!(submit["result"]["source"], "SDK trade submit · local key");
assert_eq!(submit["result"]["event_kind"], 3422);
assert_eq!(
submit["result"]["target_relays"][0],
@@ -6697,21 +6694,21 @@ fn buyer_target_flow_acceptance_uses_target_operations() {
.len(),
64
);
- assert_no_removed_command_reference(&submit, &["order", "submit", "--dry-run"]);
- assert_no_daemon_runtime_reference(&submit, &["order", "submit", "--dry-run"]);
+ assert_no_removed_command_reference(&submit, &["trade", "submit", "--dry-run"]);
+ assert_no_daemon_runtime_reference(&submit, &["trade", "submit", "--dry-run"]);
let (output, unavailable_submit) = sandbox.json_output(&[
"--format",
"json",
"--approval-token",
"approve",
- "order",
+ "trade",
"submit",
order_id,
]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(3), "{unavailable_submit}");
- assert_eq!(unavailable_submit["operation_id"], "order.submit");
+ assert_eq!(unavailable_submit["operation_id"], "trade.submit");
assert_eq!(unavailable_submit["result"], Value::Null);
assert_eq!(
unavailable_submit["errors"][0]["code"],
@@ -6731,11 +6728,11 @@ fn buyer_target_flow_acceptance_uses_target_operations() {
.expect("message")
.contains("SDK relay publish")
);
- assert_no_removed_command_reference(&unavailable_submit, &["order", "submit"]);
- assert_no_daemon_runtime_reference(&unavailable_submit, &["order", "submit"]);
+ assert_no_removed_command_reference(&unavailable_submit, &["trade", "submit"]);
+ assert_no_daemon_runtime_reference(&unavailable_submit, &["trade", "submit"]);
- let order_after_submit = sandbox.json_success(&["--format", "json", "order", "get", order_id]);
- assert_eq!(order_after_submit["operation_id"], "order.get");
+ let order_after_submit = sandbox.json_success(&["--format", "json", "trade", "get", order_id]);
+ assert_eq!(order_after_submit["operation_id"], "trade.get");
assert_eq!(order_after_submit["result"]["state"], "ready");
assert_eq!(
order_after_submit["result"]["economics"],
@@ -6743,26 +6740,26 @@ fn buyer_target_flow_acceptance_uses_target_operations() {
);
assert_eq!(order_after_submit["result"]["job"], Value::Null);
assert_eq!(order_after_submit["result"]["workflow"], Value::Null);
- assert_no_daemon_runtime_reference(&order_after_submit, &["order", "get"]);
+ assert_no_daemon_runtime_reference(&order_after_submit, &["trade", "get"]);
let (watch_output, watch) =
- sandbox.json_output(&["--format", "json", "order", "event", "watch", order_id]);
+ sandbox.json_output(&["--format", "json", "trade", "event", "watch", order_id]);
assert!(!watch_output.status.success());
assert_eq!(watch_output.status.code(), Some(3));
- assert_eq!(watch["operation_id"], "order.event.watch");
+ assert_eq!(watch["operation_id"], "trade.event.watch");
assert_eq!(watch["result"], Value::Null);
assert_eq!(watch["errors"][0]["code"], "not_implemented");
assert_eq!(watch["errors"][0]["detail"]["state"], "not_implemented");
- assert_eq!(watch["errors"][0]["detail"]["order_id"], order_id);
+ assert_eq!(watch["errors"][0]["detail"]["trade_id"], order_id);
assert_eq!(
watch["next_actions"][0]["command"],
- format!("radroots order status get {order_id}")
+ format!("radroots trade status get {order_id}")
);
- assert_no_daemon_runtime_reference(&watch, &["order", "event", "watch"]);
+ assert_no_daemon_runtime_reference(&watch, &["trade", "event", "watch"]);
assert!(
!serde_json::to_string(&watch)
.expect("watch json")
- .contains("local order drafts")
+ .contains("local trade drafts")
);
}
@@ -6771,7 +6768,7 @@ fn order_get_and_list_report_missing_bound_buyer_account() {
let sandbox = RadrootsCliSandbox::new();
let order_id = create_ready_order(&sandbox, "missing_buyer_account");
- let ready = sandbox.json_success(&["--format", "json", "order", "get", order_id.as_str()]);
+ let ready = sandbox.json_success(&["--format", "json", "trade", "get", order_id.as_str()]);
let account_id = ready["result"]["buyer_account_id"]
.as_str()
.expect("buyer account id");
@@ -6790,8 +6787,8 @@ fn order_get_and_list_report_missing_bound_buyer_account() {
account_id,
]);
- let missing = sandbox.json_success(&["--format", "json", "order", "get", order_id.as_str()]);
- assert_eq!(missing["operation_id"], "order.get");
+ let missing = sandbox.json_success(&["--format", "json", "trade", "get", order_id.as_str()]);
+ assert_eq!(missing["operation_id"], "trade.get");
assert_eq!(missing["result"]["state"], "draft");
assert_eq!(missing["result"]["ready_for_submit"], false);
assert_eq!(missing["result"]["buyer_account_id"], account_id);
@@ -6816,14 +6813,14 @@ fn order_get_and_list_report_missing_bound_buyer_account() {
.expect("actions")
.iter()
.any(|action| action
- == &Value::String(format!("radroots order rebind {order_id} <selector>")))
+ == &Value::String(format!("radroots trade rebind {order_id} <selector>")))
);
- let list = sandbox.json_success(&["--format", "json", "order", "list"]);
+ let list = sandbox.json_success(&["--format", "json", "trade", "list"]);
assert_eq!(list["result"]["state"], "degraded");
- assert_eq!(list["result"]["orders"][0]["ready_for_submit"], false);
+ assert_eq!(list["result"]["trades"][0]["ready_for_submit"], false);
assert!(
- list["result"]["orders"][0]["issues"]
+ list["result"]["trades"][0]["issues"]
.as_array()
.expect("issues")
.iter()
@@ -6834,15 +6831,15 @@ fn order_get_and_list_report_missing_bound_buyer_account() {
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"submit",
order_id.as_str(),
]);
assert!(!submit_output.status.success());
assert_eq!(submit_output.status.code(), Some(5));
- assert_eq!(submit["operation_id"], "order.submit");
+ assert_eq!(submit["operation_id"], "trade.submit");
assert_eq!(submit["errors"][0]["code"], "account_unresolved");
- assert_eq!(submit["errors"][0]["detail"]["order_id"], order_id);
+ assert_eq!(submit["errors"][0]["detail"]["trade_id"], order_id);
}
#[test]
@@ -6890,11 +6887,11 @@ fn order_get_marks_watch_only_bound_buyer_unready() {
"create",
"watch_buyer",
]);
- let order_id = quote["result"]["quote"]["order_id"]
+ let order_id = quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id");
- let get = sandbox.json_success(&["--format", "json", "order", "get", order_id]);
+ let get = sandbox.json_success(&["--format", "json", "trade", "get", order_id]);
assert_eq!(get["result"]["state"], "draft");
assert_eq!(get["result"]["ready_for_submit"], false);
assert_eq!(get["result"]["buyer_account_id"], account_id);
@@ -6919,10 +6916,10 @@ fn order_get_marks_watch_only_bound_buyer_unready() {
);
let (submit_output, submit) =
- sandbox.json_output(&["--format", "json", "--dry-run", "order", "submit", order_id]);
+ sandbox.json_output(&["--format", "json", "--dry-run", "trade", "submit", order_id]);
assert!(!submit_output.status.success());
assert_eq!(submit_output.status.code(), Some(7));
- assert_eq!(submit["operation_id"], "order.submit");
+ assert_eq!(submit["operation_id"], "trade.submit");
assert_eq!(submit["errors"][0]["code"], "account_watch_only");
assert_eq!(
submit["errors"][0]["detail"]["order_buyer_account_id"],
@@ -6937,7 +6934,7 @@ fn order_get_marks_bound_buyer_pubkey_mismatch_unready() {
let other_pubkey = identity_public(93).public_key_hex;
rewrite_order_buyer_actor_pubkey(&sandbox, order_id.as_str(), other_pubkey.as_str());
- let get = sandbox.json_success(&["--format", "json", "order", "get", order_id.as_str()]);
+ let get = sandbox.json_success(&["--format", "json", "trade", "get", order_id.as_str()]);
assert_eq!(get["result"]["state"], "draft");
assert_eq!(get["result"]["ready_for_submit"], false);
assert_eq!(get["result"]["buyer_custody"], "secret_backed");
@@ -6955,7 +6952,7 @@ fn order_get_marks_bound_buyer_pubkey_mismatch_unready() {
.expect("actions")
.iter()
.any(|action| action
- == &Value::String(format!("radroots order rebind {order_id} <selector>")))
+ == &Value::String(format!("radroots trade rebind {order_id} <selector>")))
);
}
@@ -6977,15 +6974,15 @@ fn order_rebind_previews_and_writes_bound_buyer_actor_updates() {
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"rebind",
order_id.as_str(),
second_account_id,
]);
- assert_eq!(dry_run["operation_id"], "order.rebind");
+ assert_eq!(dry_run["operation_id"], "trade.rebind");
assert_eq!(dry_run["result"]["state"], "dry_run");
- assert_eq!(dry_run["result"]["from_order_id"], order_id);
- assert_eq!(dry_run["result"]["order_id_changed"], true);
+ assert_eq!(dry_run["result"]["from_trade_id"], order_id);
+ assert_eq!(dry_run["result"]["trade_id_changed"], true);
assert_eq!(dry_run["result"]["buyer_pubkey_changed"], true);
assert_eq!(dry_run["result"]["to_buyer_account_id"], second_account_id);
assert_eq!(
@@ -7000,13 +6997,13 @@ fn order_rebind_previews_and_writes_bound_buyer_actor_updates() {
let (unapproved_output, unapproved) = sandbox.json_output(&[
"--format",
"json",
- "order",
+ "trade",
"rebind",
order_id.as_str(),
second_account_id,
]);
assert!(!unapproved_output.status.success());
- assert_eq!(unapproved["operation_id"], "order.rebind");
+ assert_eq!(unapproved["operation_id"], "trade.rebind");
assert_eq!(unapproved["errors"][0]["code"], "approval_required");
let rebound = sandbox.json_success(&[
@@ -7014,16 +7011,16 @@ fn order_rebind_previews_and_writes_bound_buyer_actor_updates() {
"json",
"--approval-token",
"approve",
- "order",
+ "trade",
"rebind",
order_id.as_str(),
second_account_id,
]);
- assert_eq!(rebound["operation_id"], "order.rebind");
+ assert_eq!(rebound["operation_id"], "trade.rebind");
assert_eq!(rebound["result"]["state"], "rebound");
- assert_eq!(rebound["result"]["from_order_id"], order_id);
- assert_eq!(rebound["result"]["order_id_changed"], true);
- let rebound_order_id = rebound["result"]["to_order_id"]
+ assert_eq!(rebound["result"]["from_trade_id"], order_id);
+ assert_eq!(rebound["result"]["trade_id_changed"], true);
+ let rebound_order_id = rebound["result"]["to_trade_id"]
.as_str()
.expect("rebound order id");
assert_ne!(rebound_order_id, order_id);
@@ -7035,7 +7032,7 @@ fn order_rebind_previews_and_writes_bound_buyer_actor_updates() {
assert!(after.contains(format!("order_id = \"{rebound_order_id}\"").as_str()));
assert!(after.contains(format!("quote_id = \"quote_{rebound_order_id}\"").as_str()));
- let get = sandbox.json_success(&["--format", "json", "order", "get", rebound_order_id]);
+ let get = sandbox.json_success(&["--format", "json", "trade", "get", rebound_order_id]);
assert_eq!(get["result"]["state"], "ready");
assert_eq!(get["result"]["buyer_account_id"], second_account_id);
assert_eq!(get["result"]["buyer_actor_source"], "order_rebind");
@@ -7045,14 +7042,14 @@ fn order_rebind_previews_and_writes_bound_buyer_actor_updates() {
"json",
"--approval-token",
"approve",
- "order",
+ "trade",
"rebind",
rebound_order_id,
second_account_id,
]);
assert_eq!(same["result"]["state"], "rebound");
- assert_eq!(same["result"]["order_id_changed"], false);
- assert_eq!(same["result"]["to_order_id"], rebound_order_id);
+ assert_eq!(same["result"]["trade_id_changed"], false);
+ assert_eq!(same["result"]["to_trade_id"], rebound_order_id);
}
#[test]
@@ -7096,7 +7093,7 @@ fn order_rebind_refuses_visible_published_request() {
"create",
"visible_rebind",
]);
- let order_id = quote["result"]["quote"]["order_id"]
+ let order_id = quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id");
let economics: RadrootsOrderEconomics =
@@ -7120,7 +7117,7 @@ fn order_rebind_refuses_visible_published_request() {
"--dry-run",
"--relay",
relay.endpoint(),
- "order",
+ "trade",
"rebind",
order_id,
target_account_id,
@@ -7129,7 +7126,7 @@ fn order_rebind_refuses_visible_published_request() {
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(10));
- assert_eq!(value["operation_id"], "order.rebind");
+ assert_eq!(value["operation_id"], "trade.rebind");
assert_eq!(value["errors"][0]["code"], "validation_failed");
assert_eq!(
value["errors"][0]["detail"]["existing_request_check"],
@@ -7184,7 +7181,7 @@ fn order_status_and_event_list_use_draft_context_after_account_override_drift()
"create",
"draft_status",
]);
- let order_id = quote["result"]["quote"]["order_id"]
+ let order_id = quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id");
let economics: RadrootsOrderEconomics =
@@ -7206,14 +7203,14 @@ fn order_status_and_event_list_use_draft_context_after_account_override_drift()
"json",
"--account-id",
drift_account_id,
- "order",
+ "trade",
"status",
"get",
order_id,
]);
- assert_eq!(status["operation_id"], "order.status.get");
- assert_eq!(status["result"]["source"], "SDK local order projection");
+ assert_eq!(status["operation_id"], "trade.status.get");
+ assert_eq!(status["result"]["source"], "SDK local trade projection");
assert_eq!(
status["result"]["actor_context_source"],
"sdk_local_projection"
@@ -7230,18 +7227,18 @@ fn order_status_and_event_list_use_draft_context_after_account_override_drift()
drift_account_id,
"--relay",
event_list_relay.endpoint(),
- "order",
+ "trade",
"event",
"list",
order_id,
]);
event_list_relay.join();
- assert_eq!(events["operation_id"], "order.event.list");
+ assert_eq!(events["operation_id"], "trade.event.list");
assert_eq!(events["result"]["actor_context_source"], "order_draft");
assert_eq!(events["result"]["seller_pubkey"], "1".repeat(64));
assert_eq!(events["result"]["count"], 1);
- assert_eq!(events["result"]["orders"][0]["id"], order_id);
+ assert_eq!(events["result"]["trades"][0]["id"], order_id);
}
#[test]
@@ -7299,7 +7296,7 @@ fn order_cancel_uses_bound_buyer_after_default_account_drift() {
"create",
"bound_cancel",
]);
- let order_id = quote["result"]["quote"]["order_id"]
+ let order_id = quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id");
let economics: RadrootsOrderEconomics =
@@ -7331,7 +7328,7 @@ fn order_cancel_uses_bound_buyer_after_default_account_drift() {
"--dry-run",
"--relay",
relay.endpoint(),
- "order",
+ "trade",
"cancel",
order_id,
"--reason",
@@ -7339,7 +7336,7 @@ fn order_cancel_uses_bound_buyer_after_default_account_drift() {
]);
relay.join();
- assert_eq!(cancel["operation_id"], "order.cancel");
+ assert_eq!(cancel["operation_id"], "trade.cancel");
assert_eq!(cancel["result"]["state"], "dry_run");
assert_eq!(cancel["result"]["buyer_pubkey"], buyer.public_key_hex());
assert_eq!(cancel["result"]["signer_mode"], "local");
@@ -7356,7 +7353,7 @@ fn buyer_side_order_writes_reject_conflicting_account_override_for_local_draft()
for (operation_id, command) in [
(
- "order.revision.accept",
+ "trade.revision.accept",
vec![
"--format",
"json",
@@ -7365,7 +7362,7 @@ fn buyer_side_order_writes_reject_conflicting_account_override_for_local_draft()
drift_account_id,
"--relay",
"ws://127.0.0.1:9",
- "order",
+ "trade",
"revision",
"accept",
order_id.as_str(),
@@ -7374,7 +7371,7 @@ fn buyer_side_order_writes_reject_conflicting_account_override_for_local_draft()
],
),
(
- "order.cancel",
+ "trade.cancel",
vec![
"--format",
"json",
@@ -7383,7 +7380,7 @@ fn buyer_side_order_writes_reject_conflicting_account_override_for_local_draft()
drift_account_id,
"--relay",
"ws://127.0.0.1:9",
- "order",
+ "trade",
"cancel",
order_id.as_str(),
"--reason",
@@ -7398,7 +7395,7 @@ fn buyer_side_order_writes_reject_conflicting_account_override_for_local_draft()
assert_eq!(value["operation_id"], operation_id);
assert_eq!(value["result"], Value::Null);
assert_eq!(value["errors"][0]["code"], "account_mismatch");
- assert_eq!(value["errors"][0]["detail"]["order_id"], order_id);
+ assert_eq!(value["errors"][0]["detail"]["trade_id"], order_id);
assert_eq!(
value["errors"][0]["detail"]["attempted_buyer_account_id"],
drift_account_id
@@ -7419,19 +7416,19 @@ fn order_submit_requires_local_replica_freshness_before_signing() {
"ws://127.0.0.1:9",
"--approval-token",
"approve",
- "order",
+ "trade",
"submit",
order_id.as_str(),
]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(3));
- assert_eq!(value["operation_id"], "order.submit");
+ assert_eq!(value["operation_id"], "trade.submit");
assert_eq!(value["errors"][0]["code"], "operation_unavailable");
assert_eq!(value["errors"][0]["detail"]["state"], "unconfigured");
assert_eq!(
value["errors"][0]["detail"]["issues"][0]["field"],
- "order.listing_addr"
+ "trade.listing_addr"
);
assert!(
value["errors"][0]["message"]
@@ -7451,20 +7448,20 @@ fn order_submit_dry_run_requires_local_replica_freshness() {
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"submit",
order_id.as_str(),
]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(3));
- assert_eq!(value["operation_id"], "order.submit");
+ assert_eq!(value["operation_id"], "trade.submit");
assert_eq!(value["dry_run"], true);
assert_eq!(value["errors"][0]["code"], "operation_unavailable");
assert_eq!(value["errors"][0]["detail"]["state"], "unconfigured");
assert_eq!(
value["errors"][0]["detail"]["issues"][0]["field"],
- "order.listing_addr"
+ "trade.listing_addr"
);
}
@@ -7481,18 +7478,18 @@ fn order_submit_rejects_missing_or_archived_local_listing_before_publish() {
"ws://127.0.0.1:9",
"--approval-token",
"approve",
- "order",
+ "trade",
"submit",
order_id.as_str(),
]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(3));
- assert_eq!(value["operation_id"], "order.submit");
+ assert_eq!(value["operation_id"], "trade.submit");
assert_eq!(value["errors"][0]["code"], "operation_unavailable");
assert_eq!(
value["errors"][0]["detail"]["issues"][0]["field"],
- "order.listing_addr"
+ "trade.listing_addr"
);
assert!(
value["errors"][0]["message"]
@@ -7516,18 +7513,18 @@ fn order_submit_rejects_superseded_local_listing_event_before_publish() {
"ws://127.0.0.1:9",
"--approval-token",
"approve",
- "order",
+ "trade",
"submit",
order_id.as_str(),
]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(3));
- assert_eq!(value["operation_id"], "order.submit");
+ assert_eq!(value["operation_id"], "trade.submit");
assert_eq!(value["errors"][0]["code"], "operation_unavailable");
assert_eq!(
value["errors"][0]["detail"]["issues"][0]["field"],
- "order.listing_event_id"
+ "trade.listing_event_id"
);
assert!(
value["errors"][0]["detail"]["issues"][0]["message"]
@@ -7565,7 +7562,7 @@ fn order_submit_rejects_over_available_quantity_before_publish() {
"create",
"over_quantity",
]);
- let order_id = quote["result"]["quote"]["order_id"]
+ let order_id = quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id");
@@ -7576,14 +7573,14 @@ fn order_submit_rejects_over_available_quantity_before_publish() {
"ws://127.0.0.1:9",
"--approval-token",
"approve",
- "order",
+ "trade",
"submit",
order_id,
]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(10));
- assert_eq!(value["operation_id"], "order.submit");
+ assert_eq!(value["operation_id"], "trade.submit");
assert_eq!(value["errors"][0]["code"], "validation_failed");
assert_eq!(
value["errors"][0]["detail"]["issues"][0]["code"],
@@ -7595,8 +7592,8 @@ fn order_submit_rejects_over_available_quantity_before_publish() {
.expect("issue message")
.contains("available quantity 5")
);
- assert_no_removed_command_reference(&value, &["order", "submit"]);
- assert_no_daemon_runtime_reference(&value, &["order", "submit"]);
+ assert_no_removed_command_reference(&value, &["trade", "submit"]);
+ assert_no_daemon_runtime_reference(&value, &["trade", "submit"]);
}
#[test]
@@ -7612,14 +7609,14 @@ fn order_submit_rejects_unknown_local_listing_bin_before_publish() {
"ws://127.0.0.1:9",
"--approval-token",
"approve",
- "order",
+ "trade",
"submit",
order_id.as_str(),
]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(10));
- assert_eq!(value["operation_id"], "order.submit");
+ assert_eq!(value["operation_id"], "trade.submit");
assert_eq!(value["errors"][0]["code"], "validation_failed");
assert_eq!(
value["errors"][0]["detail"]["issues"][0]["code"],
@@ -7635,8 +7632,8 @@ fn order_submit_rejects_unknown_local_listing_bin_before_publish() {
.expect("issue message")
.contains("expected primary bin `bin-1`")
);
- assert_no_removed_command_reference(&value, &["order", "submit"]);
- assert_no_daemon_runtime_reference(&value, &["order", "submit"]);
+ assert_no_removed_command_reference(&value, &["trade", "submit"]);
+ assert_no_daemon_runtime_reference(&value, &["trade", "submit"]);
}
#[test]
@@ -7842,14 +7839,14 @@ fn order_submit_rejects_stale_invalid_verified_primary_bin_before_relay_prefligh
"--format",
"json",
"--dry-run",
- "order",
+ "trade",
"submit",
&order_id,
]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(10));
- assert_eq!(value["operation_id"], "order.submit");
+ assert_eq!(value["operation_id"], "trade.submit");
assert_eq!(value["dry_run"], true);
assert_eq!(value["errors"][0]["code"], "validation_failed");
assert_eq!(
@@ -7860,8 +7857,8 @@ fn order_submit_rejects_stale_invalid_verified_primary_bin_before_relay_prefligh
value["errors"][0]["detail"]["issues"][0]["field"],
"inventory.primary_bin_id"
);
- assert_no_removed_command_reference(&value, &["order", "submit", "--dry-run"]);
- assert_no_daemon_runtime_reference(&value, &["order", "submit", "--dry-run"]);
+ assert_no_removed_command_reference(&value, &["trade", "submit", "--dry-run"]);
+ assert_no_daemon_runtime_reference(&value, &["trade", "submit", "--dry-run"]);
}
#[test]
@@ -7892,16 +7889,16 @@ fn order_submit_dry_run_rejects_over_available_quantity_before_relay_preflight()
"create",
"dry_over_quantity",
]);
- let order_id = quote["result"]["quote"]["order_id"]
+ let order_id = quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id");
let (output, value) =
- sandbox.json_output(&["--format", "json", "--dry-run", "order", "submit", order_id]);
+ sandbox.json_output(&["--format", "json", "--dry-run", "trade", "submit", order_id]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(10));
- assert_eq!(value["operation_id"], "order.submit");
+ assert_eq!(value["operation_id"], "trade.submit");
assert_eq!(value["dry_run"], true);
assert_eq!(value["errors"][0]["code"], "validation_failed");
assert_eq!(
@@ -7941,31 +7938,31 @@ fn ready_order_submit_dry_run_validates_local_buyer_authority() {
"create",
"ready_order",
]);
- let order_id = quote["result"]["quote"]["order_id"]
+ let order_id = quote["result"]["quote"]["trade_id"]
.as_str()
.expect("order id");
assert_eq!(quote["result"]["quote"]["ready_for_submit"], true);
assert_eq!(
- quote["result"]["order"]["buyer_account_id"],
+ quote["result"]["trade"]["buyer_account_id"],
first_account_id
);
assert_eq!(
- quote["result"]["order"]["listing_event_id"],
+ quote["result"]["trade"]["listing_event_id"],
listing_event_id
);
let dry_run =
- sandbox.json_success(&["--format", "json", "--dry-run", "order", "submit", order_id]);
- assert_eq!(dry_run["operation_id"], "order.submit");
+ sandbox.json_success(&["--format", "json", "--dry-run", "trade", "submit", order_id]);
+ assert_eq!(dry_run["operation_id"], "trade.submit");
assert_eq!(dry_run["dry_run"], true);
assert_eq!(dry_run["result"]["state"], "dry_run");
- assert_eq!(dry_run["result"]["source"], "SDK order submit · local key");
+ assert_eq!(dry_run["result"]["source"], "SDK trade submit · local key");
assert_eq!(dry_run["result"]["event_kind"], 3422);
assert_eq!(
dry_run["result"]["target_relays"][0],
ORDERABLE_LISTING_RELAY
);
- assert_no_daemon_runtime_reference(&dry_run, &["order", "submit", "--dry-run"]);
+ assert_no_daemon_runtime_reference(&dry_run, &["trade", "submit", "--dry-run"]);
let second = sandbox.json_success(&["--format", "json", "account", "create"]);
let second_account_id = second["result"]["account"]["id"]
@@ -7980,8 +7977,8 @@ fn ready_order_submit_dry_run_validates_local_buyer_authority() {
second_account_id,
]);
let drift =
- sandbox.json_success(&["--format", "json", "--dry-run", "order", "submit", order_id]);
- assert_eq!(drift["operation_id"], "order.submit");
+ sandbox.json_success(&["--format", "json", "--dry-run", "trade", "submit", order_id]);
+ assert_eq!(drift["operation_id"], "trade.submit");
assert_eq!(drift["result"]["state"], "dry_run");
assert_eq!(drift["result"]["buyer_account_id"], first_account_id);
@@ -7991,18 +7988,18 @@ fn ready_order_submit_dry_run_validates_local_buyer_authority() {
"--account-id",
second_account_id,
"--dry-run",
- "order",
+ "trade",
"submit",
order_id,
]);
assert!(!output.status.success());
assert_eq!(output.status.code(), Some(5));
- assert_eq!(mismatch["operation_id"], "order.submit");
+ assert_eq!(mismatch["operation_id"], "trade.submit");
assert_eq!(mismatch["errors"][0]["code"], "account_mismatch");
assert_eq!(mismatch["errors"][0]["detail"]["class"], "account");
- assert_no_removed_command_reference(&mismatch, &["order", "submit", "--dry-run"]);
- assert_no_daemon_runtime_reference(&mismatch, &["order", "submit", "--dry-run"]);
+ assert_no_removed_command_reference(&mismatch, &["trade", "submit", "--dry-run"]);
+ assert_no_daemon_runtime_reference(&mismatch, &["trade", "submit", "--dry-run"]);
let (network_output, network_mismatch) = sandbox.json_output(&[
"--format",
@@ -8013,18 +8010,18 @@ fn ready_order_submit_dry_run_validates_local_buyer_authority() {
"ws://127.0.0.1:9",
"--approval-token",
"approve",
- "order",
+ "trade",
"submit",
order_id,
]);
assert!(!network_output.status.success());
assert_eq!(network_output.status.code(), Some(5));
- assert_eq!(network_mismatch["operation_id"], "order.submit");
+ assert_eq!(network_mismatch["operation_id"], "trade.submit");
assert_eq!(network_mismatch["result"], Value::Null);
assert_eq!(network_mismatch["errors"][0]["code"], "account_mismatch");
assert_eq!(network_mismatch["errors"][0]["detail"]["class"], "account");
- assert_no_daemon_runtime_reference(&network_mismatch, &["order", "submit"]);
+ assert_no_daemon_runtime_reference(&network_mismatch, &["trade", "submit"]);
}
#[test]