This commit is contained in:
2026-04-21 09:16:54 +02:00
parent ff3b20f13b
commit dcee5c9447
6 changed files with 176 additions and 81 deletions

View File

@@ -8,3 +8,4 @@
0.3.0 - Registre subscriptions / notifications 0.3.0 - Registre subscriptions / notifications
0.3.1 - Ajout de subscribe/unsubscribe hlpers à WsClient 0.3.1 - Ajout de subscribe/unsubscribe hlpers à WsClient
0.3.2 - Ajout de notifications typés 0.3.2 - Ajout de notifications typés
0.3.3 - Ajout du suffix _raw au fonction raw pour avoir des fonction subscribe _typed et _raw

View File

@@ -8,7 +8,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.3.2" version = "0.3.3"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot" repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"

View File

@@ -23,10 +23,8 @@ fs2.workspace = true
kb_lib = { path = "../kb_lib" } kb_lib = { path = "../kb_lib" }
rustls.workspace = true rustls.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true
tauri.workspace = true tauri.workspace = true
tauri-plugin-tracing.workspace = true tauri-plugin-tracing.workspace = true
tokio.workspace = true tokio.workspace = true
tracing.workspace = true tracing.workspace = true
ts-rs.workspace = true ts-rs.workspace = true
uuid.workspace = true

View File

@@ -9,39 +9,21 @@ authors.workspace = true
publish.workspace = true publish.workspace = true
[dependencies] [dependencies]
async-trait.workspace = true
base64.workspace = true
chrono.workspace = true
futures-util.workspace = true futures-util.workspace = true
reqwest.workspace = true reqwest.workspace = true
rustls.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
solana-account-decoder-client-types.workspace = true
solana-address-lookup-table-interface.workspace = true
solana-client.workspace = true
solana-compute-budget-interface.workspace = true
solana-rpc-client-api.workspace = true solana-rpc-client-api.workspace = true
solana-rpc-client-types.workspace = true
solana-sdk.workspace = true solana-sdk.workspace = true
solana-sdk-ids.workspace = true solana-sdk-ids.workspace = true
solana-system-interface.workspace = true
solana-transaction-status-client-types.workspace = true
spl-associated-token-account-interface.workspace = true spl-associated-token-account-interface.workspace = true
spl-memo-interface.workspace = true
spl-token-2022-interface.workspace = true spl-token-2022-interface.workspace = true
spl-token-interface.workspace = true spl-token-interface.workspace = true
sqlx.workspace = true
tokio.workspace = true tokio.workspace = true
tokio-stream.workspace = true
tokio-tungstenite.workspace = true tokio-tungstenite.workspace = true
tracing.workspace = true tracing.workspace = true
tracing-appender.workspace = true tracing-appender.workspace = true
tracing-subscriber.workspace = true tracing-subscriber.workspace = true
yellowstone-grpc-client.workspace = true
yellowstone-grpc-proto.workspace = true
uuid.workspace = true
[dev-dependencies] [dev-dependencies]
tempfile.workspace = true

View File

@@ -204,7 +204,7 @@ impl crate::WsClient {
Ok(config_value) => config_value, Ok(config_value) => config_value,
Err(error) => return Err(error), Err(error) => return Err(error),
}; };
self.account_subscribe(pubkey, config_value).await self.account_subscribe_raw(pubkey, config_value).await
} }
/// Typed helper for `blockSubscribe`. /// Typed helper for `blockSubscribe`.
@@ -224,7 +224,7 @@ impl crate::WsClient {
Ok(config_value) => config_value, Ok(config_value) => config_value,
Err(error) => return Err(error), Err(error) => return Err(error),
}; };
self.block_subscribe(filter_value, config_value).await self.block_subscribe_raw(filter_value, config_value).await
} }
/// Typed helper for `logsSubscribe`. /// Typed helper for `logsSubscribe`.
@@ -244,7 +244,7 @@ impl crate::WsClient {
Ok(config_value) => config_value, Ok(config_value) => config_value,
Err(error) => return Err(error), Err(error) => return Err(error),
}; };
self.logs_subscribe(filter_value, config_value).await self.logs_subscribe_raw(filter_value, config_value).await
} }
/// Typed helper for `programSubscribe`. /// Typed helper for `programSubscribe`.
@@ -259,7 +259,7 @@ impl crate::WsClient {
Ok(config_value) => config_value, Ok(config_value) => config_value,
Err(error) => return Err(error), Err(error) => return Err(error),
}; };
self.program_subscribe(program_id, config_value).await self.program_subscribe_raw(program_id, config_value).await
} }
/// Typed helper for `signatureSubscribe`. /// Typed helper for `signatureSubscribe`.
@@ -274,7 +274,7 @@ impl crate::WsClient {
Ok(config_value) => config_value, Ok(config_value) => config_value,
Err(error) => return Err(error), Err(error) => return Err(error),
}; };
self.signature_subscribe(signature, config_value).await self.signature_subscribe_raw(signature, config_value).await
} }
} }

