lit_rust_sdk/
auth.rs

1use crate::{
2    types::{AuthMethod, AuthSig},
3    LitNodeClient,
4};
5use alloy::signers::local::PrivateKeySigner;
6use alloy::signers::Signer;
7use eyre::Result;
8use hex;
9use rand;
10use serde_json::json;
11use siwe::Message;
12use tracing::info;
13
14pub struct EthWalletProvider;
15
16impl EthWalletProvider {
17    pub async fn authenticate(
18        wallet: &PrivateKeySigner,
19        _lit_node_client: &LitNodeClient,
20    ) -> Result<AuthMethod> {
21        // Get the wallet address in EIP-55 checksum format
22        let address = wallet.address();
23
24        // Create nonce
25        let nonce = format!("0x{}", hex::encode(&rand::random::<[u8; 32]>()));
26
27        // Create SIWE message for authentication
28        let issued_at = chrono::Utc::now().to_rfc3339();
29        let expiration = (chrono::Utc::now() + chrono::Duration::hours(24)).to_rfc3339();
30
31        let siwe_message = format!(
32            "localhost wants you to sign in with your Ethereum account:\n{}\n\n\nURI: http://localhost\nVersion: 1\nChain ID: 1\nNonce: {}\nIssued At: {}\nExpiration Time: {}",
33            address,
34            nonce,
35            issued_at,
36            expiration
37        );
38
39        // ensure the message will be parsed correctly
40        let parsed_message: Message = siwe_message.parse().unwrap();
41        info!("Parsed message: {:?}", parsed_message);
42
43        // Sign the SIWE message
44        let signature = wallet.sign_message(&siwe_message.as_bytes()).await?;
45
46        // Convert signature to hex string
47        let sig_hex = format!("0x{}", hex::encode(signature.as_bytes()));
48
49        // Create the auth method with proper Lit Protocol auth sig format
50        let auth_sig = json!({
51            "sig": sig_hex,
52            "derivedVia": "web3.eth.personal.sign",
53            "signedMessage": siwe_message,
54            "address": address.to_checksum(None)
55        });
56
57        let auth_method = AuthMethod {
58            auth_method_type: 1, // EthWallet auth method type
59            access_token: auth_sig.to_string(),
60        };
61
62        Ok(auth_method)
63    }
64
65    pub async fn create_capacity_delegation_auth_sig(
66        wallet: &PrivateKeySigner,
67        capacity_token_id: &str,
68        delegatee_addresses: &[String],
69        uses: &str,
70    ) -> Result<AuthSig> {
71        use siwe_recap::Capability;
72        use std::collections::BTreeMap;
73        use serde_json::Value;
74        
75        let address = wallet.address();
76
77        // Create the nota bene data for the capability
78        let mut notabene = BTreeMap::new();
79        notabene.insert("nft_id".to_string(), Value::from(vec![Value::from(capacity_token_id)]));
80        notabene.insert("uses".to_string(), Value::from(uses.to_string()));
81        notabene.insert(
82            "delegate_to".to_string(),
83            Value::from(
84                delegatee_addresses
85                    .iter()
86                    .map(|addr| {
87                        // Remove 0x prefix if present for the delegate_to field
88                        Value::from(if addr.starts_with("0x") {
89                            addr[2..].to_string()
90                        } else {
91                            addr.to_string()
92                        })
93                    })
94                    .collect::<Vec<_>>()
95            ),
96        );
97
98        // Create nonce - use a random hex string
99        let nonce = format!("{}", hex::encode(&rand::random::<[u8; 16]>()));
100
101        // Create SIWE message for capacity delegation
102        let issued_at = chrono::Utc::now();
103        let expiration = issued_at + chrono::Duration::hours(24);
104
105        // Build the capability
106        let mut capabilities = Capability::<Value>::default();
107        let resource = "Auth/Auth".to_string();
108        let resource_prefix = format!("lit-ratelimitincrease://{}", capacity_token_id);
109        
110        let capabilities = capabilities
111            .with_actions_convert(resource_prefix, [(resource, [notabene])])
112            .map_err(|e| eyre::eyre!("Failed to create capability: {}", e))?;
113
114        // Build the SIWE message with the capability
115        let siwe_message = capabilities
116            .build_message(Message {
117                domain: "lit-protocol.com".parse().unwrap(),
118                address: address.0.into(),
119                statement: Some("Lit Protocol PKP sessionSig".to_string()),
120                uri: "lit:capability:delegation".parse().unwrap(),
121                version: "1".parse().unwrap(),
122                chain_id: 1,
123                nonce: nonce.clone(),
124                issued_at: issued_at.to_rfc3339_opts(chrono::SecondsFormat::Millis, true).parse().unwrap(),
125                expiration_time: Some(expiration.to_rfc3339_opts(chrono::SecondsFormat::Millis, true).parse().unwrap()),
126                not_before: None,
127                request_id: None,
128                resources: vec![],
129            })
130            .map_err(|e| eyre::eyre!("Failed to build SIWE message: {}", e))?;
131
132        // Prepare the message string
133        let message_str = siwe_message.to_string();
134
135        // Sign the SIWE message
136        let signature = wallet.sign_message(&message_str.as_bytes()).await?;
137
138        let sig_hex = format!("0x{}", hex::encode(signature.as_bytes()));
139
140        Ok(AuthSig {
141            sig: sig_hex,
142            derived_via: "web3.eth.personal.sign".to_string(),
143            signed_message: message_str,
144            address: address.to_checksum(None),
145            algo: None,
146        })
147    }
148}
149
150pub fn load_wallet_from_env() -> Result<PrivateKeySigner> {
151    dotenv::dotenv().ok(); // Load .env file if it exists
152
153    let private_key = std::env::var("ETHEREUM_PRIVATE_KEY")
154        .map_err(|_| eyre::eyre!("ETHEREUM_PRIVATE_KEY environment variable not set"))?;
155
156    match private_key.parse() {
157        Ok(signer) => Ok(signer),
158        Err(e) => Err(eyre::eyre!("Invalid private key: {}", e)),
159    }
160}