lit_rust_sdk/
chain.rs

1use crate::error::LitSdkError;
2use crate::network::NetworkConfig;
3use ethers::contract::abigen;
4use ethers::prelude::*;
5use ethers::providers::{Http, Provider};
6use ethers::types::{Address, Bytes, I256, U256};
7use ethers::utils::{keccak256, to_checksum};
8use std::sync::Arc;
9use tokio::time::{sleep, Duration};
10
11abigen!(
12    LedgerContract,
13    r#"[{
14        "inputs":[],
15        "name":"deposit",
16        "outputs":[],
17        "stateMutability":"payable",
18        "type":"function"
19    },{
20        "inputs":[{"internalType":"address","name":"user","type":"address"}],
21        "name":"depositForUser",
22        "outputs":[],
23        "stateMutability":"payable",
24        "type":"function"
25    },{
26        "inputs":[{"internalType":"int256","name":"amount","type":"int256"}],
27        "name":"requestWithdraw",
28        "outputs":[],
29        "stateMutability":"nonpayable",
30        "type":"function"
31    },{
32        "inputs":[{"internalType":"int256","name":"amount","type":"int256"}],
33        "name":"withdraw",
34        "outputs":[],
35        "stateMutability":"nonpayable",
36        "type":"function"
37    },{
38        "inputs":[{"internalType":"address","name":"user","type":"address"}],
39        "name":"balance",
40        "outputs":[{"internalType":"int256","name":"","type":"int256"}],
41        "stateMutability":"view",
42        "type":"function"
43    },{
44        "inputs":[{"internalType":"address","name":"user","type":"address"}],
45        "name":"stableBalance",
46        "outputs":[{"internalType":"int256","name":"","type":"int256"}],
47        "stateMutability":"view",
48        "type":"function"
49    },{
50        "inputs":[{"internalType":"address","name":"user","type":"address"}],
51        "name":"latestWithdrawRequest",
52        "outputs":[{"components":[
53            {"internalType":"uint256","name":"timestamp","type":"uint256"},
54            {"internalType":"uint256","name":"amount","type":"uint256"}
55        ],"internalType":"struct LibLedgerStorage.WithdrawRequest","name":"","type":"tuple"}],
56        "stateMutability":"view",
57        "type":"function"
58    },{
59        "inputs":[],
60        "name":"userWithdrawDelay",
61        "outputs":[{"internalType":"uint256","name":"","type":"uint256"}],
62        "stateMutability":"view",
63        "type":"function"
64    }]"#,
65);
66
67abigen!(
68    PaymentDelegationContract,
69    r#"[{
70        "inputs":[{"internalType":"address","name":"user","type":"address"}],
71        "name":"delegatePayments",
72        "outputs":[],
73        "stateMutability":"nonpayable",
74        "type":"function"
75    },{
76        "inputs":[{"internalType":"address[]","name":"users","type":"address[]"}],
77        "name":"delegatePaymentsBatch",
78        "outputs":[],
79        "stateMutability":"nonpayable",
80        "type":"function"
81    },{
82        "inputs":[{"components":[
83            {"internalType":"uint128","name":"totalMaxPrice","type":"uint128"},
84            {"internalType":"uint256","name":"requestsPerPeriod","type":"uint256"},
85            {"internalType":"uint256","name":"periodSeconds","type":"uint256"}
86        ],"internalType":"struct LibPaymentDelegationStorage.Restriction","name":"r","type":"tuple"}],
87        "name":"setRestriction",
88        "outputs":[],
89        "stateMutability":"nonpayable",
90        "type":"function"
91    },{
92        "inputs":[{"internalType":"address","name":"payer","type":"address"}],
93        "name":"getRestriction",
94        "outputs":[{"components":[
95            {"internalType":"uint128","name":"totalMaxPrice","type":"uint128"},
96            {"internalType":"uint256","name":"requestsPerPeriod","type":"uint256"},
97            {"internalType":"uint256","name":"periodSeconds","type":"uint256"}
98        ],"internalType":"struct LibPaymentDelegationStorage.Restriction","name":"","type":"tuple"}],
99        "stateMutability":"view",
100        "type":"function"
101    },{
102        "inputs":[{"internalType":"address","name":"user","type":"address"}],
103        "name":"getPayers",
104        "outputs":[{"internalType":"address[]","name":"","type":"address[]"}],
105        "stateMutability":"view",
106        "type":"function"
107    },{
108        "inputs":[{"internalType":"address","name":"payer","type":"address"}],
109        "name":"getUsers",
110        "outputs":[{"internalType":"address[]","name":"","type":"address[]"}],
111        "stateMutability":"view",
112        "type":"function"
113    },{
114        "inputs":[{"internalType":"address[]","name":"users","type":"address[]"}],
115        "name":"getPayersAndRestrictions",
116        "outputs":[
117            {"internalType":"address[][]","name":"","type":"address[][]"},
118            {"components":[
119                {"internalType":"uint128","name":"totalMaxPrice","type":"uint128"},
120                {"internalType":"uint256","name":"requestsPerPeriod","type":"uint256"},
121                {"internalType":"uint256","name":"periodSeconds","type":"uint256"}
122            ],"internalType":"struct LibPaymentDelegationStorage.Restriction[][]","name":"","type":"tuple[][]"}
123        ],
124        "stateMutability":"view",
125        "type":"function"
126    },{
127        "inputs":[{"internalType":"address","name":"user","type":"address"}],
128        "name":"undelegatePayments",
129        "outputs":[],
130        "stateMutability":"nonpayable",
131        "type":"function"
132    },{
133        "inputs":[{"internalType":"address[]","name":"users","type":"address[]"}],
134        "name":"undelegatePaymentsBatch",
135        "outputs":[],
136        "stateMutability":"nonpayable",
137        "type":"function"
138    }]"#,
139);
140
141abigen!(
142    PkpNftEnumerableContract,
143    r#"[{
144        "inputs":[
145            {"internalType":"address","name":"owner","type":"address"},
146            {"internalType":"uint256","name":"index","type":"uint256"}
147        ],
148        "name":"tokenOfOwnerByIndex",
149        "outputs":[{"internalType":"uint256","name":"","type":"uint256"}],
150        "stateMutability":"view",
151        "type":"function"
152    }]"#,
153);
154
155abigen!(
156    PkpNftMintContract,
157    r#"[{
158        "inputs":[],
159        "name":"mintCost",
160        "outputs":[{"internalType":"uint256","name":"","type":"uint256"}],
161        "stateMutability":"view",
162        "type":"function"
163    },{
164        "inputs":[
165            {"internalType":"uint256","name":"keyType","type":"uint256"},
166            {"internalType":"string","name":"keySetId","type":"string"}
167        ],
168        "name":"mintNext",
169        "outputs":[{"internalType":"uint256","name":"","type":"uint256"}],
170        "stateMutability":"payable",
171        "type":"function"
172    }]"#,
173);
174
175abigen!(
176    PkpHelperContract,
177    r#"[{
178        "inputs":[
179            {"internalType":"uint256","name":"keyType","type":"uint256"},
180            {"internalType":"string","name":"keySetId","type":"string"},
181            {"internalType":"uint256[]","name":"permittedAuthMethodTypes","type":"uint256[]"},
182            {"internalType":"bytes[]","name":"permittedAuthMethodIds","type":"bytes[]"},
183            {"internalType":"bytes[]","name":"permittedAuthMethodPubkeys","type":"bytes[]"},
184            {"internalType":"uint256[][]","name":"permittedAuthMethodScopes","type":"uint256[][]"},
185            {"internalType":"bool","name":"addPkpEthAddressAsPermittedAddress","type":"bool"},
186            {"internalType":"bool","name":"sendPkpToItself","type":"bool"}
187        ],
188        "name":"mintNextAndAddAuthMethods",
189        "outputs":[{"internalType":"uint256","name":"","type":"uint256"}],
190        "stateMutability":"payable",
191        "type":"function"
192    }]"#,
193);
194
195abigen!(
196    PubkeyRouterContract,
197    r#"[{
198        "inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],
199        "name":"getPubkey",
200        "outputs":[{"internalType":"bytes","name":"","type":"bytes"}],
201        "stateMutability":"view",
202        "type":"function"
203    },{
204        "inputs":[
205            {"internalType":"address","name":"stakingContract","type":"address"},
206            {"internalType":"string","name":"keySetId","type":"string"},
207            {"internalType":"bytes32","name":"derivedKeyId","type":"bytes32"}
208        ],
209        "name":"getDerivedPubkey",
210        "outputs":[{"internalType":"bytes","name":"","type":"bytes"}],
211        "stateMutability":"view",
212        "type":"function"
213    }]"#,
214);
215
216abigen!(
217    PkpPermissionsContract,
218    r#"[{
219        "inputs":[
220            {"internalType":"uint256","name":"tokenId","type":"uint256"},
221            {"components":[
222                {"internalType":"uint256","name":"authMethodType","type":"uint256"},
223                {"internalType":"bytes","name":"id","type":"bytes"},
224                {"internalType":"bytes","name":"userPubkey","type":"bytes"}
225            ],"internalType":"struct LibPKPPermissionsStorage.AuthMethod","name":"authMethod","type":"tuple"},
226            {"internalType":"uint256[]","name":"scopes","type":"uint256[]"}
227        ],
228        "name":"addPermittedAuthMethod",
229        "outputs":[],
230        "stateMutability":"nonpayable",
231        "type":"function"
232    },{
233        "inputs":[
234            {"internalType":"uint256","name":"tokenId","type":"uint256"},
235            {"internalType":"uint256","name":"authMethodType","type":"uint256"},
236            {"internalType":"bytes","name":"id","type":"bytes"},
237            {"internalType":"uint256","name":"scopeId","type":"uint256"}
238        ],
239        "name":"removePermittedAuthMethodScope",
240        "outputs":[],
241        "stateMutability":"nonpayable",
242        "type":"function"
243    },{
244        "inputs":[
245            {"internalType":"uint256","name":"tokenId","type":"uint256"},
246            {"internalType":"uint256","name":"authMethodType","type":"uint256"},
247            {"internalType":"bytes","name":"id","type":"bytes"}
248        ],
249        "name":"removePermittedAuthMethod",
250        "outputs":[],
251        "stateMutability":"nonpayable",
252        "type":"function"
253    },{
254        "inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],
255        "name":"getPermittedActions",
256        "outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],
257        "stateMutability":"view",
258        "type":"function"
259    },{
260        "inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],
261        "name":"getPermittedAddresses",
262        "outputs":[{"internalType":"address[]","name":"","type":"address[]"}],
263        "stateMutability":"view",
264        "type":"function"
265    },{
266        "inputs":[
267            {"internalType":"uint256","name":"tokenId","type":"uint256"},
268            {"internalType":"uint256","name":"authMethodType","type":"uint256"},
269            {"internalType":"bytes","name":"id","type":"bytes"},
270            {"internalType":"uint256","name":"maxScopeId","type":"uint256"}
271        ],
272        "name":"getPermittedAuthMethodScopes",
273        "outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],
274        "stateMutability":"view",
275        "type":"function"
276    },{
277        "inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],
278        "name":"getPermittedAuthMethods",
279        "outputs":[{"components":[
280            {"internalType":"uint256","name":"authMethodType","type":"uint256"},
281            {"internalType":"bytes","name":"id","type":"bytes"},
282            {"internalType":"bytes","name":"userPubkey","type":"bytes"}
283        ],"internalType":"struct LibPKPPermissionsStorage.AuthMethod[]","name":"","type":"tuple[]"}],
284        "stateMutability":"view",
285        "type":"function"
286    },{
287        "inputs":[
288            {"internalType":"uint256","name":"authMethodType","type":"uint256"},
289            {"internalType":"bytes","name":"id","type":"bytes"}
290        ],
291        "name":"getTokenIdsForAuthMethod",
292        "outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],
293        "stateMutability":"view",
294        "type":"function"
295    },{
296        "inputs":[
297            {"internalType":"uint256","name":"tokenId","type":"uint256"},
298            {"internalType":"bytes","name":"ipfsCID","type":"bytes"}
299        ],
300        "name":"isPermittedAction",
301        "outputs":[{"internalType":"bool","name":"","type":"bool"}],
302        "stateMutability":"view",
303        "type":"function"
304    },{
305        "inputs":[
306            {"internalType":"uint256","name":"tokenId","type":"uint256"},
307            {"internalType":"address","name":"user","type":"address"}
308        ],
309        "name":"isPermittedAddress",
310        "outputs":[{"internalType":"bool","name":"","type":"bool"}],
311        "stateMutability":"view",
312        "type":"function"
313    }]"#,
314);
315
316pub fn ledger_address_for(network: &str) -> Option<Address> {
317    match network {
318        "naga-dev" => Some("0x81061b50a66EBB3E7F9CEbeF2b1C1A961aE858F4".parse().ok()?),
319        "naga-test" => Some("0xbA0aEB6Bbf58F1B74E896416A20DB5be51C991f2".parse().ok()?),
320        "naga-staging" => Some("0x23Be686cAFCe69C5Fb075E2be7a4505598E338E8".parse().ok()?),
321        "naga-proto" => Some("0x25be72246358491Ac7a1eF138C39Ff3b240E50b5".parse().ok()?),
322        "naga" => Some("0x9BD023448d2D3b2D73fe61E4d7859007F6dA372c".parse().ok()?),
323        _ => None,
324    }
325}
326
327pub fn payment_delegation_address_for(network: &str) -> Option<Address> {
328    match network {
329        "naga-dev" => Some("0x2F202f846CBB27Aa5EbE6b9cfad50D65c49c01FF".parse().ok()?),
330        "naga-test" => Some("0xd1E59c174BcF85012c54086AB600Dd0aB032e88B".parse().ok()?),
331        "naga-staging" => Some("0x13fC0864A37B38D3C2A7d5E9C08D5124B9Cec4bF".parse().ok()?),
332        "naga-proto" => Some("0x5033b79388EBBAf466B4CF82c0b72Abd9bB940d6".parse().ok()?),
333        "naga" => Some("0x5EF658cB6ab3C3BfB75C8293B9a6C8ccb0b96C3c".parse().ok()?),
334        _ => None,
335    }
336}
337
338pub fn pkp_nft_address_for(network: &str) -> Option<Address> {
339    match network {
340        "naga-dev" => Some("0xB144B88514316a2f155D22937C76795b8fC9aDCd".parse().ok()?),
341        "naga-test" => Some("0xaf4Dddb07Cdde48042e93eb5bf266b49950bC5BD".parse().ok()?),
342        "naga-staging" => Some("0x92d2a4Acb70E498a486E0523AD42fF3F6d3D3642".parse().ok()?),
343        "naga-proto" => Some("0xaeEA5fE3654919c8Bb2b356aDCb5dF4eC082C168".parse().ok()?),
344        "naga" => Some("0x11eBfFeab32f6cb5775BeF83E09124B9322E4026".parse().ok()?),
345        _ => None,
346    }
347}
348
349pub fn pkp_helper_address_for(network: &str) -> Option<Address> {
350    match network {
351        "naga-dev" => Some("0xDC62fcb77554229FF2d9857B25f5BB824d33aE71".parse().ok()?),
352        "naga-test" => Some("0x13428A18C0b181344F97ceaC5596F31a9d182e5c".parse().ok()?),
353        "naga-staging" => Some("0xe97fFbc4eDa5CdF70375D4b8f87e476D40b628EC".parse().ok()?),
354        "naga-proto" => Some("0xCCb4A87731B3eFd6732e257381486912eEde24C5".parse().ok()?),
355        "naga" => Some("0xAe666c3080AA5Dd935574099c18E1eD779FFB231".parse().ok()?),
356        _ => None,
357    }
358}
359
360fn pubkey_router_address_for(network: &str) -> Option<Address> {
361    match network {
362        "naga-dev" => Some("0x9067d809df0CF7DaF6a9f20E39d572fee1564c8E".parse().ok()?),
363        "naga-test" => Some("0x054Ddcfef7E9434413ad62A6F37946Bf6B6CFc1A".parse().ok()?),
364        "naga-staging" => Some("0xE37847746012c756d5D91d37B311eeB8e59684e9".parse().ok()?),
365        "naga-proto" => Some("0xB0c6B245B25F2e542c3570b53439825615371231".parse().ok()?),
366        "naga" => Some("0x5655D71832f6f2AFD72c3012a60144f5572897F1".parse().ok()?),
367        _ => None,
368    }
369}
370
371fn pkp_permissions_address_for(network: &str) -> Option<Address> {
372    match network {
373        "naga-dev" => Some("0x85Fa92469Ed765791818b17C926d29fA824E25Ca".parse().ok()?),
374        "naga-test" => Some("0x7255737630fCFb4914cF51552123eEe9abEc6120".parse().ok()?),
375        "naga-staging" => Some("0x1E382ef3957218423C6e1a992a4cE6294861cC93".parse().ok()?),
376        "naga-proto" => Some("0x3894cae120A6ca08150e6e51cBcBdD5c16115F9c".parse().ok()?),
377        "naga" => Some("0xEB1F9A8567bC01b8cfa9d6e7078bEf587D908342".parse().ok()?),
378        _ => None,
379    }
380}
381
382fn staking_address_for(network: &str) -> Option<Address> {
383    match network {
384        "naga-dev" => Some("0x544ac098670a266d3598B543aefBEbAb0A2C86C6".parse().ok()?),
385        "naga-test" => Some("0x9f3cE810695180C5f693a7cD2a0203A381fd57E1".parse().ok()?),
386        "naga-staging" => Some("0x9b8Ed3FD964Bc38dDc32CF637439e230CD50e3Dd".parse().ok()?),
387        "naga-proto" => Some("0x28759afC5989B961D0A8EB236C9074c4141Baea1".parse().ok()?),
388        "naga" => Some("0x8a861B3640c1ff058CCB109ba11CA3224d228159".parse().ok()?),
389        _ => None,
390    }
391}
392
393fn provider_from_config(config: &NetworkConfig) -> Result<Arc<Provider<Http>>, LitSdkError> {
394    let rpc_url = config
395        .rpc_url
396        .as_deref()
397        .ok_or_else(|| LitSdkError::Config("rpc_url is required for chain APIs".into()))?;
398    let provider =
399        Provider::<Http>::try_from(rpc_url).map_err(|e| LitSdkError::Config(e.to_string()))?;
400    Ok(Arc::new(provider))
401}
402
403fn parse_hex_bytes(hex_str: &str) -> Result<Bytes, LitSdkError> {
404    let s = hex_str.strip_prefix("0x").unwrap_or(hex_str);
405    let bytes = hex::decode(s).map_err(|e| LitSdkError::Config(e.to_string()))?;
406    Ok(Bytes::from(bytes))
407}
408
409pub fn auth_method_id_for_eth_wallet(address: Address) -> Bytes {
410    let checksum = to_checksum(&address, None);
411    let msg = format!("{checksum}:lit");
412    Bytes::from(keccak256(msg.as_bytes()).to_vec())
413}
414
415fn eth_address_from_pubkey(pubkey: &[u8]) -> Result<Address, LitSdkError> {
416    if pubkey.len() < 2 {
417        return Err(LitSdkError::Config("pubkey too short".into()));
418    }
419    let hash = keccak256(&pubkey[1..]);
420    Ok(Address::from_slice(&hash[12..]))
421}
422
423#[derive(Clone, Debug, Default)]
424pub struct Pagination {
425    pub limit: usize,
426    pub offset: usize,
427}
428
429#[derive(Clone, Debug)]
430pub struct PaginationInfo {
431    pub limit: usize,
432    pub offset: usize,
433    pub total: usize,
434    pub has_more: bool,
435}
436
437#[derive(Clone, Debug)]
438pub struct PkpData {
439    pub token_id: U256,
440    pub pubkey: String,
441    pub eth_address: Address,
442}
443
444#[derive(Clone, Debug)]
445pub struct MintPkpTx {
446    pub hash: TxHash,
447    pub receipt: TransactionReceipt,
448    pub data: PkpData,
449}
450
451#[derive(Clone, Debug)]
452pub struct PaginatedPkps {
453    pub pkps: Vec<PkpData>,
454    pub pagination: PaginationInfo,
455}
456
457async fn token_ids_for_owner<M: Middleware>(
458    pkp_nft: &PkpNftEnumerableContract<M>,
459    owner: Address,
460) -> Vec<U256> {
461    const BATCH_SIZE: usize = 5;
462    const MAX_BATCHES: usize = 20;
463
464    let mut out = vec![];
465    for batch_index in 0..MAX_BATCHES {
466        let start_index = batch_index * BATCH_SIZE;
467        let calls = (0..BATCH_SIZE)
468            .map(|i| {
469                let index = U256::from((start_index + i) as u64);
470                let call = pkp_nft.token_of_owner_by_index(owner, index);
471                async move { call.call().await }
472            })
473            .collect::<Vec<_>>();
474
475        let results = futures::future::join_all(calls).await;
476
477        let mut successes = 0usize;
478        for token_id in results.into_iter().flatten() {
479            successes += 1;
480            out.push(token_id);
481        }
482
483        if successes == 0 {
484            break;
485        }
486    }
487
488    out
489}
490
491async fn pkp_details_for_token_ids(
492    config: &NetworkConfig,
493    token_ids: &[U256],
494) -> Result<Vec<PkpData>, LitSdkError> {
495    let provider = provider_from_config(config)?;
496    let router_addr = pubkey_router_address_for(config.network).ok_or_else(|| {
497        LitSdkError::Config(format!(
498            "unknown PubkeyRouter address for network {}",
499            config.network
500        ))
501    })?;
502    let router = PubkeyRouterContract::new(router_addr, provider);
503
504    let mut out = Vec::with_capacity(token_ids.len());
505    for token_id in token_ids {
506        let pubkey_bytes: Bytes = router
507            .get_pubkey(*token_id)
508            .call()
509            .await
510            .map_err(|e| LitSdkError::Network(e.to_string()))?;
511
512        let pubkey_hex = format!("0x{}", hex::encode(pubkey_bytes.as_ref()));
513        let eth_address = eth_address_from_pubkey(pubkey_bytes.as_ref())?;
514
515        out.push(PkpData {
516            token_id: *token_id,
517            pubkey: pubkey_hex,
518            eth_address,
519        });
520    }
521
522    Ok(out)
523}
524
525/// Fetch a derived public key from the PubkeyRouter contract.
526///
527/// Matches the JS SDK behavior used by `litClient.utils.getDerivedKeyId(...)`.
528pub async fn get_derived_pubkey(
529    config: &NetworkConfig,
530    derived_key_id: H256,
531) -> Result<String, LitSdkError> {
532    const DEFAULT_KEY_SET_ID: &str = "naga-keyset1";
533
534    let provider = provider_from_config(config)?;
535    let router_addr = pubkey_router_address_for(config.network).ok_or_else(|| {
536        LitSdkError::Config(format!(
537            "unknown PubkeyRouter address for network {}",
538            config.network
539        ))
540    })?;
541    let staking_addr = staking_address_for(config.network).ok_or_else(|| {
542        LitSdkError::Config(format!(
543            "unknown Staking contract address for network {}",
544            config.network
545        ))
546    })?;
547
548    let router = PubkeyRouterContract::new(router_addr, provider);
549    let pubkey_bytes: Bytes = router
550        .get_derived_pubkey(
551            staking_addr,
552            DEFAULT_KEY_SET_ID.to_string(),
553            derived_key_id.0,
554        )
555        .call()
556        .await
557        .map_err(|e| LitSdkError::Network(e.to_string()))?;
558
559    Ok(format!("0x{}", hex::encode(pubkey_bytes.as_ref())))
560}
561
562pub async fn view_pkps_by_address(
563    config: &NetworkConfig,
564    owner_address: Address,
565    pagination: Pagination,
566) -> Result<PaginatedPkps, LitSdkError> {
567    let provider = provider_from_config(config)?;
568    let pkp_nft_addr = pkp_nft_address_for(config.network).ok_or_else(|| {
569        LitSdkError::Config(format!(
570            "unknown PKPNFT address for network {}",
571            config.network
572        ))
573    })?;
574
575    let pkp_nft = PkpNftEnumerableContract::new(pkp_nft_addr, provider);
576    let all_token_ids = token_ids_for_owner(&pkp_nft, owner_address).await;
577
578    let total = all_token_ids.len();
579    let limit = if pagination.limit == 0 {
580        10
581    } else {
582        pagination.limit
583    };
584    let offset = pagination.offset;
585    let has_more = offset + limit < total;
586
587    let paginated = all_token_ids
588        .iter()
589        .skip(offset)
590        .take(limit)
591        .copied()
592        .collect::<Vec<_>>();
593    let pkps = pkp_details_for_token_ids(config, &paginated).await?;
594
595    Ok(PaginatedPkps {
596        pkps,
597        pagination: PaginationInfo {
598            limit,
599            offset,
600            total,
601            has_more,
602        },
603    })
604}
605
606pub async fn view_pkps_by_auth_data(
607    config: &NetworkConfig,
608    auth_method_type: U256,
609    auth_method_id_hex: &str,
610    pagination: Pagination,
611) -> Result<PaginatedPkps, LitSdkError> {
612    let provider = provider_from_config(config)?;
613    let permissions_addr = pkp_permissions_address_for(config.network).ok_or_else(|| {
614        LitSdkError::Config(format!(
615            "unknown PKPPermissions address for network {}",
616            config.network
617        ))
618    })?;
619    let permissions = PkpPermissionsContract::new(permissions_addr, provider);
620    let auth_method_id = parse_hex_bytes(auth_method_id_hex)?;
621
622    let token_ids: Vec<U256> = permissions
623        .get_token_ids_for_auth_method(auth_method_type, auth_method_id)
624        .call()
625        .await
626        .map_err(|e| LitSdkError::Network(e.to_string()))?;
627
628    let total = token_ids.len();
629    let limit = if pagination.limit == 0 {
630        50
631    } else {
632        pagination.limit
633    };
634    let offset = pagination.offset;
635    let has_more = offset + limit < total;
636
637    let paginated = token_ids
638        .iter()
639        .skip(offset)
640        .take(limit)
641        .copied()
642        .collect::<Vec<_>>();
643
644    let pkps = pkp_details_for_token_ids(config, &paginated).await?;
645
646    Ok(PaginatedPkps {
647        pkps,
648        pagination: PaginationInfo {
649            limit,
650            offset,
651            total,
652            has_more,
653        },
654    })
655}
656
657fn pkp_data_from_mint_receipt(receipt: &TransactionReceipt) -> Result<PkpData, LitSdkError> {
658    let event_sig = H256::from(keccak256("PKPMinted(uint256,bytes)".as_bytes()));
659    for log in &receipt.logs {
660        if log.topics.first() != Some(&event_sig) || log.topics.len() < 2 {
661            continue;
662        }
663
664        let token_id = U256::from_big_endian(log.topics[1].as_bytes());
665        let decoded = ethers::abi::decode(&[ethers::abi::ParamType::Bytes], log.data.as_ref())
666            .map_err(|e| LitSdkError::Network(e.to_string()))?;
667        let Some(ethers::abi::Token::Bytes(pubkey_bytes)) = decoded.into_iter().next() else {
668            continue;
669        };
670
671        let pubkey_hex = format!("0x{}", hex::encode(pubkey_bytes.as_slice()));
672        let eth_address = eth_address_from_pubkey(pubkey_bytes.as_slice())?;
673        return Ok(PkpData {
674            token_id,
675            pubkey: pubkey_hex,
676            eth_address,
677        });
678    }
679
680    Err(LitSdkError::Network("PKPMinted event not found".into()))
681}
682
683pub struct PkpMintManager<M: Middleware> {
684    pkp_nft: PkpNftMintContract<M>,
685    pkp_helper: PkpHelperContract<M>,
686}
687
688impl<M: Middleware> PkpMintManager<M> {
689    pub fn new(config: &NetworkConfig, middleware: Arc<M>) -> Result<Self, LitSdkError> {
690        let pkp_nft_addr = pkp_nft_address_for(config.network).ok_or_else(|| {
691            LitSdkError::Config(format!(
692                "unknown PKPNFT address for network {}",
693                config.network
694            ))
695        })?;
696        let pkp_helper_addr = pkp_helper_address_for(config.network).ok_or_else(|| {
697            LitSdkError::Config(format!(
698                "unknown PKPHelper address for network {}",
699                config.network
700            ))
701        })?;
702        Ok(Self {
703            pkp_nft: PkpNftMintContract::new(pkp_nft_addr, middleware.clone()),
704            pkp_helper: PkpHelperContract::new(pkp_helper_addr, middleware),
705        })
706    }
707
708    async fn receipt_with_pkp_data(
709        &self,
710        tx_hash: TxHash,
711        mut receipt: TransactionReceipt,
712    ) -> Result<(TransactionReceipt, PkpData), LitSdkError> {
713        if receipt.status.unwrap_or_default().as_u64() != 1 {
714            return Err(LitSdkError::Network("PKP mint transaction failed".into()));
715        }
716
717        match pkp_data_from_mint_receipt(&receipt) {
718            Ok(data) => Ok((receipt, data)),
719            Err(err) => {
720                let mut last_err = err;
721                let middleware = self.pkp_nft.client();
722                for _ in 0..3 {
723                    sleep(Duration::from_secs(2)).await;
724                    let refreshed = middleware
725                        .get_transaction_receipt(tx_hash)
726                        .await
727                        .map_err(|e| LitSdkError::Network(e.to_string()))?;
728                    if let Some(r) = refreshed {
729                        receipt = r;
730                        match pkp_data_from_mint_receipt(&receipt) {
731                            Ok(data) => return Ok((receipt, data)),
732                            Err(err) => last_err = err,
733                        }
734                    }
735                }
736                Err(last_err)
737            }
738        }
739    }
740
741    pub async fn mint_next(
742        &self,
743        key_type: U256,
744        key_set_id: impl Into<String>,
745    ) -> Result<MintPkpTx, LitSdkError> {
746        let mint_cost = self
747            .pkp_nft
748            .mint_cost()
749            .call()
750            .await
751            .map_err(|e| LitSdkError::Network(e.to_string()))?;
752
753        let call = self
754            .pkp_nft
755            .mint_next(key_type, key_set_id.into())
756            .value(mint_cost);
757
758        let pending = call
759            .send()
760            .await
761            .map_err(|e| LitSdkError::Network(e.to_string()))?;
762        let hash = *pending;
763        let receipt = pending
764            .await
765            .map_err(|e| LitSdkError::Network(e.to_string()))?
766            .ok_or_else(|| LitSdkError::Network("mintNext tx dropped from mempool".into()))?;
767        let (receipt, data) = self.receipt_with_pkp_data(hash, receipt).await?;
768        Ok(MintPkpTx {
769            hash,
770            receipt,
771            data,
772        })
773    }
774
775    #[allow(clippy::too_many_arguments)]
776    pub async fn mint_next_and_add_auth_methods(
777        &self,
778        key_type: U256,
779        key_set_id: impl Into<String>,
780        permitted_auth_method_types: Vec<U256>,
781        permitted_auth_method_ids: Vec<Bytes>,
782        permitted_auth_method_pubkeys: Vec<Bytes>,
783        permitted_auth_method_scopes: Vec<Vec<U256>>,
784        add_pkp_eth_address_as_permitted_address: bool,
785        send_pkp_to_itself: bool,
786    ) -> Result<MintPkpTx, LitSdkError> {
787        let n = permitted_auth_method_types.len();
788        if permitted_auth_method_ids.len() != n
789            || permitted_auth_method_pubkeys.len() != n
790            || permitted_auth_method_scopes.len() != n
791        {
792            return Err(LitSdkError::Config(
793                "permitted auth method arrays must be the same length".into(),
794            ));
795        }
796
797        let mint_cost = self
798            .pkp_nft
799            .mint_cost()
800            .call()
801            .await
802            .map_err(|e| LitSdkError::Network(e.to_string()))?;
803
804        let call = self
805            .pkp_helper
806            .mint_next_and_add_auth_methods(
807                key_type,
808                key_set_id.into(),
809                permitted_auth_method_types,
810                permitted_auth_method_ids,
811                permitted_auth_method_pubkeys,
812                permitted_auth_method_scopes,
813                add_pkp_eth_address_as_permitted_address,
814                send_pkp_to_itself,
815            )
816            .value(mint_cost);
817
818        let pending = call
819            .send()
820            .await
821            .map_err(|e| LitSdkError::Network(e.to_string()))?;
822        let hash = *pending;
823        let receipt = pending
824            .await
825            .map_err(|e| LitSdkError::Network(e.to_string()))?
826            .ok_or_else(|| {
827                LitSdkError::Network("mintNextAndAddAuthMethods tx dropped from mempool".into())
828            })?;
829        let (receipt, data) = self.receipt_with_pkp_data(hash, receipt).await?;
830        Ok(MintPkpTx {
831            hash,
832            receipt,
833            data,
834        })
835    }
836
837    pub async fn mint_with_custom_auth(
838        &self,
839        custom_auth_method_type: U256,
840        custom_auth_method_id: Bytes,
841        validation_ipfs_cid_v0: &str,
842        scope: &str,
843        add_pkp_eth_address_as_permitted_address: bool,
844        send_pkp_to_itself: bool,
845    ) -> Result<MintPkpTx, LitSdkError> {
846        let scope_id = scope_id_for(scope)
847            .ok_or_else(|| LitSdkError::Config(format!("unsupported auth scope: {scope}")))?;
848        let validation_ipfs_id = ipfs_cid_v0_to_bytes(validation_ipfs_cid_v0)?;
849
850        self.mint_next_and_add_auth_methods(
851            U256::from(2u64),
852            "naga-keyset1",
853            vec![custom_auth_method_type, U256::from(2u64)],
854            vec![custom_auth_method_id, validation_ipfs_id],
855            vec![Bytes::from(vec![]), Bytes::from(vec![])],
856            vec![vec![U256::from(scope_id)], vec![U256::from(scope_id)]],
857            add_pkp_eth_address_as_permitted_address,
858            send_pkp_to_itself,
859        )
860        .await
861    }
862
863    pub async fn mint_with_eoa(&self) -> Result<MintPkpTx, LitSdkError> {
864        self.mint_next(U256::from(2u64), "naga-keyset1").await
865    }
866}
867
868#[derive(Clone, Debug)]
869pub struct PaymentBalance {
870    pub total_balance_wei: I256,
871    pub available_balance_wei: I256,
872}
873
874#[derive(Clone, Debug)]
875pub struct WithdrawRequest {
876    pub timestamp: U256,
877    pub amount: U256,
878}
879
880#[derive(Clone, Debug)]
881pub struct WithdrawRequestInfo {
882    pub request: WithdrawRequest,
883    pub is_pending: bool,
884}
885
886#[derive(Clone, Debug)]
887pub struct PaymentTx {
888    pub hash: TxHash,
889    pub receipt: TransactionReceipt,
890}
891
892pub struct PaymentManager<M: Middleware> {
893    ledger: LedgerContract<M>,
894}
895
896impl<M: Middleware> PaymentManager<M> {
897    pub fn new(config: &NetworkConfig, middleware: Arc<M>) -> Result<Self, LitSdkError> {
898        let addr = ledger_address_for(config.network).ok_or_else(|| {
899            LitSdkError::Config(format!(
900                "unknown Ledger address for network {}",
901                config.network
902            ))
903        })?;
904        Ok(Self {
905            ledger: LedgerContract::new(addr, middleware),
906        })
907    }
908
909    pub async fn deposit(&self, amount_wei: U256) -> Result<PaymentTx, LitSdkError> {
910        let call = self.ledger.deposit().value(amount_wei);
911        let pending = call
912            .send()
913            .await
914            .map_err(|e| LitSdkError::Network(e.to_string()))?;
915        let hash = *pending;
916        let receipt = pending
917            .await
918            .map_err(|e| LitSdkError::Network(e.to_string()))?
919            .ok_or_else(|| LitSdkError::Network("deposit tx dropped from mempool".into()))?;
920        Ok(PaymentTx { hash, receipt })
921    }
922
923    pub async fn deposit_for_user(
924        &self,
925        user_address: Address,
926        amount_wei: U256,
927    ) -> Result<PaymentTx, LitSdkError> {
928        let call = self.ledger.deposit_for_user(user_address).value(amount_wei);
929        let pending = call
930            .send()
931            .await
932            .map_err(|e| LitSdkError::Network(e.to_string()))?;
933        let hash = *pending;
934        let receipt = pending
935            .await
936            .map_err(|e| LitSdkError::Network(e.to_string()))?
937            .ok_or_else(|| LitSdkError::Network("depositForUser tx dropped from mempool".into()))?;
938        Ok(PaymentTx { hash, receipt })
939    }
940
941    pub async fn get_balance(&self, user_address: Address) -> Result<PaymentBalance, LitSdkError> {
942        let total = self
943            .ledger
944            .balance(user_address)
945            .call()
946            .await
947            .map_err(|e| LitSdkError::Network(e.to_string()))?;
948        let available = self
949            .ledger
950            .stable_balance(user_address)
951            .call()
952            .await
953            .map_err(|e| LitSdkError::Network(e.to_string()))?;
954
955        Ok(PaymentBalance {
956            total_balance_wei: total,
957            available_balance_wei: available,
958        })
959    }
960
961    pub async fn request_withdraw(&self, amount_wei: U256) -> Result<PaymentTx, LitSdkError> {
962        let amount = I256::from_raw(amount_wei);
963        let call = self.ledger.request_withdraw(amount);
964        let pending = call
965            .send()
966            .await
967            .map_err(|e| LitSdkError::Network(e.to_string()))?;
968        let hash = *pending;
969        let receipt = pending
970            .await
971            .map_err(|e| LitSdkError::Network(e.to_string()))?
972            .ok_or_else(|| {
973                LitSdkError::Network("requestWithdraw tx dropped from mempool".into())
974            })?;
975        Ok(PaymentTx { hash, receipt })
976    }
977
978    pub async fn withdraw(&self, amount_wei: U256) -> Result<PaymentTx, LitSdkError> {
979        let amount = I256::from_raw(amount_wei);
980        let call = self.ledger.withdraw(amount);
981        let pending = call
982            .send()
983            .await
984            .map_err(|e| LitSdkError::Network(e.to_string()))?;
985        let hash = *pending;
986        let receipt = pending
987            .await
988            .map_err(|e| LitSdkError::Network(e.to_string()))?
989            .ok_or_else(|| LitSdkError::Network("withdraw tx dropped from mempool".into()))?;
990        Ok(PaymentTx { hash, receipt })
991    }
992
993    pub async fn get_withdraw_request(
994        &self,
995        user_address: Address,
996    ) -> Result<WithdrawRequestInfo, LitSdkError> {
997        let wr = self
998            .ledger
999            .latest_withdraw_request(user_address)
1000            .call()
1001            .await
1002            .map_err(|e| LitSdkError::Network(e.to_string()))?;
1003        let is_pending = wr.timestamp > U256::zero() && wr.amount > U256::zero();
1004        Ok(WithdrawRequestInfo {
1005            request: WithdrawRequest {
1006                timestamp: wr.timestamp,
1007                amount: wr.amount,
1008            },
1009            is_pending,
1010        })
1011    }
1012
1013    pub async fn get_withdraw_delay_seconds(&self) -> Result<U256, LitSdkError> {
1014        self.ledger
1015            .user_withdraw_delay()
1016            .call()
1017            .await
1018            .map_err(|e| LitSdkError::Network(e.to_string()))
1019    }
1020
1021    pub async fn can_execute_withdraw(
1022        &self,
1023        user_address: Address,
1024    ) -> Result<(bool, Option<u64>, WithdrawRequestInfo), LitSdkError> {
1025        let wr = self.get_withdraw_request(user_address).await?;
1026        let delay = self.get_withdraw_delay_seconds().await?;
1027        if !wr.is_pending {
1028            return Ok((false, None, wr));
1029        }
1030
1031        let now_secs = std::time::SystemTime::now()
1032            .duration_since(std::time::SystemTime::UNIX_EPOCH)
1033            .map_err(|e| LitSdkError::Network(e.to_string()))?
1034            .as_secs();
1035
1036        let req_time = wr.request.timestamp.as_u64();
1037        let delay_secs = delay.as_u64();
1038        let execute_time = req_time.saturating_add(delay_secs);
1039        if now_secs >= execute_time {
1040            Ok((true, None, wr))
1041        } else {
1042            Ok((false, Some(execute_time - now_secs), wr))
1043        }
1044    }
1045}
1046
1047fn ipfs_cid_v0_to_bytes(cid_v0: &str) -> Result<Bytes, LitSdkError> {
1048    let bytes = bs58::decode(cid_v0)
1049        .into_vec()
1050        .map_err(|e| LitSdkError::Config(format!("invalid ipfs cid: {e}")))?;
1051    Ok(Bytes::from(bytes))
1052}
1053
1054fn bytes_to_ipfs_cid_v0(bytes: &[u8]) -> String {
1055    bs58::encode(bytes).into_string()
1056}
1057
1058fn scope_id_for(scope: &str) -> Option<u64> {
1059    match scope {
1060        "no-permissions" => Some(0),
1061        "sign-anything" => Some(1),
1062        "personal-sign" => Some(2),
1063        _ => None,
1064    }
1065}
1066
1067fn scopes_to_u256(scopes: &[String]) -> Result<Vec<U256>, LitSdkError> {
1068    scopes
1069        .iter()
1070        .map(|s| {
1071            scope_id_for(s)
1072                .map(U256::from)
1073                .ok_or_else(|| LitSdkError::Config(format!("unknown scope: {s}")))
1074        })
1075        .collect()
1076}
1077
1078#[derive(Clone, Debug)]
1079pub struct PkpAuthMethod {
1080    pub auth_method_type: U256,
1081    pub id: String,
1082    pub user_pubkey: String,
1083}
1084
1085#[derive(Clone, Debug)]
1086pub struct PkpAuthMethodWithScopes {
1087    pub auth_method_type: U256,
1088    pub id: String,
1089    pub user_pubkey: String,
1090    pub scopes: Vec<String>,
1091}
1092
1093#[derive(Clone, Debug)]
1094pub struct PkpPermissionsContext {
1095    pub actions: Vec<String>,
1096    pub addresses: Vec<Address>,
1097    pub auth_methods: Vec<PkpAuthMethodWithScopes>,
1098}
1099
1100impl PkpPermissionsContext {
1101    pub fn is_address_permitted(&self, address: Address) -> bool {
1102        self.addresses.contains(&address)
1103    }
1104
1105    pub fn is_action_permitted(&self, ipfs_cid_v0: &str) -> bool {
1106        self.actions.iter().any(|a| a == ipfs_cid_v0)
1107    }
1108
1109    pub fn is_auth_method_permitted(
1110        &self,
1111        auth_method_type: U256,
1112        auth_method_id_hex: &str,
1113    ) -> bool {
1114        let id = auth_method_id_hex.to_lowercase();
1115        self.auth_methods
1116            .iter()
1117            .any(|m| m.auth_method_type == auth_method_type && m.id.to_lowercase() == id)
1118    }
1119}
1120
1121pub struct PkpPermissionsManager<M: Middleware> {
1122    contract: PkpPermissionsContract<M>,
1123    token_id: U256,
1124}
1125
1126impl<M: Middleware> PkpPermissionsManager<M> {
1127    pub fn new(
1128        config: &NetworkConfig,
1129        middleware: Arc<M>,
1130        token_id: U256,
1131    ) -> Result<Self, LitSdkError> {
1132        let addr = pkp_permissions_address_for(config.network).ok_or_else(|| {
1133            LitSdkError::Config(format!(
1134                "unknown PKPPermissions address for network {}",
1135                config.network
1136            ))
1137        })?;
1138        Ok(Self {
1139            contract: PkpPermissionsContract::new(addr, middleware),
1140            token_id,
1141        })
1142    }
1143
1144    pub async fn get_permitted_addresses(&self) -> Result<Vec<Address>, LitSdkError> {
1145        self.contract
1146            .get_permitted_addresses(self.token_id)
1147            .call()
1148            .await
1149            .map_err(|e| LitSdkError::Network(e.to_string()))
1150    }
1151
1152    pub async fn get_permitted_actions(&self) -> Result<Vec<String>, LitSdkError> {
1153        let actions = self
1154            .contract
1155            .get_permitted_actions(self.token_id)
1156            .call()
1157            .await
1158            .map_err(|e| LitSdkError::Network(e.to_string()))?;
1159        Ok(actions
1160            .iter()
1161            .map(|b| bytes_to_ipfs_cid_v0(b.as_ref()))
1162            .collect())
1163    }
1164
1165    pub async fn is_permitted_address(&self, user: Address) -> Result<bool, LitSdkError> {
1166        self.contract
1167            .is_permitted_address(self.token_id, user)
1168            .call()
1169            .await
1170            .map_err(|e| LitSdkError::Network(e.to_string()))
1171    }
1172
1173    pub async fn is_permitted_action(&self, ipfs_cid_v0: &str) -> Result<bool, LitSdkError> {
1174        let ipfs_bytes = ipfs_cid_v0_to_bytes(ipfs_cid_v0)?;
1175        self.contract
1176            .is_permitted_action(self.token_id, ipfs_bytes)
1177            .call()
1178            .await
1179            .map_err(|e| LitSdkError::Network(e.to_string()))
1180    }
1181
1182    pub async fn get_permitted_auth_methods(&self) -> Result<Vec<PkpAuthMethod>, LitSdkError> {
1183        let auth_methods = self
1184            .contract
1185            .get_permitted_auth_methods(self.token_id)
1186            .call()
1187            .await
1188            .map_err(|e| LitSdkError::Network(e.to_string()))?;
1189
1190        Ok(auth_methods
1191            .into_iter()
1192            .map(|am| PkpAuthMethod {
1193                auth_method_type: am.auth_method_type,
1194                id: format!("0x{}", hex::encode(am.id.as_ref())),
1195                user_pubkey: format!("0x{}", hex::encode(am.user_pubkey.as_ref())),
1196            })
1197            .collect())
1198    }
1199
1200    pub async fn get_permitted_auth_method_scopes(
1201        &self,
1202        auth_method_type: U256,
1203        auth_method_id_hex: &str,
1204        scope_id: Option<u64>,
1205    ) -> Result<Vec<bool>, LitSdkError> {
1206        let max_scope_id = scope_id.unwrap_or(3);
1207        let id_bytes = parse_hex_bytes(auth_method_id_hex)?;
1208        self.contract
1209            .get_permitted_auth_method_scopes(
1210                self.token_id,
1211                auth_method_type,
1212                id_bytes,
1213                U256::from(max_scope_id),
1214            )
1215            .call()
1216            .await
1217            .map_err(|e| LitSdkError::Network(e.to_string()))
1218    }
1219
1220    pub async fn get_permissions_context(&self) -> Result<PkpPermissionsContext, LitSdkError> {
1221        let (actions, addresses, auth_methods) = futures::try_join!(
1222            self.get_permitted_actions(),
1223            self.get_permitted_addresses(),
1224            self.get_permitted_auth_methods()
1225        )?;
1226
1227        let mut auth_methods_with_scopes = Vec::with_capacity(auth_methods.len());
1228        for am in &auth_methods {
1229            let flags = self
1230                .get_permitted_auth_method_scopes(am.auth_method_type, &am.id, None)
1231                .await?;
1232            let names = ["no-permissions", "sign-anything", "personal-sign"];
1233            let scopes: Vec<String> = flags
1234                .iter()
1235                .enumerate()
1236                .filter_map(|(i, enabled)| {
1237                    (*enabled && i < names.len()).then_some(names[i].to_string())
1238                })
1239                .collect();
1240            auth_methods_with_scopes.push(PkpAuthMethodWithScopes {
1241                auth_method_type: am.auth_method_type,
1242                id: am.id.clone(),
1243                user_pubkey: am.user_pubkey.clone(),
1244                scopes,
1245            });
1246        }
1247
1248        Ok(PkpPermissionsContext {
1249            actions,
1250            addresses,
1251            auth_methods: auth_methods_with_scopes,
1252        })
1253    }
1254
1255    pub async fn add_permitted_auth_method(
1256        &self,
1257        auth_method_type: U256,
1258        auth_method_id_hex: &str,
1259        user_pubkey_hex: &str,
1260        scopes: Vec<String>,
1261    ) -> Result<PaymentTx, LitSdkError> {
1262        let id = parse_hex_bytes(auth_method_id_hex)?;
1263        let user_pubkey = parse_hex_bytes(user_pubkey_hex)?;
1264        let scopes = scopes_to_u256(&scopes)?;
1265
1266        let auth_method = pkp_permissions_contract::AuthMethod {
1267            auth_method_type,
1268            id,
1269            user_pubkey,
1270        };
1271
1272        let call = self
1273            .contract
1274            .add_permitted_auth_method(self.token_id, auth_method, scopes);
1275        let pending = call
1276            .send()
1277            .await
1278            .map_err(|e| LitSdkError::Network(e.to_string()))?;
1279        let hash = *pending;
1280        let receipt = pending
1281            .await
1282            .map_err(|e| LitSdkError::Network(e.to_string()))?
1283            .ok_or_else(|| LitSdkError::Network("addPermittedAuthMethod tx dropped".into()))?;
1284        Ok(PaymentTx { hash, receipt })
1285    }
1286
1287    pub async fn remove_permitted_auth_method_scope(
1288        &self,
1289        auth_method_type: U256,
1290        auth_method_id_hex: &str,
1291        scope_id: U256,
1292    ) -> Result<PaymentTx, LitSdkError> {
1293        let id = parse_hex_bytes(auth_method_id_hex)?;
1294        let call = self.contract.remove_permitted_auth_method_scope(
1295            self.token_id,
1296            auth_method_type,
1297            id,
1298            scope_id,
1299        );
1300        let pending = call
1301            .send()
1302            .await
1303            .map_err(|e| LitSdkError::Network(e.to_string()))?;
1304        let hash = *pending;
1305        let receipt = pending
1306            .await
1307            .map_err(|e| LitSdkError::Network(e.to_string()))?
1308            .ok_or_else(|| {
1309                LitSdkError::Network("removePermittedAuthMethodScope tx dropped".into())
1310            })?;
1311        Ok(PaymentTx { hash, receipt })
1312    }
1313
1314    pub async fn remove_permitted_auth_method(
1315        &self,
1316        auth_method_type: U256,
1317        auth_method_id_hex: &str,
1318    ) -> Result<PaymentTx, LitSdkError> {
1319        let id = parse_hex_bytes(auth_method_id_hex)?;
1320        let call = self
1321            .contract
1322            .remove_permitted_auth_method(self.token_id, auth_method_type, id);
1323        let pending = call
1324            .send()
1325            .await
1326            .map_err(|e| LitSdkError::Network(e.to_string()))?;
1327        let hash = *pending;
1328        let receipt = pending
1329            .await
1330            .map_err(|e| LitSdkError::Network(e.to_string()))?
1331            .ok_or_else(|| LitSdkError::Network("removePermittedAuthMethod tx dropped".into()))?;
1332        Ok(PaymentTx { hash, receipt })
1333    }
1334}