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 let address = wallet.address();
23
24 let nonce = format!("0x{}", hex::encode(&rand::random::<[u8; 32]>()));
26
27 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 let parsed_message: Message = siwe_message.parse().unwrap();
41 info!("Parsed message: {:?}", parsed_message);
42
43 let signature = wallet.sign_message(&siwe_message.as_bytes()).await?;
45
46 let sig_hex = format!("0x{}", hex::encode(signature.as_bytes()));
48
49 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, 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 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 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 let nonce = format!("{}", hex::encode(&rand::random::<[u8; 16]>()));
100
101 let issued_at = chrono::Utc::now();
103 let expiration = issued_at + chrono::Duration::hours(24);
104
105 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 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 let message_str = siwe_message.to_string();
134
135 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(); 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}