View File

@@ -557,8 +557,8 @@ impl WsClient {
} }
} }
/// Subscribes to account change notifications. /// Subscribes to account change notifications using raw JSON values.
pub async fn account_subscribe( pub async fn account_subscribe_raw(
&self, &self,
pubkey: std::string::String, pubkey: std::string::String,
config: std::option::Option<serde_json::Value>, config: std::option::Option<serde_json::Value>,
@@ -568,8 +568,11 @@ impl WsClient {
.await .await
} }
/// Unsubscribes from an account change subscription. /// Unsubscribes from an account change subscription using raw JSON values.
pub async fn account_unsubscribe(&self, subscription_id: u64) -> Result<u64, crate::KbError> { pub async fn account_unsubscribe_raw(
&self,
subscription_id: u64,
) -> Result<u64, crate::KbError> {
self.send_json_rpc_request( self.send_json_rpc_request(
"accountUnsubscribe".to_string(), "accountUnsubscribe".to_string(),
vec![serde_json::Value::from(subscription_id)], vec![serde_json::Value::from(subscription_id)],
@@ -577,10 +580,10 @@ impl WsClient {
.await .await
} }
/// Subscribes to block notifications. /// Subscribes to block notifications using raw JSON values.
/// ///
/// The Solana RPC documentation marks this subscription as unstable. /// The Solana RPC documentation marks this subscription as unstable.
pub async fn block_subscribe( pub async fn block_subscribe_raw(
&self, &self,
filter: serde_json::Value, filter: serde_json::Value,
config: std::option::Option<serde_json::Value>, config: std::option::Option<serde_json::Value>,
@@ -590,8 +593,8 @@ impl WsClient {
.await .await
} }
/// Unsubscribes from a block notification subscription. /// Unsubscribes from a block notification subscription using raw JSON values.
pub async fn block_unsubscribe(&self, subscription_id: u64) -> Result<u64, crate::KbError> { pub async fn block_unsubscribe_raw(&self, subscription_id: u64) -> Result<u64, crate::KbError> {
self.send_json_rpc_request( self.send_json_rpc_request(
"blockUnsubscribe".to_string(), "blockUnsubscribe".to_string(),
vec![serde_json::Value::from(subscription_id)], vec![serde_json::Value::from(subscription_id)],
@@ -599,8 +602,8 @@ impl WsClient {
.await .await
} }
/// Subscribes to transaction log notifications. /// Subscribes to transaction log notifications using raw JSON values.
pub async fn logs_subscribe( pub async fn logs_subscribe_raw(
&self, &self,
filter: serde_json::Value, filter: serde_json::Value,
config: std::option::Option<serde_json::Value>, config: std::option::Option<serde_json::Value>,
@@ -610,8 +613,8 @@ impl WsClient {
.await .await
} }
/// Unsubscribes from a logs subscription. /// Unsubscribes from a logs subscription using raw JSON values.
pub async fn logs_unsubscribe(&self, subscription_id: u64) -> Result<u64, crate::KbError> { pub async fn logs_unsubscribe_raw(&self, subscription_id: u64) -> Result<u64, crate::KbError> {
self.send_json_rpc_request( self.send_json_rpc_request(
"logsUnsubscribe".to_string(), "logsUnsubscribe".to_string(),
vec![serde_json::Value::from(subscription_id)], vec![serde_json::Value::from(subscription_id)],
@@ -619,8 +622,8 @@ impl WsClient {
.await .await
} }
/// Subscribes to program-owned account notifications. /// Subscribes to program-owned account notifications using raw JSON values.
pub async fn program_subscribe( pub async fn program_subscribe_raw(
&self, &self,
program_id: std::string::String, program_id: std::string::String,
config: std::option::Option<serde_json::Value>, config: std::option::Option<serde_json::Value>,
@@ -630,8 +633,11 @@ impl WsClient {
.await .await
} }
/// Unsubscribes from a program subscription. /// Unsubscribes from a program subscription using raw JSON values.
pub async fn program_unsubscribe(&self, subscription_id: u64) -> Result<u64, crate::KbError> { pub async fn program_unsubscribe_raw(
&self,
subscription_id: u64,
) -> Result<u64, crate::KbError> {
self.send_json_rpc_request( self.send_json_rpc_request(
"programUnsubscribe".to_string(), "programUnsubscribe".to_string(),
vec![serde_json::Value::from(subscription_id)], vec![serde_json::Value::from(subscription_id)],
@@ -654,8 +660,8 @@ impl WsClient {
.await .await
} }
/// Subscribes to one transaction signature status. /// Subscribes to one transaction signature status using raw JSON values.
pub async fn signature_subscribe( pub async fn signature_subscribe_raw(
&self, &self,
signature: std::string::String, signature: std::string::String,
config: std::option::Option<serde_json::Value>, config: std::option::Option<serde_json::Value>,
@@ -665,8 +671,11 @@ impl WsClient {
.await .await
} }
/// Unsubscribes from a signature subscription. /// Unsubscribes from a signature subscription using raw JSON values.
pub async fn signature_unsubscribe(&self, subscription_id: u64) -> Result<u64, crate::KbError> { pub async fn signature_unsubscribe_raw(
&self,
subscription_id: u64,
) -> Result<u64, crate::KbError> {
self.send_json_rpc_request( self.send_json_rpc_request(
"signatureUnsubscribe".to_string(), "signatureUnsubscribe".to_string(),
vec![serde_json::Value::from(subscription_id)], vec![serde_json::Value::from(subscription_id)],
@@ -1924,7 +1933,7 @@ mod tests {
client.disconnect().await.expect("disconnect must succeed"); client.disconnect().await.expect("disconnect must succeed");
server.shutdown().await; server.shutdown().await;
} }
#[tokio::test] #[tokio::test]
async fn helper_methods_send_expected_subscribe_method_names() { async fn helper_methods_send_expected_subscribe_method_names() {
let server = TestWsServer::spawn_json_rpc_server().await; let server = TestWsServer::spawn_json_rpc_server().await;
@@ -1934,59 +1943,104 @@ mod tests {
client.connect().await.expect("connect must succeed"); client.connect().await.expect("connect must succeed");
let _ = recv_event(&mut receiver).await; let _ = recv_event(&mut receiver).await;
let _ = client let _ = client
.account_subscribe( .account_subscribe_raw(
"CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12".to_string(), "CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12".to_string(),
Some(serde_json::json!({"encoding": "jsonParsed"})), Some(serde_json::json!({"encoding": "jsonParsed"})),
) )
.await .await
.expect("account_subscribe must succeed"); .expect("account_subscribe must succeed");
let _ = client let _ = client
.block_subscribe( .block_subscribe_raw(
serde_json::json!("all"), serde_json::json!("all"),
Some(serde_json::json!({"commitment": "confirmed"})), Some(serde_json::json!({"commitment": "confirmed"})),
) )
.await .await
.expect("block_subscribe must succeed"); .expect("block_subscribe must succeed");
let _ = client let _ = client
.logs_subscribe( .logs_subscribe_raw(
serde_json::json!("all"), serde_json::json!("all"),
Some(serde_json::json!({"commitment": "finalized"})), Some(serde_json::json!({"commitment": "finalized"})),
) )
.await .await
.expect("logs_subscribe must succeed"); .expect("logs_subscribe must succeed");
let _ = client let _ = client
.program_subscribe( .program_subscribe_raw(
"11111111111111111111111111111111".to_string(), "11111111111111111111111111111111".to_string(),
Some(serde_json::json!({"encoding": "base64"})), Some(serde_json::json!({"encoding": "base64"})),
) )
.await .await
.expect("program_subscribe must succeed"); .expect("program_subscribe must succeed");
let _ = client.root_subscribe().await.expect("root_subscribe must succeed");
let _ = client let _ = client
.signature_subscribe( .root_subscribe()
.await
.expect("root_subscribe must succeed");
let _ = client
.signature_subscribe_raw(
"2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b".to_string(), "2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b".to_string(),
Some(serde_json::json!({"commitment": "confirmed"})), Some(serde_json::json!({"commitment": "confirmed"})),
) )
.await .await
.expect("signature_subscribe must succeed"); .expect("signature_subscribe must succeed");
let _ = client.slot_subscribe().await.expect("slot_subscribe must succeed"); let _ = client
.slot_subscribe()
.await
.expect("slot_subscribe must succeed");
let _ = client let _ = client
.slots_updates_subscribe() .slots_updates_subscribe()
.await .await
.expect("slots_updates_subscribe must succeed"); .expect("slots_updates_subscribe must succeed");
let _ = client.vote_subscribe().await.expect("vote_subscribe must succeed"); let _ = client
.vote_subscribe()
.await
.expect("vote_subscribe must succeed");
tokio::time::sleep(std::time::Duration::from_millis(150)).await; tokio::time::sleep(std::time::Duration::from_millis(150)).await;
let observed_methods = server.observed_methods_snapshot().await; let observed_methods = server.observed_methods_snapshot().await;
assert!(observed_methods.iter().any(|method| method == "accountSubscribe")); assert!(
assert!(observed_methods.iter().any(|method| method == "blockSubscribe")); observed_methods
assert!(observed_methods.iter().any(|method| method == "logsSubscribe")); .iter()
assert!(observed_methods.iter().any(|method| method == "programSubscribe")); .any(|method| method == "accountSubscribe")
assert!(observed_methods.iter().any(|method| method == "rootSubscribe")); );
assert!(observed_methods.iter().any(|method| method == "signatureSubscribe")); assert!(
assert!(observed_methods.iter().any(|method| method == "slotSubscribe")); observed_methods
assert!(observed_methods.iter().any(|method| method == "slotsUpdatesSubscribe")); .iter()
assert!(observed_methods.iter().any(|method| method == "voteSubscribe")); .any(|method| method == "blockSubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "logsSubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "programSubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "rootSubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "signatureSubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "slotSubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "slotsUpdatesSubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "voteSubscribe")
);
client.disconnect().await.expect("disconnect must succeed"); client.disconnect().await.expect("disconnect must succeed");
server.shutdown().await; server.shutdown().await;
} }
@@ -1999,29 +2053,89 @@ mod tests {
let mut receiver = client.subscribe_events(); let mut receiver = client.subscribe_events();
client.connect().await.expect("connect must succeed"); client.connect().await.expect("connect must succeed");
let _ = recv_event(&mut receiver).await; let _ = recv_event(&mut receiver).await;
let _ = client.account_unsubscribe(10).await.expect("account_unsubscribe must succeed"); let _ = client
let _ = client.block_unsubscribe(11).await.expect("block_unsubscribe must succeed"); .account_unsubscribe_raw(10)
let _ = client.logs_unsubscribe(12).await.expect("logs_unsubscribe must succeed"); .await
let _ = client.program_unsubscribe(13).await.expect("program_unsubscribe must succeed"); .expect("account_unsubscribe must succeed");
let _ = client.root_unsubscribe(14).await.expect("root_unsubscribe must succeed"); let _ = client
let _ = client.signature_unsubscribe(15).await.expect("signature_unsubscribe must succeed"); .block_unsubscribe_raw(11)
let _ = client.slot_unsubscribe(16).await.expect("slot_unsubscribe must succeed"); .await
.expect("block_unsubscribe must succeed");
let _ = client
.logs_unsubscribe_raw(12)
.await
.expect("logs_unsubscribe must succeed");
let _ = client
.program_unsubscribe_raw(13)
.await
.expect("program_unsubscribe must succeed");
let _ = client
.root_unsubscribe(14)
.await
.expect("root_unsubscribe must succeed");
let _ = client
.signature_unsubscribe_raw(15)
.await
.expect("signature_unsubscribe must succeed");
let _ = client
.slot_unsubscribe(16)
.await
.expect("slot_unsubscribe must succeed");
let _ = client let _ = client
.slots_updates_unsubscribe(17) .slots_updates_unsubscribe(17)
.await .await
.expect("slots_updates_unsubscribe must succeed"); .expect("slots_updates_unsubscribe must succeed");
let _ = client.vote_unsubscribe(18).await.expect("vote_unsubscribe must succeed"); let _ = client
.vote_unsubscribe(18)
.await
.expect("vote_unsubscribe must succeed");
tokio::time::sleep(std::time::Duration::from_millis(150)).await; tokio::time::sleep(std::time::Duration::from_millis(150)).await;
let observed_methods = server.observed_methods_snapshot().await; let observed_methods = server.observed_methods_snapshot().await;
assert!(observed_methods.iter().any(|method| method == "accountUnsubscribe")); assert!(
assert!(observed_methods.iter().any(|method| method == "blockUnsubscribe")); observed_methods
assert!(observed_methods.iter().any(|method| method == "logsUnsubscribe")); .iter()
assert!(observed_methods.iter().any(|method| method == "programUnsubscribe")); .any(|method| method == "accountUnsubscribe")
assert!(observed_methods.iter().any(|method| method == "rootUnsubscribe")); );
assert!(observed_methods.iter().any(|method| method == "signatureUnsubscribe")); assert!(
assert!(observed_methods.iter().any(|method| method == "slotUnsubscribe")); observed_methods
assert!(observed_methods.iter().any(|method| method == "slotsUpdatesUnsubscribe")); .iter()
assert!(observed_methods.iter().any(|method| method == "voteUnsubscribe")); .any(|method| method == "blockUnsubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "logsUnsubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "programUnsubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "rootUnsubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "signatureUnsubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "slotUnsubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "slotsUpdatesUnsubscribe")
);
assert!(
observed_methods
.iter()
.any(|method| method == "voteUnsubscribe")
);
client.disconnect().await.expect("disconnect must succeed"); client.disconnect().await.expect("disconnect must succeed");
server.shutdown().await; server.shutdown().await;
} }