use df_hyperliquid_core::client::Http;
use df_hyperliquid_core::utils::Address;
use zeroize::Zeroizing;
use super::{KeyState, SessionRecord, key_state};
use crate::account::Account;
use crate::account::session_key::{self, RENEW_BEFORE_MS, SESSION_TTL_MS, SessionKey};
use crate::account::store::{Slot, Store};
use crate::account::wallet::{Purpose, Wallet};
use crate::client::{Client, Error};
use crate::trade::Intent;
use crate::utils::Lane;
impl<H: Http + Clone> Client<H> {
pub async fn session(&self, store: &impl Store, owner: Address) -> Result<Option<SessionRecord>, Error> {
let bytes = store.get(owner, Slot::Session).await?.map(Zeroizing::new);
Ok(bytes.as_deref().and_then(|bytes| SessionRecord::from_bytes(bytes)))
}
async fn keep(&self, store: &impl Store, owner: Address, record: &SessionRecord) -> Result<(), Error> {
let ttl = record.held_until_ms.saturating_sub(self.now_ms());
Ok(store.put(owner, Slot::Session, record.to_bytes().to_vec(), Some(ttl)).await?)
}
pub async fn key_state(
&self,
store: &impl Store,
owner: Address,
account: Option<&Account>,
) -> Result<KeyState, Error> {
Ok(key_state(self.session(store, owner).await?.as_ref(), account, self.now_ms()))
}
pub async fn new_session(&self, store: &impl Store, owner: Address) -> Result<SessionRecord, Error> {
let now = self.now_ms();
if let Some(record) = self.session(store, owner).await?
&& record.held_until_ms > now.saturating_add(RENEW_BEFORE_MS)
{
return Ok(record);
}
let record = SessionRecord {
secret: Zeroizing::new(self.random::<32>()?),
held_until_ms: now.saturating_add(SESSION_TTL_MS),
registered_at_ms: None,
};
record.address()?;
self.keep(store, owner, &record).await?;
Ok(record)
}
pub async fn register(&self, store: &impl Store, wallet: &impl Wallet) -> Result<SessionRecord, Error> {
let owner = wallet.address();
let mut record = self.new_session(store, owner).await?;
let nonce = self.take_nonce(store, owner, Lane::Exchange).await?;
let registration =
session_key::registration(owner, record.address()?, record.held_until_ms, nonce, self.now_ms())?;
let signed = self
.wallet_sign(wallet, owner, Intent::RegisterSessionKey(registration), Purpose::RegisterSessionKey)
.await?;
self.submit(&signed).await?;
record.registered_at_ms = Some(self.now_ms());
self.keep(store, owner, &record).await?;
Ok(record)
}
pub async fn enable(
&self,
store: &impl Store,
wallet: &impl Wallet,
account: Option<&Account>,
) -> Result<KeyState, Error> {
if self.key_state(store, wallet.address(), account).await? == KeyState::Ready {
return Ok(KeyState::Ready);
}
self.register(store, wallet).await?;
Ok(KeyState::Ready)
}
pub async fn session_key(
&self,
store: &impl Store,
owner: Address,
account: Option<&Account>,
) -> Result<SessionKey, Error> {
let record = self.session(store, owner).await?;
match key_state(record.as_ref(), account, self.now_ms()) {
KeyState::Ready => record.ok_or(Error::NeedsKey(KeyState::Missing))?.signer(owner),
other => Err(Error::NeedsKey(other)),
}
}
}