radrootsd

JSON-RPC bridge for Radroots event publishing
git clone https://radroots.dev/git/radrootsd.git
Log | Files | Refs | README | LICENSE

commit f548cdbf400ac1759bd2100f9a8576759d7f6e62
parent 8c98d750c8f11a9b8230a09ce51c38b66bd4a952
Author: triesap <tyson@radroots.org>
Date:   Sun, 12 Apr 2026 04:21:43 +0000

bridge: consume shared trade workflow helpers

Diffstat:
MCargo.lock | 27++++++++++++++-------------
Msrc/transport/jsonrpc/methods/bridge/listing_publish.rs | 99+++++++++++++++++++++++++++++++------------------------------------------------
Msrc/transport/jsonrpc/methods/bridge/order_request.rs | 139++++++++++++++++++-------------------------------------------------------------
Msrc/transport/jsonrpc/methods/bridge/public_trade.rs | 122++++++++++++++++++-------------------------------------------------------------
4 files changed, 113 insertions(+), 274 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -1726,7 +1726,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "radroots_core" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "rust_decimal", "rust_decimal_macros", @@ -1736,7 +1736,7 @@ dependencies = [ [[package]] name = "radroots_events" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "radroots_core", "serde", @@ -1746,7 +1746,7 @@ dependencies = [ [[package]] name = "radroots_events_codec" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "nostr", "radroots_core", @@ -1757,7 +1757,7 @@ dependencies = [ [[package]] name = "radroots_identity" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "nostr", "radroots_events", @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "radroots_log" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "chrono", "thiserror 1.0.69", @@ -1782,7 +1782,7 @@ dependencies = [ [[package]] name = "radroots_nostr" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "nostr", "nostr-sdk", @@ -1797,7 +1797,7 @@ dependencies = [ [[package]] name = "radroots_nostr_connect" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "nostr", "serde", @@ -1808,7 +1808,7 @@ dependencies = [ [[package]] name = "radroots_nostr_signer" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "hex", "nostr", @@ -1825,7 +1825,7 @@ dependencies = [ [[package]] name = "radroots_protected_store" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "chacha20poly1305", "getrandom 0.2.17", @@ -1837,7 +1837,7 @@ dependencies = [ [[package]] name = "radroots_runtime" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "anyhow", "chacha20poly1305", @@ -1860,24 +1860,25 @@ dependencies = [ [[package]] name = "radroots_runtime_paths" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "thiserror 1.0.69", ] [[package]] name = "radroots_secret_vault" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" [[package]] name = "radroots_trade" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "radroots_core", "radroots_events", "radroots_events_codec", "serde", "serde_json", + "thiserror 1.0.69", "ts-rs", ] diff --git a/src/transport/jsonrpc/methods/bridge/listing_publish.rs b/src/transport/jsonrpc/methods/bridge/listing_publish.rs @@ -1,12 +1,13 @@ use anyhow::Result; use jsonrpsee::server::RpcModule; -use radroots_events::RadrootsNostrEvent; -use radroots_events::kinds::{KIND_LISTING, KIND_LISTING_DRAFT, is_listing_kind}; use radroots_events::listing::RadrootsListing; use radroots_events_codec::listing::encode::to_wire_parts_with_kind; use radroots_events_codec::wire::WireEventParts; use radroots_nostr::prelude::radroots_nostr_build_event; -use radroots_trade::listing::validation::validate_listing_event; +use radroots_trade::listing::publish::{ + RadrootsTradeListingPublishError, canonicalize_listing_for_seller, resolve_listing_kind, + validate_listing_for_seller, +}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -14,8 +15,8 @@ use crate::core::bridge::publish::{ BridgePublishSettings, connect_and_publish_event, failed_prepublish_execution, }; use crate::core::bridge::store::new_listing_publish_job; -use crate::transport::jsonrpc::auth::require_bridge_auth; use crate::core::nip46::session::Nip46SessionAuthority; +use crate::transport::jsonrpc::auth::require_bridge_auth; use crate::transport::jsonrpc::methods::bridge::shared::{ BridgePublishResponse, ensure_bridge_enabled, fingerprint_bridge_request, normalize_idempotency_key, reserve_bridge_job, resolve_actor_bridge_signer, @@ -64,7 +65,7 @@ async fn publish_listing( ) -> Result<BridgePublishResponse, RpcError> { ensure_bridge_enabled(&ctx)?; let idempotency_key = normalize_idempotency_key(params.idempotency_key)?; - let kind = resolve_listing_kind(params.kind)?; + let kind = resolve_listing_kind(params.kind).map_err(map_listing_publish_error)?; let signer = resolve_actor_bridge_signer( &ctx, params.signer_session_id.as_deref(), @@ -74,7 +75,7 @@ async fn publish_listing( ) .await?; let signer_pubkey = signer.signer_pubkey_hex(); - let listing = canonicalize_listing_for_signer(params.listing, signer_pubkey.as_str()); + let listing = canonicalize_listing_for_seller(params.listing, signer_pubkey.as_str()); let canonical = CanonicalBridgeListingPublishRequest { kind, listing }; let request_fingerprint = fingerprint_bridge_request("bridge.listing.publish", &signer, &canonical)?; @@ -145,46 +146,21 @@ async fn publish_listing( }) } -fn resolve_listing_kind(kind: Option<u32>) -> Result<u32, RpcError> { - let kind = kind.unwrap_or(KIND_LISTING); - if !is_listing_kind(kind) { - return Err(RpcError::InvalidParams(format!( - "listing kind must be {KIND_LISTING} or {KIND_LISTING_DRAFT}" - ))); - } - Ok(kind) -} - -fn canonicalize_listing_for_signer( - mut listing: RadrootsListing, - signer_pubkey: &str, -) -> RadrootsListing { - if listing.farm.pubkey.trim().is_empty() { - listing.farm.pubkey = signer_pubkey.to_string(); - } - listing -} - fn validate_canonical_listing_contract_for_signer( listing: &RadrootsListing, signer_pubkey: &str, parts: &WireEventParts, ) -> Result<radroots_trade::listing::validation::RadrootsTradeListing, RpcError> { - let canonical = RadrootsNostrEvent { - id: String::new(), - author: signer_pubkey.to_string(), - created_at: 0, - kind: parts.kind, - tags: parts.tags.clone(), - content: parts.content.clone(), - sig: String::new(), - }; - let validated = validate_listing_event(&canonical) - .map_err(|error| RpcError::InvalidParams(format!("invalid listing contract: {error}")))?; + let validated = validate_listing_for_seller(listing.clone(), signer_pubkey, parts.kind) + .map_err(map_listing_publish_error)?; debug_assert_eq!(validated.listing.d_tag, listing.d_tag); Ok(validated) } +fn map_listing_publish_error(error: RadrootsTradeListingPublishError) -> RpcError { + RpcError::InvalidParams(error.to_string()) +} + #[cfg(test)] mod tests { use radroots_core::{ @@ -208,21 +184,21 @@ mod tests { use crate::core::Radrootsd; use crate::core::nip46::session::Nip46Session; use crate::transport::jsonrpc::{MethodRegistry, RpcContext}; + use radroots_trade::listing::publish::canonicalize_listing_for_seller; use super::{ - BridgeListingPublishParams, canonicalize_listing_for_signer, publish_listing, - validate_canonical_listing_contract_for_signer, + BridgeListingPublishParams, publish_listing, validate_canonical_listing_contract_for_signer, }; #[test] fn canonicalize_listing_sets_missing_farm_pubkey() { - let listing = canonicalize_listing_for_signer(base_listing(), "abc123"); + let listing = canonicalize_listing_for_seller(base_listing(), "abc123"); assert_eq!(listing.farm.pubkey, "abc123"); } #[test] fn validate_canonical_listing_contract_rejects_mismatched_seller_before_sign() { - let listing = canonicalize_listing_for_signer(base_listing(), "abc123"); + let listing = canonicalize_listing_for_seller(base_listing(), "abc123"); let mut invalid = listing.clone(); invalid.farm.pubkey = "other".to_string(); let parts = to_wire_parts_with_kind(&invalid, KIND_LISTING).expect("wire parts"); @@ -398,25 +374,28 @@ mod tests { let client = RadrootsNostrClient::new(signer_keys.clone()); let client_keys = signer_keys.clone(); let client_pubkey = client_keys.public_key(); - ctx.state.nip46_sessions.insert(Nip46Session { - id: session_id.to_string(), - client, - client_keys, - client_pubkey, - remote_signer_pubkey, - user_pubkey: None, - relays: Vec::new(), - perms: vec!["sign_event".to_string()], - name: None, - url: None, - image: None, - expires_at: Some(Instant::now() + std::time::Duration::from_secs(60)), - auth_required: false, - authorized: true, - auth_url: None, - pending_request: None, - signer_authority: None, - }).await; + ctx.state + .nip46_sessions + .insert(Nip46Session { + id: session_id.to_string(), + client, + client_keys, + client_pubkey, + remote_signer_pubkey, + user_pubkey: None, + relays: Vec::new(), + perms: vec!["sign_event".to_string()], + name: None, + url: None, + image: None, + expires_at: Some(Instant::now() + std::time::Duration::from_secs(60)), + auth_required: false, + authorized: true, + auth_url: None, + pending_request: None, + signer_authority: None, + }) + .await; session_id.to_string() } diff --git a/src/transport/jsonrpc/methods/bridge/order_request.rs b/src/transport/jsonrpc/methods/bridge/order_request.rs @@ -15,6 +15,7 @@ use radroots_nostr::prelude::{ RadrootsNostrFilter, RadrootsNostrKind, radroots_event_ptr_from_nostr, radroots_nostr_build_event, radroots_nostr_filter_tag, radroots_nostr_parse_pubkey, }; +use radroots_trade::order::canonicalize_order_request_for_signer; use serde::Deserialize; use std::time::Duration; use uuid::Uuid; @@ -75,7 +76,13 @@ async fn publish_order_request( ) .await?; let signer_pubkey = signer.signer_pubkey_hex(); - let order = canonicalize_order_request_for_signer(params.order, signer_pubkey.as_str())?; + let order = canonicalize_order_request_for_signer(params.order, signer_pubkey.as_str()) + .map_err(|error| RpcError::InvalidParams(error.to_string()))?; + radroots_nostr_parse_pubkey(&order.buyer_pubkey) + .map_err(|error| RpcError::InvalidParams(format!("invalid order.buyer_pubkey: {error}")))?; + radroots_nostr_parse_pubkey(&order.seller_pubkey).map_err(|error| { + RpcError::InvalidParams(format!("invalid order.seller_pubkey: {error}")) + })?; let request_fingerprint = fingerprint_bridge_request("bridge.order.request", &signer, &order)?; let envelope = TradeListingEnvelope::new( TradeListingMessageType::OrderRequest, @@ -202,90 +209,6 @@ fn synthetic_listing_snapshot(listing_addr: &TradeListingAddress) -> RadrootsNos } } -fn canonicalize_order_request_for_signer( - mut order: TradeOrder, - signer_pubkey: &str, -) -> Result<TradeOrder, RpcError> { - let order_id = - normalized_required_string(std::mem::take(&mut order.order_id), "order.order_id")?; - let listing_addr_raw = normalized_required_string( - std::mem::take(&mut order.listing_addr), - "order.listing_addr", - )?; - let listing_addr = TradeListingAddress::parse(&listing_addr_raw) - .map_err(|error| RpcError::InvalidParams(format!("invalid order.listing_addr: {error}")))?; - if u32::from(listing_addr.kind) != KIND_LISTING { - return Err(RpcError::InvalidParams( - "order.listing_addr must reference a public NIP-99 listing".to_string(), - )); - } - - let buyer_pubkey = if order.buyer_pubkey.trim().is_empty() { - signer_pubkey.to_string() - } else { - normalized_required_string( - std::mem::take(&mut order.buyer_pubkey), - "order.buyer_pubkey", - )? - }; - if buyer_pubkey != signer_pubkey { - return Err(RpcError::InvalidParams( - "order.buyer_pubkey must match the requested bridge signer identity".to_string(), - )); - } - - let seller_pubkey = if order.seller_pubkey.trim().is_empty() { - listing_addr.seller_pubkey.clone() - } else { - normalized_required_string( - std::mem::take(&mut order.seller_pubkey), - "order.seller_pubkey", - )? - }; - if seller_pubkey != listing_addr.seller_pubkey { - return Err(RpcError::InvalidParams( - "order.seller_pubkey must match order.listing_addr seller".to_string(), - )); - } - - radroots_nostr_parse_pubkey(&buyer_pubkey) - .map_err(|error| RpcError::InvalidParams(format!("invalid order.buyer_pubkey: {error}")))?; - radroots_nostr_parse_pubkey(&seller_pubkey).map_err(|error| { - RpcError::InvalidParams(format!("invalid order.seller_pubkey: {error}")) - })?; - - if order.items.is_empty() { - return Err(RpcError::InvalidParams( - "order.items must contain at least one item".to_string(), - )); - } - for (index, item) in order.items.iter_mut().enumerate() { - item.bin_id = normalized_required_string(item.bin_id.clone(), "order.items[].bin_id")?; - if item.bin_count == 0 { - return Err(RpcError::InvalidParams(format!( - "order.items[{index}].bin_count must be greater than zero" - ))); - } - } - - order.order_id = order_id; - order.listing_addr = listing_addr.as_str(); - order.buyer_pubkey = buyer_pubkey; - order.seller_pubkey = seller_pubkey; - if order.discounts.as_ref().is_some_and(Vec::is_empty) { - order.discounts = None; - } - Ok(order) -} - -fn normalized_required_string(value: String, field: &str) -> Result<String, RpcError> { - let value = value.trim().to_string(); - if value.is_empty() { - return Err(RpcError::InvalidParams(format!("{field} cannot be empty"))); - } - Ok(value) -} - #[cfg(test)] mod tests { use radroots_core::RadrootsCoreDiscountValue; @@ -302,10 +225,9 @@ mod tests { use crate::core::Radrootsd; use crate::core::nip46::session::Nip46Session; use crate::transport::jsonrpc::{MethodRegistry, RpcContext}; + use radroots_trade::order::canonicalize_order_request_for_signer; - use super::{ - BridgeOrderRequestParams, canonicalize_order_request_for_signer, publish_order_request, - }; + use super::{BridgeOrderRequestParams, publish_order_request}; #[test] fn canonicalize_order_request_sets_missing_buyer_and_seller_pubkeys() { @@ -487,25 +409,28 @@ mod tests { let client = RadrootsNostrClient::new(signer_keys.clone()); let client_keys = signer_keys.clone(); let client_pubkey = client_keys.public_key(); - ctx.state.nip46_sessions.insert(Nip46Session { - id: session_id.to_string(), - client, - client_keys, - client_pubkey, - remote_signer_pubkey, - user_pubkey: None, - relays: Vec::new(), - perms: vec!["sign_event".to_string()], - name: None, - url: None, - image: None, - expires_at: Some(Instant::now() + std::time::Duration::from_secs(60)), - auth_required: false, - authorized: true, - auth_url: None, - pending_request: None, - signer_authority: None, - }).await; + ctx.state + .nip46_sessions + .insert(Nip46Session { + id: session_id.to_string(), + client, + client_keys, + client_pubkey, + remote_signer_pubkey, + user_pubkey: None, + relays: Vec::new(), + perms: vec!["sign_event".to_string()], + name: None, + url: None, + image: None, + expires_at: Some(Instant::now() + std::time::Duration::from_secs(60)), + auth_required: false, + authorized: true, + auth_url: None, + pending_request: None, + signer_authority: None, + }) + .await; session_id.to_string() } diff --git a/src/transport/jsonrpc/methods/bridge/public_trade.rs b/src/transport/jsonrpc/methods/bridge/public_trade.rs @@ -1,7 +1,6 @@ use anyhow::Result; use jsonrpsee::server::RpcModule; use radroots_events::RadrootsNostrEventPtr; -use radroots_events::kinds::KIND_LISTING; use radroots_events::trade::{ RadrootsTradeDiscountDecision as TradeDiscountDecision, RadrootsTradeMessagePayload as TradeListingMessagePayload, @@ -16,6 +15,7 @@ use radroots_nostr::prelude::{ radroots_nostr_parse_pubkey, }; use radroots_trade::listing::validation::validate_listing_event; +use radroots_trade::public_trade::canonicalize_public_trade_context; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use uuid::Uuid; @@ -63,13 +63,6 @@ struct CanonicalBridgePublicTradeRequest<T> { payload: T, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum ExpectedPublicTradeAuthor { - Buyer, - Seller, - Either, -} - pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> { register_public_trade_method( m, @@ -295,34 +288,19 @@ fn canonicalize_public_trade_params<T>( signer_pubkey: &str, message_type: TradeListingMessageType, ) -> Result<(CanonicalBridgePublicTradeRequest<T>, TradeListingAddress), RpcError> { - let listing_addr = normalized_required_string(params.listing_addr, "listing_addr")?; - let parsed_listing_addr = TradeListingAddress::parse(&listing_addr) - .map_err(|error| RpcError::InvalidParams(format!("invalid listing_addr: {error}")))?; - if u32::from(parsed_listing_addr.kind) != KIND_LISTING { - return Err(RpcError::InvalidParams( - "listing_addr must reference a public NIP-99 listing".to_string(), - )); - } - - let order_id = normalized_required_string(params.order_id, "order_id")?; - let counterparty_pubkey = - normalized_required_string(params.counterparty_pubkey, "counterparty_pubkey")?; - radroots_nostr_parse_pubkey(&counterparty_pubkey).map_err(|error| { + let context = canonicalize_public_trade_context( + params.listing_addr, + params.order_id, + params.counterparty_pubkey, + signer_pubkey, + message_type, + ) + .map_err(|error| RpcError::InvalidParams(error.to_string()))?; + radroots_nostr_parse_pubkey(&context.counterparty_pubkey).map_err(|error| { RpcError::InvalidParams(format!("invalid counterparty_pubkey: {error}")) })?; - - if counterparty_pubkey == signer_pubkey { - return Err(RpcError::InvalidParams( - "counterparty_pubkey must not match the requested bridge signer identity".to_string(), - )); - } - - validate_expected_author( - &parsed_listing_addr, - message_type, - signer_pubkey, - &counterparty_pubkey, - )?; + let parsed_listing_addr = TradeListingAddress::parse(&context.listing_addr) + .map_err(|error| RpcError::InvalidParams(format!("invalid listing_addr: {error}")))?; let listing_event = if message_type.requires_listing_snapshot() { Some(normalize_listing_event_ptr( @@ -353,9 +331,9 @@ fn canonicalize_public_trade_params<T>( Ok(( CanonicalBridgePublicTradeRequest { - listing_addr, - order_id, - counterparty_pubkey, + listing_addr: context.listing_addr, + order_id: context.order_id, + counterparty_pubkey: context.counterparty_pubkey, listing_event, root_event_id, prev_event_id, @@ -365,59 +343,6 @@ fn canonicalize_public_trade_params<T>( )) } -fn validate_expected_author( - listing_addr: &TradeListingAddress, - message_type: TradeListingMessageType, - signer_pubkey: &str, - counterparty_pubkey: &str, -) -> Result<(), RpcError> { - match expected_author(message_type) { - ExpectedPublicTradeAuthor::Seller => { - if signer_pubkey != listing_addr.seller_pubkey { - return Err(RpcError::InvalidParams(format!( - "{message_type:?} must be authored by the listing seller" - ))); - } - } - ExpectedPublicTradeAuthor::Buyer => { - if signer_pubkey == listing_addr.seller_pubkey { - return Err(RpcError::InvalidParams(format!( - "{message_type:?} must be authored by the buyer, not the listing seller" - ))); - } - if counterparty_pubkey != listing_addr.seller_pubkey { - return Err(RpcError::InvalidParams( - "counterparty_pubkey must match the listing seller for buyer-authored trade messages" - .to_string(), - )); - } - } - ExpectedPublicTradeAuthor::Either => {} - } - Ok(()) -} - -fn expected_author(message_type: TradeListingMessageType) -> ExpectedPublicTradeAuthor { - match message_type { - TradeListingMessageType::OrderResponse - | TradeListingMessageType::OrderRevision - | TradeListingMessageType::Answer - | TradeListingMessageType::DiscountOffer - | TradeListingMessageType::FulfillmentUpdate => ExpectedPublicTradeAuthor::Seller, - TradeListingMessageType::OrderRequest - | TradeListingMessageType::OrderRevisionAccept - | TradeListingMessageType::OrderRevisionDecline - | TradeListingMessageType::Question - | TradeListingMessageType::DiscountRequest - | TradeListingMessageType::DiscountAccept - | TradeListingMessageType::DiscountDecline - | TradeListingMessageType::Receipt => ExpectedPublicTradeAuthor::Buyer, - TradeListingMessageType::Cancel => ExpectedPublicTradeAuthor::Either, - TradeListingMessageType::ListingValidateRequest - | TradeListingMessageType::ListingValidateResult => ExpectedPublicTradeAuthor::Either, - } -} - fn normalize_listing_event_ptr( ptr: RadrootsNostrEventPtr, ) -> Result<RadrootsNostrEventPtr, RpcError> { @@ -532,6 +457,7 @@ fn normalized_required_string(value: String, field: &str) -> Result<String, RpcE #[cfg(test)] mod tests { use radroots_core::{RadrootsCoreDecimal, RadrootsCoreDiscountValue, RadrootsCorePercent}; + use radroots_events::kinds::KIND_LISTING; use radroots_events::trade::{ RadrootsTradeDiscountRequest as TradeDiscountRequest, RadrootsTradeOrderResponse as TradeOrderResponse, @@ -731,15 +657,15 @@ mod tests { #[tokio::test] async fn publish_revision_accept_rejects_decline_payload() { - let ctx = buyer_ctx().expect("ctx"); + let (ctx, seller_pubkey) = signer_ctx().expect("ctx"); let err = publish_public_trade( ctx, "bridge.order.revision.accept", TradeListingMessageType::OrderRevisionAccept, BridgePublicTradeParams { - listing_addr: base_listing_addr(base_seller_pubkey()), + listing_addr: base_listing_addr(&seller_pubkey), order_id: "order-1".to_string(), - counterparty_pubkey: base_seller_pubkey().to_string(), + counterparty_pubkey: base_buyer_pubkey().to_string(), listing_event: None, root_event_id: Some("root".to_string()), prev_event_id: Some("prev".to_string()), @@ -758,7 +684,12 @@ mod tests { } fn buyer_ctx() -> Result<RpcContext, RpcError> { + signer_ctx().map(|(ctx, _)| ctx) + } + + fn signer_ctx() -> Result<(RpcContext, String), RpcError> { let identity = RadrootsIdentity::generate(); + let signer_pubkey = identity.public_key_hex(); let metadata: RadrootsNostrMetadata = serde_json::from_str(r#"{"name":"radrootsd-test"}"#).expect("metadata"); let state = Radrootsd::new( @@ -772,7 +703,10 @@ mod tests { Nip46Config::default(), ) .map_err(|error| RpcError::Other(format!("build state: {error}")))?; - Ok(RpcContext::new(state, MethodRegistry::default())) + Ok(( + RpcContext::new(state, MethodRegistry::default()), + signer_pubkey, + )) } fn base_seller_pubkey() -> &'static str {