pub struct SignerContext;Expand description
The SignerContext provides thread-local signer management for secure multi-tenant operation.
This enables stateless tools that can access the appropriate signer without explicit passing, while maintaining strict isolation between different async tasks and users.
§Security Features
- Thread isolation: Each async task has its own isolated signer context
- No signer leakage: Contexts cannot access signers from other tasks
- Safe concurrent access: Multiple tasks can run concurrently with different signers
- Automatic cleanup: Contexts are automatically cleaned up when tasks complete
§Usage Patterns
§Basic Usage
use riglr_core::signer::SignerContext;
// Concrete signers are from tools crates:
// use riglr_solana_tools::LocalSolanaSigner;
use std::sync::Arc;
let keypair = Keypair::new();
let signer = Arc::new(LocalSolanaSigner::new(
keypair,
"https://api.devnet.solana.com".to_string()
));
// Execute code with signer context
let result = SignerContext::with_signer(signer, async {
// Inside this scope, tools can access the signer
let current = SignerContext::current().await?;
let user_id = current.user_id();
Ok(format!("Processing for user: {:?}", user_id))
}).await?;
println!("Result: {}", result);§Multi-Tenant Service Example
use riglr_core::signer::{SignerContext, UnifiedSigner};
use std::sync::Arc;
async fn handle_user_request(
user_signer: Arc<dyn UnifiedSigner>,
operation: &str
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
SignerContext::with_signer(user_signer, async {
// All operations in this scope use the user's signer
match operation {
"balance" => check_balance().await,
"transfer" => perform_transfer().await,
_ => Err(riglr_core::signer::SignerError::NoSignerContext)
}
}).await.map_err(Into::into)
}
async fn check_balance() -> Result<String, riglr_core::signer::SignerError> {
let signer = SignerContext::current().await?;
Ok(format!("Balance for user: {:?}", signer.user_id()))
}
async fn perform_transfer() -> Result<String, riglr_core::signer::SignerError> {
let signer = SignerContext::current().await?;
// Perform actual transfer using signer...
Ok("Transfer completed".to_string())
}§Error Handling
Tools should always check for signer availability:
use riglr_core::signer::SignerContext;
async fn safe_operation() -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
if !SignerContext::is_available().await {
return Err("This operation requires a signer context".into());
}
let signer = SignerContext::current().await?;
// Proceed with operation...
Ok("Operation completed".to_string())
}§Security Considerations
- Never store signers globally: Always use the context pattern
- Validate user permissions: Check that users own the addresses they’re operating on
- Audit all operations: Log all signer usage for security auditing
- Use environment-specific endpoints: Different signers for mainnet/testnet
Implementations§
Source§impl SignerContext
impl SignerContext
Sourcepub async fn with_signer<T, F>(
signer: Arc<dyn UnifiedSigner>,
future: F,
) -> Result<T, SignerError>
pub async fn with_signer<T, F>( signer: Arc<dyn UnifiedSigner>, future: F, ) -> Result<T, SignerError>
Execute a future with a unified signer context.
This is the primary method for setting up a signer context. It uses the new UnifiedSigner trait which provides better type safety and chain-specific access.
§Arguments
signer- The unified signer to make available in the contextfuture- The async code to execute with the signer context
§Examples
use riglr_core::signer::{SignerContext, UnifiedSigner};
use riglr_solana_tools::LocalSolanaSigner;
use std::sync::Arc;
let signer: Arc<dyn UnifiedSigner> = Arc::new(LocalSolanaSigner::new(
keypair,
"https://api.devnet.solana.com".to_string()
));
let result = SignerContext::with_signer(signer, async {
// Access as Solana signer
let solana = SignerContext::current_as_solana().await?;
let pubkey = solana.pubkey();
Ok(pubkey)
}).await?;Sourcepub async fn current() -> Result<Arc<dyn UnifiedSigner>, SignerError>
pub async fn current() -> Result<Arc<dyn UnifiedSigner>, SignerError>
Get the current unified signer from thread-local context.
This function retrieves the signer that was set by SignerContext::with_signer().
Returns the UnifiedSigner which can be cast to specific signer types.
§Returns
Ok(Arc<dyn UnifiedSigner>)- The current signer if availableErr(SignerError::NoSignerContext)- If called outside a signer context
§Examples
use riglr_core::signer::SignerContext;
async fn tool_that_needs_signer() -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
// Get the current unified signer
let signer = SignerContext::current().await?;
// Check what chains it supports
if signer.supports_solana() {
let solana = SignerContext::current_as_solana().await?;
return Ok(format!("Solana pubkey: {}", solana.pubkey()));
}
if signer.supports_evm() {
let evm = SignerContext::current_as_evm().await?;
return Ok(format!("EVM address: {}", evm.address()));
}
Ok("Unknown signer type".to_string())
}Sourcepub async fn is_available() -> bool
pub async fn is_available() -> bool
Check if there is currently a signer context available.
This function returns true if the current async task is running within
a SignerContext::with_signer() scope, and false otherwise.
This is useful for tools that want to provide different behavior when called with or without a signer context, such as read-only vs. transactional operations.
§Returns
true- If a signer context is availablefalse- If no signer context is available
§Examples
use riglr_core::signer::SignerContext;
// Concrete signers from tools crates:
// use riglr_solana_tools::LocalSolanaSigner;
use std::sync::Arc;
async fn flexible_tool() -> Result<String, riglr_core::signer::SignerError> {
if SignerContext::is_available().await {
// We have a signer, can perform transactions
let signer = SignerContext::current().await?;
Ok("Performing transaction with signer".to_string())
} else {
// No signer available, provide read-only functionality
Ok("Read-only mode: no signer available".to_string())
}
}
// Test without signer context
let result1 = flexible_tool().await.unwrap();
assert_eq!(result1, "Read-only mode: no signer available");
// Test with signer context
let keypair = Keypair::new();
let signer = Arc::new(LocalSolanaSigner::new(
keypair,
"https://api.devnet.solana.com".to_string()
));
let result2 = SignerContext::with_signer(signer, async {
flexible_tool().await
}).await.unwrap();
assert_eq!(result2, "Performing transaction with signer");
§Performance
This function is very lightweight and can be called frequently without performance concerns. It simply checks if the thread-local storage contains a signer reference.
Sourcepub async fn current_as_solana() -> Result<SolanaSignerHandle, SignerError>
pub async fn current_as_solana() -> Result<SolanaSignerHandle, SignerError>
Get the current signer as a specific type.
This method allows type-safe access to chain-specific signer capabilities. Tools can require specific signer types and get compile-time guarantees.
§Type Parameters
T- The specific signer trait to cast to (e.g.,dyn SolanaSigner,dyn EvmSigner)
§Returns
Ok(&T)- Reference to the signer with the requested capabilitiesErr(SignerError::UnsupportedOperation)- If the current signer doesn’t support the requested typeErr(SignerError::NoSignerContext)- If no signer context is available
§Examples
use riglr_core::signer::{SignerContext, SolanaSigner, EvmSigner};
// Require Solana signer
async fn solana_operation() -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let signer = SignerContext::current_as::<dyn SolanaSigner>().await?;
Ok(format!("Solana pubkey: {}", signer.pubkey()))
}
// Require EVM signer
async fn evm_operation() -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let signer = SignerContext::current_as::<dyn EvmSigner>().await?;
Ok(format!("EVM chain: {}", signer.chain_id()))
}Get the current signer as a Solana signer with type-safe access.
This method provides a strongly-typed handle to the current signer’s Solana
capabilities. The returned handle implements Deref to dyn SolanaSigner,
allowing direct access to all Solana-specific methods.
§Returns
Ok(SolanaSignerHandle)- A handle providing type-safe access to Solana operationsErr(SignerError::NoSignerContext)- If called outside a signer contextErr(SignerError::UnsupportedOperation)- If the current signer doesn’t support Solana
§Thread Safety
The returned handle is thread-safe and can be passed between async tasks. The underlying signer is immutable and reference-counted.
§Examples
use riglr_core::signer::SignerContext;
async fn solana_only_operation() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// This will fail if the current signer doesn't support Solana
let signer = SignerContext::current_as_solana().await?;
// Now we have type-safe access to Solana methods
let pubkey = signer.pubkey();
let client = signer.client();
// Perform Solana-specific operations
let balance = client.get_balance(&pubkey.parse()?).await?;
println!("Balance: {} SOL", balance);
Ok(())
}Sourcepub async fn current_as_evm() -> Result<EvmSignerHandle, SignerError>
pub async fn current_as_evm() -> Result<EvmSignerHandle, SignerError>
Get the current signer as an EVM signer with type-safe access.
This method provides a strongly-typed handle to the current signer’s EVM
capabilities. The returned handle implements Deref to dyn EvmSigner,
allowing direct access to all EVM-specific methods.
§Returns
Ok(EvmSignerHandle)- A handle providing type-safe access to EVM operationsErr(SignerError::NoSignerContext)- If called outside a signer contextErr(SignerError::UnsupportedOperation)- If the current signer doesn’t support EVM
§Thread Safety
The returned handle is thread-safe and can be passed between async tasks. The underlying signer is immutable and reference-counted.
§Examples
use riglr_core::signer::SignerContext;
async fn evm_only_operation() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// This will fail if the current signer doesn't support EVM
let signer = SignerContext::current_as_evm().await?;
// Now we have type-safe access to EVM methods
let address = signer.address();
let chain_id = signer.chain_id();
println!("Operating on chain {} with address {}", chain_id, address);
// Build and send a transaction
let tx = TransactionRequest::default()
.to(Address::ZERO)
.value(U256::from(1000000000000000u64)); // 0.001 ETH
let tx_hash = signer.sign_and_send_transaction(tx).await?;
println!("Transaction sent: {}", tx_hash);
Ok(())
}Auto Trait Implementations§
impl Freeze for SignerContext
impl RefUnwindSafe for SignerContext
impl Send for SignerContext
impl Sync for SignerContext
impl Unpin for SignerContext
impl UnsafeUnpin for SignerContext
impl UnwindSafe for SignerContext
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more