Skip to content

Commit 5b34c21

Browse files
committed
Remove duplicate ExactScheme and U64String implementations and extract scheme_slug_from_json helper function
* Delete ExactScheme struct with Display, AsRef, FromStr, Serialize, and Deserialize impls from r402-svm/src/exact/types.rs * Add pub use r402::scheme::ExactScheme in r402-svm/src/exact/types.rs * Replace manual Serialize/Deserialize impls for U64String with serde_with::DisplayFromStr in r402/src/proto/mod.rs * Add serde_as attribute to U64String struct in r402/src/proto/mod.rs * Extract
1 parent 5284f1e commit 5b34c21

File tree

2 files changed

+22
-95
lines changed

2 files changed

+22
-95
lines changed

r402-svm/src/exact/types.rs

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! This module defines shared wire format types for SPL Token based payments
44
//! on Solana. Wire format type aliases live in the [`v2`] sub-module.
55
6+
pub use r402::scheme::ExactScheme;
67
use serde::{Deserialize, Serialize};
78
use solana_pubkey::{Pubkey, pubkey};
89
use std::sync::LazyLock;
@@ -26,58 +27,6 @@ use solana_signer::Signer;
2627
#[cfg(any(feature = "client", feature = "facilitator"))]
2728
use solana_transaction::versioned::VersionedTransaction;
2829

29-
/// A unit struct representing the string literal "exact".
30-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31-
pub struct ExactScheme;
32-
33-
impl ExactScheme {
34-
/// The string literal value: `"exact"`.
35-
pub const VALUE: &'static str = "exact";
36-
}
37-
38-
impl AsRef<str> for ExactScheme {
39-
fn as_ref(&self) -> &str {
40-
Self::VALUE
41-
}
42-
}
43-
44-
impl std::str::FromStr for ExactScheme {
45-
type Err = String;
46-
fn from_str(s: &str) -> Result<Self, Self::Err> {
47-
if s == Self::VALUE {
48-
Ok(Self)
49-
} else {
50-
Err(format!("expected '{}', got '{s}'", Self::VALUE))
51-
}
52-
}
53-
}
54-
55-
impl std::fmt::Display for ExactScheme {
56-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57-
f.write_str(Self::VALUE)
58-
}
59-
}
60-
61-
impl Serialize for ExactScheme {
62-
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
63-
serializer.serialize_str(Self::VALUE)
64-
}
65-
}
66-
67-
impl<'de> Deserialize<'de> for ExactScheme {
68-
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
69-
let s = String::deserialize(deserializer)?;
70-
if s == Self::VALUE {
71-
Ok(Self)
72-
} else {
73-
Err(serde::de::Error::custom(format!(
74-
"expected '{}', got '{s}'",
75-
Self::VALUE,
76-
)))
77-
}
78-
}
79-
}
80-
8130
/// Phantom Lighthouse program ID - security program injected by Phantom wallet on mainnet
8231
/// See: <https://github.com/coinbase/x402/issues/828>
8332
pub static PHANTOM_LIGHTHOUSE_PROGRAM: LazyLock<Pubkey> = LazyLock::new(|| {

r402/src/proto/mod.rs

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
//! All types serialize to JSON using camelCase field names. The protocol version
1818
//! is indicated by the `x402Version` field in payment payloads.
1919
20-
use serde::{Deserialize, Deserializer, Serialize, Serializer};
20+
use serde::{Deserialize, Serialize};
2121
use serde_with::{VecSkipError, serde_as};
2222
use std::collections::HashMap;
2323
use std::str::FromStr;
@@ -103,8 +103,9 @@ pub type Extensions = HashMap<String, serde_json::Value>;
103103
/// Some JSON parsers (particularly in `JavaScript`) cannot accurately represent
104104
/// large integers. This type serializes `u64` values as strings to preserve
105105
/// precision across all platforms.
106-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
107-
pub struct U64String(u64);
106+
#[serde_as]
107+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
108+
pub struct U64String(#[serde_as(as = "serde_with::DisplayFromStr")] u64);
108109

109110
impl U64String {
110111
/// Returns the inner `u64` value.
@@ -134,25 +135,6 @@ impl From<U64String> for u64 {
134135
}
135136
}
136137

137-
impl Serialize for U64String {
138-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139-
where
140-
S: Serializer,
141-
{
142-
serializer.serialize_str(&self.0.to_string())
143-
}
144-
}
145-
146-
impl<'de> Deserialize<'de> for U64String {
147-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
148-
where
149-
D: Deserializer<'de>,
150-
{
151-
let s = String::deserialize(deserializer)?;
152-
s.parse::<u64>().map(Self).map_err(serde::de::Error::custom)
153-
}
154-
}
155-
156138
/// Describes a payment method supported by a facilitator.
157139
///
158140
/// This type is returned in the [`SupportedResponse`] to indicate what
@@ -246,8 +228,7 @@ impl SettleRequest {
246228
/// Delegates to the same logic as [`VerifyRequest::scheme_slug`].
247229
#[must_use]
248230
pub fn scheme_slug(&self) -> Option<SchemeSlug> {
249-
let tmp = VerifyRequest(self.0.clone());
250-
tmp.scheme_slug()
231+
scheme_slug_from_json(&self.0)
251232
}
252233
}
253234

@@ -284,26 +265,23 @@ impl VerifyRequest {
284265
/// Returns `None` if the request format is invalid or the scheme is unknown.
285266
#[must_use]
286267
pub fn scheme_slug(&self) -> Option<SchemeSlug> {
287-
let x402_version: u8 = self.0.get("x402Version")?.as_u64()?.try_into().ok()?;
288-
if x402_version != v2::X402Version2::VALUE {
289-
return None;
290-
}
291-
let chain_id_string = self
292-
.0
293-
.get("paymentPayload")?
294-
.get("accepted")?
295-
.get("network")?
296-
.as_str()?;
297-
let chain_id = ChainId::from_str(chain_id_string).ok()?;
298-
let scheme = self
299-
.0
300-
.get("paymentPayload")?
301-
.get("accepted")?
302-
.get("scheme")?
303-
.as_str()?;
304-
let slug = SchemeSlug::new(chain_id, scheme.into());
305-
Some(slug)
268+
scheme_slug_from_json(&self.0)
269+
}
270+
}
271+
272+
/// Extracts a [`SchemeSlug`] from a raw verify/settle JSON value.
273+
///
274+
/// Navigates `x402Version`, `paymentPayload.accepted.network`, and
275+
/// `paymentPayload.accepted.scheme` without cloning the JSON tree.
276+
fn scheme_slug_from_json(json: &serde_json::Value) -> Option<SchemeSlug> {
277+
let x402_version: u8 = json.get("x402Version")?.as_u64()?.try_into().ok()?;
278+
if x402_version != v2::X402Version2::VALUE {
279+
return None;
306280
}
281+
let accepted = json.get("paymentPayload")?.get("accepted")?;
282+
let chain_id = ChainId::from_str(accepted.get("network")?.as_str()?).ok()?;
283+
let scheme = accepted.get("scheme")?.as_str()?;
284+
Some(SchemeSlug::new(chain_id, scheme.into()))
307285
}
308286

309287
/// Result returned by a facilitator after verifying a payment payload

0 commit comments

Comments
 (0)