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 serde_json::Value;
72 use siwe_recap::Capability;
73 use std::collections::BTreeMap;
74
75 let address = wallet.address();
76
77 let mut notabene = BTreeMap::new();
79 notabene.insert(
80 "nft_id".to_string(),
81 Value::from(vec![Value::from(capacity_token_id)]),
82 );
83 notabene.insert("uses".to_string(), Value::from(uses.to_string()));
84 notabene.insert(
85 "delegate_to".to_string(),
86 Value::from(
87 delegatee_addresses
88 .iter()
89 .map(|addr| {
90 Value::from(if let Some(stripped) = addr.strip_prefix("0x") {
92 stripped.to_string()
93 } else {
94 addr.to_string()
95 })
96 })
97 .collect::<Vec<_>>(),
98 ),
99 );
100
101 let nonce = hex::encode(rand::random::<[u8; 16]>());
103
104 let issued_at = chrono::Utc::now();
106 let expiration = issued_at + chrono::Duration::hours(24);
107
108 let mut capabilities = Capability::<Value>::default();
110 let resource = "Auth/Auth".to_string();
111 let resource_prefix = format!("lit-ratelimitincrease://{}", capacity_token_id);
112
113 let capabilities = capabilities
114 .with_actions_convert(resource_prefix, [(resource, [notabene])])
115 .map_err(|e| eyre::eyre!("Failed to create capability: {}", e))?;
116
117 let siwe_message = capabilities
119 .build_message(Message {
120 domain: "lit-protocol.com".parse().unwrap(),
121 address: address.0.into(),
122 statement: Some("Lit Protocol PKP sessionSig".to_string()),
123 uri: "lit:capability:delegation".parse().unwrap(),
124 version: "1".parse().unwrap(),
125 chain_id: 1,
126 nonce: nonce.clone(),
127 issued_at: issued_at
128 .to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
129 .parse()
130 .unwrap(),
131 expiration_time: Some(
132 expiration
133 .to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
134 .parse()
135 .unwrap(),
136 ),
137 not_before: None,
138 request_id: None,
139 resources: vec![],
140 })
141 .map_err(|e| eyre::eyre!("Failed to build SIWE message: {}", e))?;
142
143 let message_str = siwe_message.to_string();
145
146 let signature = wallet.sign_message(message_str.as_bytes()).await?;
148
149 let sig_hex = format!("0x{}", hex::encode(signature.as_bytes()));
150
151 Ok(AuthSig {
152 sig: sig_hex,
153 derived_via: "web3.eth.personal.sign".to_string(),
154 signed_message: message_str,
155 address: address.to_checksum(None),
156 algo: None,
157 })
158 }
159}
160
161pub fn load_wallet_from_env() -> Result<PrivateKeySigner> {
162 dotenv::dotenv().ok(); let private_key = std::env::var("ETHEREUM_PRIVATE_KEY")
165 .map_err(|_| eyre::eyre!("ETHEREUM_PRIVATE_KEY environment variable not set"))?;
166
167 match private_key.parse() {
168 Ok(signer) => Ok(signer),
169 Err(e) => Err(eyre::eyre!("Invalid private key: {}", e)),
170 }
171}