# Circle SDK for Rust Circle SDK for Rust is a comprehensive, typed, async SDK for Circle's Web3 Services (W3S) API platform. It provides full API coverage for User-Controlled Wallets, Developer-Controlled Wallets, Compliance Engine, and Modular Buidl Wallets, enabling Rust developers to interact with blockchain wallets, execute transactions, screen addresses for compliance, and manage ERC-4337 smart contract accounts. The SDK follows idiomatic Rust patterns with strong type safety, proper error handling via `Result` types, and async/await support using `tokio`. Each API family is exposed as a separate crate (`circle-user-controlled-wallets`, `circle-developer-controlled-wallets`, `circle-compliance`, `circle-buidl-wallets`) plus a command-line interface (`circle-cli`) for direct API interaction without writing code. ## Installation Add the desired crates to your `Cargo.toml`: ```toml [dependencies] circle-user-controlled-wallets = "0.1.0" circle-developer-controlled-wallets = "0.1.0" circle-compliance = "0.1.0" circle-buidl-wallets = "0.1.0" ``` --- ## User-Controlled Wallets API ### Create User Register a new end-user in the Circle system. The user ID is application-defined and used to identify users across API calls. ```rust use circle_user_controlled_wallets::{UserWalletsClient, models::user::CreateUserRequest}; #[tokio::main] async fn main() -> Result<(), Box> { let client = UserWalletsClient::new("YOUR_API_KEY"); let req = CreateUserRequest { user_id: "my-app-user-001".to_string(), }; let response = client.create_user(&req).await?; println!("Created user: {:?}", response.data.id); // Output: Created user: Some("my-app-user-001") Ok(()) } ``` ### Get User Token Obtain a short-lived JWT token for user-scoped operations. This token is required for wallet and transaction operations and expires after approximately 1 hour. ```rust use circle_user_controlled_wallets::{UserWalletsClient, models::user::GetUserTokenRequest}; #[tokio::main] async fn main() -> Result<(), Box> { let client = UserWalletsClient::new("YOUR_API_KEY"); let req = GetUserTokenRequest { user_id: "my-app-user-001".to_string(), }; let response = client.get_user_token(&req).await?; println!("User token: {}", response.data.user_token); println!("Encryption key: {:?}", response.data.encryption_key); // Output: User token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... Ok(()) } ``` ### List Users List all registered end-users with optional filtering by PIN status and pagination support. ```rust use circle_user_controlled_wallets::{UserWalletsClient, models::user::{ListUsersParams, PinStatus}}; use circle_user_controlled_wallets::models::common::PageParams; #[tokio::main] async fn main() -> Result<(), Box> { let client = UserWalletsClient::new("YOUR_API_KEY"); let params = ListUsersParams { pin_status: Some(PinStatus::Enabled), page: PageParams { page_size: Some(10), ..Default::default() }, }; let response = client.list_users(¶ms).await?; for user in &response.data.users { println!("User ID: {:?}, PIN Status: {:?}", user.id, user.pin_status); } // Output: User ID: Some("user-123"), PIN Status: Some(Enabled) Ok(()) } ``` ### List User Wallets List wallets accessible to an authenticated user. Requires a valid user token obtained from `get_user_token`. ```rust use circle_user_controlled_wallets::{UserWalletsClient, models::wallet::ListWalletsParams}; use circle_user_controlled_wallets::models::common::Blockchain; #[tokio::main] async fn main() -> Result<(), Box> { let client = UserWalletsClient::new("YOUR_API_KEY"); let user_token = "eyJhbGciOiJSUzI1NiIs..."; // From get_user_token let params = ListWalletsParams { blockchain: Some(Blockchain::EthSepolia), ..Default::default() }; let response = client.list_wallets(user_token, ¶ms).await?; for wallet in &response.data.wallets { println!("Wallet: {} on {:?}", wallet.address, wallet.blockchain); } // Output: Wallet: 0x1234...abcd on Some(EthSepolia) Ok(()) } ``` ### Validate Address Check if a blockchain address is valid for a given network before initiating transfers. ```rust use circle_user_controlled_wallets::{UserWalletsClient, models::transaction::ValidateAddressRequest}; use circle_user_controlled_wallets::models::common::Blockchain; #[tokio::main] async fn main() -> Result<(), Box> { let client = UserWalletsClient::new("YOUR_API_KEY"); let req = ValidateAddressRequest { blockchain: Blockchain::EthSepolia, address: "0xab5801a7d398351b8be11c439e05c5b3259aec9b".to_string(), }; let response = client.validate_address(&req).await?; println!("Address valid: {}", response.data.is_valid); // Output: Address valid: true Ok(()) } ``` --- ## Developer-Controlled Wallets API ### Create Wallet Set Create a new wallet set to organize developer-controlled wallets. Requires an entity secret ciphertext for secure key management. ```rust use circle_developer_controlled_wallets::{DeveloperWalletsClient, models::wallet_set::CreateWalletSetRequest}; #[tokio::main] async fn main() -> Result<(), Box> { let client = DeveloperWalletsClient::new("YOUR_API_KEY"); let req = CreateWalletSetRequest { entity_secret_ciphertext: "YOUR_ENCRYPTED_ENTITY_SECRET".to_string(), idempotency_key: uuid::Uuid::new_v4().to_string(), name: Some("Production Wallets".to_string()), }; let response = client.create_wallet_set(&req).await?; println!("Created wallet set: {}", response.data.wallet_set.id); // Output: Created wallet set: f5d71c75-b8e3-4d1e-ae38-e8c3c3c3c3c3 Ok(()) } ``` ### List Wallet Sets Retrieve all wallet sets belonging to the developer entity with pagination support. ```rust use circle_developer_controlled_wallets::{DeveloperWalletsClient, models::wallet_set::ListWalletSetsParams}; #[tokio::main] async fn main() -> Result<(), Box> { let client = DeveloperWalletsClient::new("YOUR_API_KEY"); let params = ListWalletSetsParams::default(); let response = client.list_wallet_sets(¶ms).await?; for wallet_set in &response.data.wallet_sets { println!("Wallet Set: {} - {:?}", wallet_set.id, wallet_set.name); } // Output: Wallet Set: f5d71c75-... - Some("Production Wallets") Ok(()) } ``` ### Create Wallets Create one or more developer-controlled wallets within a wallet set across multiple blockchains. ```rust use circle_developer_controlled_wallets::{DeveloperWalletsClient, models::wallet::CreateWalletsRequest}; use circle_developer_controlled_wallets::models::common::Blockchain; #[tokio::main] async fn main() -> Result<(), Box> { let client = DeveloperWalletsClient::new("YOUR_API_KEY"); let req = CreateWalletsRequest { idempotency_key: uuid::Uuid::new_v4().to_string(), entity_secret_ciphertext: "YOUR_ENCRYPTED_ENTITY_SECRET".to_string(), wallet_set_id: "f5d71c75-b8e3-4d1e-ae38-e8c3c3c3c3c3".to_string(), blockchains: vec![Blockchain::EthSepolia, Blockchain::MaticAmoy], account_type: None, count: Some(2), metadata: None, }; let response = client.create_wallets(&req).await?; for wallet in &response.data.wallets { println!("Created wallet {} on {:?}", wallet.address, wallet.blockchain); } // Output: Created wallet 0xabc123... on EthSepolia Ok(()) } ``` ### List Wallets Query wallets with filtering by blockchain, state, wallet set, and other criteria. ```rust use circle_developer_controlled_wallets::{DeveloperWalletsClient, models::wallet::ListWalletsParams}; use circle_developer_controlled_wallets::models::common::{Blockchain, WalletState}; #[tokio::main] async fn main() -> Result<(), Box> { let client = DeveloperWalletsClient::new("YOUR_API_KEY"); let params = ListWalletsParams { blockchain: Some(Blockchain::EthSepolia), state: Some(WalletState::Live), ..Default::default() }; let response = client.list_wallets(¶ms).await?; for wallet in &response.data.wallets { println!("Wallet: {} ({:?})", wallet.address, wallet.state); } // Output: Wallet: 0x1234... (Some(Live)) Ok(()) } ``` ### Create Transfer Transaction Initiate a token transfer from a developer-controlled wallet to a destination address. ```rust use circle_developer_controlled_wallets::{DeveloperWalletsClient, models::transaction::CreateTransferTxRequest}; use circle_developer_controlled_wallets::models::common::FeeLevel; #[tokio::main] async fn main() -> Result<(), Box> { let client = DeveloperWalletsClient::new("YOUR_API_KEY"); let req = CreateTransferTxRequest { idempotency_key: uuid::Uuid::new_v4().to_string(), entity_secret_ciphertext: "YOUR_ENCRYPTED_ENTITY_SECRET".to_string(), wallet_id: "wallet-uuid-here".to_string(), blockchain: None, token_id: Some("usdc-token-id".to_string()), destination_address: "0xrecipient...".to_string(), amounts: Some(vec!["10.00".to_string()]), nft_token_ids: None, ref_id: Some("order-12345".to_string()), fee_level: Some(FeeLevel::Medium), gas_limit: None, gas_price: None, max_fee: None, priority_fee: None, }; let response = client.create_transfer_transaction(&req).await?; println!("Transaction ID: {}, State: {:?}", response.data.transaction.id, response.data.transaction.state); // Output: Transaction ID: tx-abc123, State: Initiated Ok(()) } ``` ### List Transactions Query transactions with comprehensive filtering options including state, blockchain, and date range. ```rust use circle_developer_controlled_wallets::{DeveloperWalletsClient, models::transaction::{ListTransactionsParams, TransactionState}}; use circle_developer_controlled_wallets::models::common::Blockchain; #[tokio::main] async fn main() -> Result<(), Box> { let client = DeveloperWalletsClient::new("YOUR_API_KEY"); let params = ListTransactionsParams { blockchain: Some(Blockchain::EthSepolia), state: Some(TransactionState::Complete), page_size: Some(10), ..Default::default() }; let response = client.list_transactions(¶ms).await?; for tx in &response.data.transactions { println!("TX: {} - {:?} - Hash: {:?}", tx.id, tx.state, tx.tx_hash); } // Output: TX: tx-123 - Complete - Hash: Some("0xdeadbeef...") Ok(()) } ``` ### Sign Message Sign a plain text or hex-encoded message using a developer-controlled wallet's private key. ```rust use circle_developer_controlled_wallets::{DeveloperWalletsClient, models::signing::SignMessageRequest}; #[tokio::main] async fn main() -> Result<(), Box> { let client = DeveloperWalletsClient::new("YOUR_API_KEY"); let req = SignMessageRequest { wallet_id: "wallet-uuid-here".to_string(), entity_secret_ciphertext: "YOUR_ENCRYPTED_ENTITY_SECRET".to_string(), message: "Hello, Circle!".to_string(), encoding: None, // or Some("HEX") for hex-encoded messages }; let response = client.sign_message(&req).await?; println!("Signature: {}", response.data.signature); // Output: Signature: 0x1234abcd... Ok(()) } ``` ### Validate Address Validate a blockchain address format for a specific network. ```rust use circle_developer_controlled_wallets::{DeveloperWalletsClient, models::transaction::ValidateAddressRequest}; use circle_developer_controlled_wallets::models::common::Blockchain; #[tokio::main] async fn main() -> Result<(), Box> { let client = DeveloperWalletsClient::new("YOUR_API_KEY"); let req = ValidateAddressRequest { blockchain: Blockchain::EthSepolia, address: "0xab5801a7d398351b8be11c439e05c5b3259aec9b".to_string(), }; let response = client.validate_address(&req).await?; println!("Is valid: {}", response.data.is_valid); // Output: Is valid: true Ok(()) } ``` --- ## Compliance Engine API ### Screen Address Screen a blockchain address for compliance risk. Returns risk scores, categories, and recommended actions based on sanctions lists and other compliance data sources. ```rust use circle_compliance::{ComplianceClient, models::screening::{ScreenAddressRequest, Chain}}; #[tokio::main] async fn main() -> Result<(), Box> { let client = ComplianceClient::new("YOUR_API_KEY"); let req = ScreenAddressRequest { idempotency_key: uuid::Uuid::new_v4().to_string(), address: "0xab5801a7d398351b8be11c439e05c5b3259aec9b".to_string(), chain: Chain::EthSepolia, }; let response = client.screen_address(&req).await?; println!("Screening result: {:?}", response.result); println!("Decision date: {}", response.decision.screening_date); println!("Risk actions: {:?}", response.decision.actions); // Output: Screening result: Approved // Decision date: 2024-01-15T10:30:00Z // Risk actions: Some([Approve]) Ok(()) } ``` --- ## Buidl Wallets API (Account Abstraction) ### List User Operations Query ERC-4337 user operations for smart contract account wallets. ```rust use circle_buidl_wallets::{BuidlWalletsClient, models::user_op::ListUserOpsParams}; #[tokio::main] async fn main() -> Result<(), Box> { let client = BuidlWalletsClient::new("YOUR_API_KEY"); let params = ListUserOpsParams::default(); let response = client.list_user_ops(¶ms).await?; for user_op in &response.data.user_operations { println!("UserOp: {} - State: {:?}", user_op.id, user_op.state); } // Output: UserOp: op-123 - State: Some(Complete) Ok(()) } ``` ### List Transfers Query asset transfers with wallet address filtering. The `wallet_addresses` parameter is required. ```rust use circle_buidl_wallets::{BuidlWalletsClient, models::transfer::ListTransfersParams}; #[tokio::main] async fn main() -> Result<(), Box> { let client = BuidlWalletsClient::new("YOUR_API_KEY"); let params = ListTransfersParams { wallet_addresses: Some("0xab5801a7d398351b8be11c439e05c5b3259aec9b".to_string()), ..Default::default() }; let response = client.list_transfers(¶ms).await?; for transfer in &response.data.transfers { println!("Transfer: {} - Amount: {:?}", transfer.id, transfer.amounts); } // Output: Transfer: tr-456 - Amount: Some(["1.5"]) Ok(()) } ``` ### List Wallet Balances Retrieve token balances for a wallet by UUID or by blockchain/address combination. ```rust use circle_buidl_wallets::{BuidlWalletsClient, models::wallet::ListWalletBalancesParams}; #[tokio::main] async fn main() -> Result<(), Box> { let client = BuidlWalletsClient::new("YOUR_API_KEY"); let wallet_id = "wallet-uuid-here"; let params = ListWalletBalancesParams::default(); let response = client.list_wallet_balances_by_id(wallet_id, ¶ms).await?; for balance in &response.data.token_balances { println!("Token: {:?} - Amount: {}", balance.token.symbol, balance.amount); } // Output: Token: Some("ETH") - Amount: 1.5 // Token: Some("USDC") - Amount: 100.00 Ok(()) } ``` ### List Wallet NFTs Retrieve NFT holdings for a wallet by UUID. ```rust use circle_buidl_wallets::{BuidlWalletsClient, models::wallet::ListWalletNftsParams}; #[tokio::main] async fn main() -> Result<(), Box> { let client = BuidlWalletsClient::new("YOUR_API_KEY"); let wallet_id = "wallet-uuid-here"; let params = ListWalletNftsParams::default(); let response = client.list_wallet_nfts_by_id(wallet_id, ¶ms).await?; for nft in &response.data.nfts { println!("NFT Token ID: {:?} - Amount: {}", nft.nft_token_id, nft.amount); } // Output: NFT Token ID: Some("42") - Amount: 1 Ok(()) } ``` --- ## Command-Line Interface (CLI) ### Build and Configure ```bash # Build the CLI cargo build -p circle-cli # Set API key (recommended via environment variable) export CIRCLE_API_KEY="YOUR_API_KEY" # Or use --api-key flag ./target/debug/circle-cli --api-key "YOUR_API_KEY" user list-users ``` ### Developer Wallet Operations ```bash # List all wallet sets circle-cli developer list-wallet-sets # Output: {"data":{"walletSets":[...]}} # List wallets filtered by blockchain circle-cli developer list-wallets --blockchain ETH-SEPOLIA # List transactions with filters circle-cli developer list-transactions --blockchain ETH-SEPOLIA --state COMPLETE --page-size 10 # Validate an address circle-cli developer validate-address --blockchain ETH-SEPOLIA --address 0xab5801a7d398351b8be11c439e05c5b3259aec9b # Output: {"data":{"isValid":true}} ``` ### User Wallet Operations ```bash # Create a new user circle-cli user create-user --user-id my-test-user-001 # Output: {"data":{"id":"my-test-user-001","pinStatus":"UNSET","status":"ENABLED"}} # Get user token circle-cli user get-user-token --user-id my-test-user-001 # Output: {"data":{"userToken":"","encryptionKey":""}} # List wallets (requires user token) export CIRCLE_USER_TOKEN=$(circle-cli user get-user-token --user-id my-test-user-001 | jq -r '.data.userToken') circle-cli user list-wallets --user-token "$CIRCLE_USER_TOKEN" # Validate address in user context circle-cli user validate-address --blockchain ETH-SEPOLIA --address 0xab5801a7d398351b8be11c439e05c5b3259aec9b ``` ### Compliance Operations ```bash # Screen an address for compliance risk circle-cli compliance screen-address --chain ETH-SEPOLIA --address 0xab5801a7d398351b8be11c439e05c5b3259aec9b # Output: { # "result": "APPROVED", # "decision": { "screeningDate": "2024-01-15T10:30:00Z", ... }, # "address": "0xab5801a7d398351b8be11c439e05c5b3259aec9b", # "chain": "ETH-SEPOLIA" # } ``` ### Buidl Wallet Operations ```bash # List ERC-4337 user operations circle-cli buidl list-user-ops # List transfers (requires wallet addresses) circle-cli buidl list-transfers --wallet-id 0xab5801a7d398351b8be11c439e05c5b3259aec9b # List token balances for a wallet circle-cli buidl list-wallet-balances --wallet-id # List NFTs for a wallet circle-cli buidl list-wallet-nfts --wallet-id ``` --- ## Custom Base URL (Testing) Use a custom base URL for testing against Prism mock servers or sandbox environments. ```rust use circle_developer_controlled_wallets::DeveloperWalletsClient; // Use production URL (default) let prod_client = DeveloperWalletsClient::new("YOUR_API_KEY"); // Use custom base URL for Prism mock server let mock_client = DeveloperWalletsClient::with_base_url( "YOUR_API_KEY", "http://localhost:4012" ); // CLI equivalent // circle-cli --base-url http://localhost:4012 developer list-wallet-sets ``` --- ## Summary The Circle SDK for Rust provides a complete solution for building blockchain wallet applications with Circle's Web3 Services platform. Primary use cases include: custodial wallet management for exchanges and fintech applications using Developer-Controlled Wallets; non-custodial wallet integration for consumer apps using User-Controlled Wallets with PIN-based security; compliance screening for AML/KYC requirements before processing transactions; and ERC-4337 account abstraction via Buidl Wallets for gasless transactions and advanced account features. Integration patterns follow standard Rust async practices with `tokio`. Each client (`UserWalletsClient`, `DeveloperWalletsClient`, `ComplianceClient`, `BuidlWalletsClient`) is instantiated with an API key and shares the same request/response pattern using strongly-typed structs for all parameters and responses. Errors are returned as `Result` with structured API errors containing error codes and messages. The CLI provides immediate access to all API operations for testing and scripting, supporting both JSON and text output formats. For testnet development, use blockchain identifiers like `ETH-SEPOLIA`, `MATIC-AMOY`, or `SOL-DEVNET` with a TEST API key.