-
Notifications
You must be signed in to change notification settings - Fork 0
Expand crate docs, add examples, and LLM-friendly docs #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
b24620c
ec27e4b
b6e4c88
707076a
a7e157d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,10 +9,14 @@ homepage = "https://github.com/MinaProtocol/mina-sdk-rust" | |
| documentation = "https://docs.rs/mina-sdk" | ||
| readme = "README.md" | ||
| authors = ["Mina Protocol <[email protected]>"] | ||
| keywords = ["mina", "blockchain", "graphql", "cryptocurrency"] | ||
| categories = ["api-bindings"] | ||
| keywords = ["mina", "blockchain", "graphql", "cryptocurrency", "web3"] | ||
| categories = ["api-bindings", "cryptography::cryptocurrencies"] | ||
| exclude = [".github/", "scripts/", "schema/", "tests/integration_tests.rs"] | ||
|
|
||
| [package.metadata.docs.rs] | ||
| all-features = true | ||
| rustdoc-args = ["--cfg", "docsrs"] | ||
|
|
||
| [dependencies] | ||
| reqwest = { version = "0.12", features = ["json"] } | ||
| serde = { version = "1", features = ["derive"] } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| //! Working with Mina currency amounts. | ||
| //! | ||
| //! Run with: cargo run --example currency_operations | ||
| //! | ||
| //! This example demonstrates Currency creation, conversion, arithmetic, | ||
| //! and comparison — no running node required. | ||
|
|
||
| use mina_sdk::Currency; | ||
|
|
||
| fn main() -> mina_sdk::Result<()> { | ||
| // ---- Creation ---- | ||
|
|
||
| // From a human-readable MINA string | ||
| let one_mina = Currency::from_mina("1.0")?; | ||
|
|
||
| // From nanomina (1 MINA = 1,000,000,000 nanomina) | ||
| let also_one_mina = Currency::from_nanomina(1_000_000_000); | ||
|
|
||
| // From a GraphQL response value (nanomina as string) | ||
| let from_graphql = Currency::from_graphql("1000000000")?; | ||
|
|
||
| assert_eq!(one_mina, also_one_mina); | ||
| assert_eq!(also_one_mina, from_graphql); | ||
|
|
||
| // ---- Display & conversion ---- | ||
|
|
||
| let amount = Currency::from_mina("42.5")?; | ||
| println!("Display: {amount}"); // 42.500000000 | ||
| println!("As MINA: {}", amount.mina()); // 42.500000000 | ||
| println!("As nanomina: {}", amount.nanomina()); // 42500000000 | ||
| println!("For GraphQL: {}", amount.to_nanomina_str()); // 42500000000 | ||
|
|
||
| // ---- Arithmetic ---- | ||
|
|
||
| let a = Currency::from_mina("10.0")?; | ||
| let b = Currency::from_mina("3.5")?; | ||
|
|
||
| // Addition | ||
| let sum = a + b; | ||
| println!("{a} + {b} = {sum}"); | ||
|
|
||
| // Subtraction | ||
| let diff = a - b; | ||
| println!("{a} - {b} = {diff}"); | ||
|
|
||
| // Checked subtraction (returns Error instead of panicking) | ||
| let small = Currency::from_mina("1.0")?; | ||
| let large = Currency::from_mina("999.0")?; | ||
| match small.checked_sub(large) { | ||
| Ok(result) => println!("Result: {result}"), | ||
| Err(e) => println!("Expected underflow: {e}"), | ||
| } | ||
|
|
||
| // Multiplication by scalar | ||
| let fee = Currency::from_mina("0.01")?; | ||
| let ten_fees = fee * 10; | ||
| println!("10 x {fee} = {ten_fees}"); | ||
|
|
||
| // Also works in reverse | ||
| let same = 10_u64 * fee; | ||
| assert_eq!(ten_fees, same); | ||
|
|
||
| // Checked overflow-safe variants | ||
| assert!(fee.checked_add(a).is_some()); | ||
| assert!(fee.checked_mul(100).is_some()); | ||
|
|
||
| // ---- Comparison ---- | ||
|
|
||
| let low = Currency::from_mina("0.5")?; | ||
| let high = Currency::from_mina("100.0")?; | ||
| assert!(low < high); | ||
| assert!(high > low); | ||
|
|
||
| // Can be used in collections | ||
| use std::collections::BTreeSet; | ||
| let mut amounts = BTreeSet::new(); | ||
| amounts.insert(Currency::from_mina("3.0")?); | ||
| amounts.insert(Currency::from_mina("1.0")?); | ||
| amounts.insert(Currency::from_mina("2.0")?); | ||
| println!( | ||
| "Sorted: {:?}", | ||
| amounts.iter().map(|c| c.mina()).collect::<Vec<_>>() | ||
| ); | ||
|
|
||
| // ---- Smallest unit ---- | ||
|
|
||
| let one_nanomina = Currency::from_nanomina(1); | ||
| println!("Smallest unit: {one_nanomina}"); // 0.000000001 | ||
|
|
||
| Ok(()) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| //! Execute a custom GraphQL query against the Mina daemon. | ||
| //! | ||
| //! Run with: cargo run --example custom_query | ||
| //! | ||
| //! The SDK exposes `execute_query()` for running arbitrary GraphQL. | ||
| //! This is useful for queries not covered by the typed API methods. | ||
|
|
||
| use mina_sdk::MinaClient; | ||
| use serde_json::json; | ||
|
|
||
| #[tokio::main] | ||
| async fn main() -> mina_sdk::Result<()> { | ||
| let client = MinaClient::new("http://127.0.0.1:3085/graphql"); | ||
|
|
||
| // Example 1: Query the node's version | ||
| let data = client | ||
| .execute_query(r#"query { version }"#, None, "get_version") | ||
| .await?; | ||
| println!("Node version: {}", data["version"]); | ||
|
|
||
| // Example 2: Query with variables | ||
| let query = r#"query ($maxLength: Int) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this query be in sdk? That's the very purpose of this project to encapsulate those graphql calls
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed — |
||
| bestChain(maxLength: $maxLength) { | ||
| stateHash | ||
| protocolState { | ||
| consensusState { | ||
| blockHeight | ||
| epoch | ||
| slot | ||
| } | ||
| } | ||
| } | ||
| }"#; | ||
|
|
||
| let data = client | ||
| .execute_query(query, Some(json!({ "maxLength": 3 })), "best_chain_custom") | ||
| .await?; | ||
|
|
||
| if let Some(chain) = data["bestChain"].as_array() { | ||
| for block in chain { | ||
| let consensus = &block["protocolState"]["consensusState"]; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not produce nice wrapper around block? and consensus too , user should not be force to use response as array
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolved by dropping the raw |
||
| println!( | ||
| "Block {} (epoch {}, slot {}): {}", | ||
| consensus["blockHeight"], | ||
| consensus["epoch"], | ||
| consensus["slot"], | ||
| &block["stateHash"].as_str().unwrap_or("?")[..20], | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // Example 3: The built-in query constants are also available | ||
| let data = client | ||
| .execute_query(mina_sdk::queries::DAEMON_STATUS, None, "daemon_status") | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's good to have execute_query but all others parameters should be names . maybe builder type (I already forgot what argument None is referring to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a |
||
| .await?; | ||
| println!("Raw daemon status JSON: {}", data["daemonStatus"]); | ||
|
|
||
| Ok(()) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| //! Handling errors from the Mina SDK. | ||
| //! | ||
| //! Run with: cargo run --example error_handling | ||
| //! | ||
| //! Demonstrates matching on specific error variants for robust applications. | ||
|
|
||
| use mina_sdk::{Currency, Error, MinaClient, Payment}; | ||
|
|
||
| #[tokio::main] | ||
| async fn main() { | ||
| let client = MinaClient::new("http://127.0.0.1:3085/graphql"); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe let's introduce default() for this and it will automatically connect to this url ? also some helper method like from URI or inet or host_and_port() erc
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added |
||
|
|
||
| // ---- AccountNotFound ---- | ||
| match client.get_account("B62qNONEXISTENT", None).await { | ||
| Ok(account) => println!("Balance: {}", account.balance.total), | ||
| Err(Error::AccountNotFound(key)) => { | ||
| println!("Account {key} does not exist on chain") | ||
| } | ||
| Err(e) => eprintln!("Unexpected error: {e}"), | ||
| } | ||
|
|
||
| // ---- Connection errors (node not reachable) ---- | ||
| let bad_client = MinaClient::new("http://127.0.0.1:9999/graphql"); | ||
| match bad_client.get_sync_status().await { | ||
| Ok(status) => println!("Status: {status}"), | ||
| Err(Error::Connection { attempts, .. }) => { | ||
| eprintln!("Could not reach node after {attempts} attempts") | ||
| } | ||
| Err(e) => eprintln!("Other error: {e}"), | ||
| } | ||
|
|
||
| // ---- GraphQL errors (e.g., invalid mutation input) ---- | ||
| match client | ||
| .send_payment( | ||
| Payment::sender("INVALID_KEY") | ||
| .to("INVALID_KEY") | ||
| .amount(Currency::from_nanomina(0)) | ||
| .fee(Currency::from_nanomina(0)), | ||
| ) | ||
| .await | ||
| { | ||
| Ok(r) => println!("Unexpected success: {}", r.hash), | ||
| Err(Error::Graphql { messages, .. }) => { | ||
| eprintln!("GraphQL rejected the request: {messages}") | ||
| } | ||
| Err(e) => eprintln!("Other error: {e}"), | ||
| } | ||
|
|
||
| // ---- Currency validation errors (no node needed) ---- | ||
| match Currency::from_mina("not_a_number") { | ||
| Ok(_) => println!("Unexpected success"), | ||
| Err(Error::InvalidCurrency(input)) => { | ||
| eprintln!("Bad currency input: {input}") | ||
| } | ||
| Err(e) => eprintln!("Other: {e}"), | ||
| } | ||
|
|
||
| // ---- Currency underflow ---- | ||
| let small = Currency::from_mina("1.0").unwrap(); | ||
| let large = Currency::from_mina("999.0").unwrap(); | ||
| match small.checked_sub(large) { | ||
| Ok(result) => println!("Result: {result}"), | ||
| Err(Error::CurrencyUnderflow(a, b)) => { | ||
| eprintln!("Cannot subtract {b} from {a} (would go negative)") | ||
| } | ||
| Err(e) => eprintln!("Other: {e}"), | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| //! Monitor a Mina node — sync status, peers, and recent blocks. | ||
| //! | ||
| //! Run with: cargo run --example node_monitoring | ||
| //! | ||
| //! Demonstrates using the SDK for node observability and health checks. | ||
|
|
||
| use mina_sdk::{MinaClient, SyncStatus}; | ||
|
|
||
| #[tokio::main] | ||
| async fn main() -> mina_sdk::Result<()> { | ||
| let client = MinaClient::new("http://127.0.0.1:3085/graphql"); | ||
|
|
||
| // ---- Health check ---- | ||
| let sync = client.get_sync_status().await?; | ||
| match sync { | ||
| SyncStatus::Synced => println!("[OK] Node is synced"), | ||
| SyncStatus::Bootstrap => println!("[WARN] Node is bootstrapping"), | ||
| SyncStatus::Catchup => println!("[WARN] Node is catching up"), | ||
| SyncStatus::Connecting => println!("[WARN] Node is connecting to network"), | ||
| SyncStatus::Listening => println!("[WARN] Node is listening (not yet syncing)"), | ||
| SyncStatus::Offline => println!("[ERR] Node is offline"), | ||
| } | ||
|
|
||
| // ---- Daemon status ---- | ||
| let status = client.get_daemon_status().await?; | ||
| println!("\nDaemon Status:"); | ||
| println!(" Blockchain length: {:?}", status.blockchain_length); | ||
| println!( | ||
| " Highest block received: {:?}", | ||
| status.highest_block_length_received | ||
| ); | ||
| if let Some(secs) = status.uptime_secs { | ||
| println!(" Uptime: {}h {}m", secs / 3600, (secs % 3600) / 60); | ||
| } | ||
| if let Some(hash) = &status.state_hash { | ||
| println!(" State hash: {}", &hash[..20.min(hash.len())]); | ||
| } | ||
|
|
||
| // ---- Network info ---- | ||
| let network_id = client.get_network_id().await?; | ||
| println!("\nNetwork: {network_id}"); | ||
|
|
||
| // ---- Peers ---- | ||
| let peers = client.get_peers().await?; | ||
| println!("\nConnected peers: {}", peers.len()); | ||
| for (i, peer) in peers.iter().enumerate().take(5) { | ||
| println!( | ||
| " {}: {}:{} ({})", | ||
| i + 1, | ||
| peer.host, | ||
| peer.port, | ||
| peer.peer_id | ||
| ); | ||
| } | ||
| if peers.len() > 5 { | ||
| println!(" ... and {} more", peers.len() - 5); | ||
| } | ||
|
|
||
| // ---- Recent blocks ---- | ||
| let blocks = client.get_best_chain(Some(5)).await?; | ||
| println!("\nRecent blocks:"); | ||
| for block in &blocks { | ||
| println!( | ||
| " Height {} | {} txns | {}", | ||
| block.height, | ||
| block.command_transaction_count, | ||
| &block.state_hash[..16], | ||
| ); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove that nesting also show example format of this key
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flattened to a
let-elseearly return and added a realB62q…key in the comment so readers see the format. Fixed in a7e157d.