Skip to content

Commit

Permalink
Handle next trade on fiatsent (#421)
Browse files Browse the repository at this point in the history
* Handle next trade on fiatsent

Add next_trade_pubkey and next_trade_index fields to order table, this fields should be sent when
the maker of a range order is a buyer, mostrod needs these fields to create the new child order

Implement logic of saving next trade fields after fiat-sent action by buyer

Code refactoring to optimize some parts of the code

Add logic on release to use next trade fields in order when the maker of the range order is the buyer

Update mostro-core version

* Update src/app/release.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Add better error messages and better comments

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
grunch and coderabbitai[bot] authored Jan 10, 2025
1 parent d3c7840 commit a17b33b
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 88 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ uuid = { version = "1.8.0", features = [
"serde",
] }
reqwest = { version = "0.12.1", features = ["json"] }
mostro-core = { version = "0.6.24", features = ["sqlx"] }
mostro-core = { version = "0.6.25", features = ["sqlx"] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
config = "0.15.4"
Expand Down
4 changes: 3 additions & 1 deletion migrations/20221222153301_orders.sql
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ CREATE TABLE IF NOT EXISTS orders (
failed_payment integer default 0,
expires_at integer not null,
trade_index_seller integer default 0,
trade_index_buyer integer default 0
trade_index_buyer integer default 0,
next_trade_pubkey char(64),
next_trade_index integer default 0
);
16 changes: 15 additions & 1 deletion src/app/fiat_sent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub async fn fiat_sent_action(
} else {
return Err(Error::msg("No order id"));
};
let order = match Order::by_id(pool, order_id).await? {
let mut order = match Order::by_id(pool, order_id).await? {
Some(order) => order,
None => {
error!("Order Id {order_id} not found!");
Expand Down Expand Up @@ -54,6 +54,10 @@ pub async fn fiat_sent_action(
.await;
return Ok(());
}
let next_trade: Option<(String, u32)> = match &msg.get_inner_message_kind().payload {
Some(Payload::NextTrade(pubkey, index)) => Some((pubkey.clone(), *index)),
_ => None,
};

// We publish a new replaceable kind nostr event with the status updated
// and update on local database the status and new event id
Expand Down Expand Up @@ -93,5 +97,15 @@ pub async fn fiat_sent_action(
)
.await;

// Update next trade fields only when the buyer is the maker of a range order
// These fields will be used to create the next child order in the range
if order.creator_pubkey == event.rumor.pubkey.to_string() && next_trade.is_some() {
if let Some((pubkey, index)) = next_trade {
order.next_trade_pubkey = Some(pubkey.clone());
order.next_trade_index = Some(index as i64);
order.update(pool).await?;
}
}

Ok(())
}
170 changes: 92 additions & 78 deletions src/app/release.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,51 +72,41 @@ pub async fn release_action(
_ => None,
};

// Check if order id is ok
let order_id = if let Some(order_id) = msg.get_inner_message_kind().id {
let order_id = msg
.get_inner_message_kind()
.id
.ok_or(Error::msg("Order ID is required but was not provided"))?;

let mut order = Order::by_id(pool, order_id)
.await?
.ok_or(Error::msg(format!(
"Order {} not found in database",
order_id
)))?;

let seller_pubkey_hex = order.seller_pubkey.as_ref().ok_or(Error::msg(format!(
"Seller public key not found for order {}",
order_id
} else {
return Err(Error::msg("No order id"));
};
)))?;

let order = match Order::by_id(pool, order_id).await? {
Some(order) => order,
None => {
error!("Order Id {order_id} not found!");
return Ok(());
}
};
let seller_pubkey_hex = match order.seller_pubkey {
Some(ref pk) => pk,
None => {
error!("Order Id {}: Seller pubkey not found!", order.id);
return Ok(());
}
};
let seller_pubkey = event.rumor.pubkey;
let current_status =
Status::from_str(&order.status).map_err(|_| Error::msg("Wrong order status"))?;

let current_status = if let Ok(current_status) = Status::from_str(&order.status) {
current_status
} else {
return Err(Error::msg("Wrong order status"));
};

if current_status != Status::Active
&& current_status != Status::FiatSent
&& current_status != Status::Dispute
{
if !matches!(
current_status,
Status::Active | Status::FiatSent | Status::Dispute
) {
send_cant_do_msg(
request_id,
Some(order.id),
Some(CantDoReason::NotAllowedByStatus),
&event.rumor.pubkey,
)
.await;

return Ok(());
}

if &seller_pubkey.to_string() != seller_pubkey_hex {
if &event.rumor.pubkey.to_string() != seller_pubkey_hex {
send_cant_do_msg(
request_id,
Some(order.id),
Expand All @@ -137,69 +127,93 @@ pub async fn release_action(
)
.await?;

let mut order_updated = update_order_event(my_keys, Status::SettledHoldInvoice, &order).await?;

let (is_range, child_order) = get_child_order(&mut order_updated, request_id, my_keys).await?;
// If we have the next trade data and the order is a range order we create a new order
if is_range && next_trade.is_some() {
if let Some((next_trade_pubkey, next_trade_index)) = next_trade.clone() {
// As we are creating a new order from Mostro to user,
// we need save the trade_pubkey and the last_trade_index for the next trade
let mut child_order = child_order.clone();
child_order.seller_pubkey = Some(next_trade_pubkey.clone());
child_order.creator_pubkey = next_trade_pubkey.clone();
let next_trade_index = Some(next_trade_index as i64);
child_order.trade_index_seller = next_trade_index;
let pool = db::connect().await?;
// We create the new order to send to the user
let new_order = child_order.as_new_order();
// We save the new order as a child of the parent range order
child_order.update(&pool).await?;
let next_trade_pubkey = PublicKey::from_str(&next_trade_pubkey)?;

// We send a message to the order creator with the new order
send_new_order_msg(
request_id,
new_order.id,
Action::NewOrder,
Some(Payload::Order(new_order)),
&next_trade_pubkey,
next_trade_index,
)
.await;
}
}
// We send a message to buyer indicating seller released funds
let buyer_pubkey = PublicKey::from_str(
order
.buyer_pubkey
.as_ref()
.ok_or(Error::msg("Missing buyer pubkey"))?
.as_str(),
)?;

// We send a HoldInvoicePaymentSettled message to seller, the client should
// indicate *funds released* message to seller
send_new_order_msg(
request_id,
None,
Some(order_id),
Action::HoldInvoicePaymentSettled,
Action::Released,
None,
&seller_pubkey,
&buyer_pubkey,
None,
)
.await;
order = update_order_event(my_keys, Status::SettledHoldInvoice, &order).await?;

// We send a message to buyer indicating seller released funds
let buyer_pubkey = match &order.buyer_pubkey {
Some(buyer) => PublicKey::from_str(buyer.as_str())?,
_ => return Err(Error::msg("Missing buyer pubkeys")),
};
// Handle child order for range orders
if let Ok((true, mut child_order)) = get_child_order(&mut order, request_id, my_keys).await {
handle_child_order(&mut child_order, &order, next_trade, pool, request_id).await?;
}

// We send a HoldInvoicePaymentSettled message to seller, the client should
// indicate *funds released* message to seller
send_new_order_msg(
None,
request_id,
Some(order_id),
Action::Released,
Action::HoldInvoicePaymentSettled,
None,
&buyer_pubkey,
&event.rumor.pubkey,
None,
)
.await;

// Finally we try to pay buyer's invoice
let _ = do_payment(order_updated, request_id).await;
let _ = do_payment(order, request_id).await;

Ok(())
}

/// Manages the creation and update of child orders in a range order sequence
///
/// # Arguments
/// * `child_order` - The child order to be created/updated
/// * `order` - The parent order
/// * `next_trade` - Optional tuple of (pubkey, index) for the next trade
/// * `pool` - Database connection pool
/// * `request_id` - Optional request ID for messaging
///
/// # Returns
/// Result indicating success or failure of the operation
async fn handle_child_order(
child_order: &mut Order,
order: &Order,
next_trade: Option<(String, u32)>,
pool: &Pool<Sqlite>,
request_id: Option<u64>,
) -> Result<()> {
if let Some((next_trade_pubkey, next_trade_index)) = next_trade {
if &order.creator_pubkey == order.seller_pubkey.as_ref().unwrap() {
child_order.seller_pubkey = Some(next_trade_pubkey.clone());
child_order.creator_pubkey = next_trade_pubkey.clone();
child_order.trade_index_seller = Some(next_trade_index as i64);
} else if &order.creator_pubkey == order.buyer_pubkey.as_ref().unwrap() {
child_order.buyer_pubkey = Some(next_trade_pubkey.clone());
child_order.creator_pubkey = next_trade_pubkey.clone();
child_order.trade_index_buyer = order.next_trade_index;
}

let new_order = child_order.as_new_order();
let next_trade_pubkey = PublicKey::from_str(&next_trade_pubkey)?;
send_new_order_msg(
request_id,
new_order.id,
Action::NewOrder,
Some(Payload::Order(new_order)),
&next_trade_pubkey,
child_order
.trade_index_buyer
.or(child_order.trade_index_seller),
)
.await;
child_order.clone().update(pool).await?;
}
Ok(())
}

Expand Down
11 changes: 6 additions & 5 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ pub async fn connect() -> Result<Pool<Sqlite>> {
})?;
match SqlitePool::connect(&db_url).await {
Ok(pool) => {
tracing::info!(
"Successfully created Mostro database file at {}",
db_path.display(),
);
match sqlx::migrate!().run(&pool).await {
Ok(_) => (),
Ok(_) => {
tracing::info!(
"Successfully created Mostro database file at {}",
db_path.display(),
);
}
Err(e) => {
// Clean up the created file on migration failure
if let Err(cleanup_err) = std::fs::remove_file(db_path) {
Expand Down

0 comments on commit a17b33b

Please sign in to comment.