use crate::account::signer::Signature;
use crate::client::body;
use crate::client::network::Network;
use crate::utils::sign::eip712::{Domain, Field, TypedStruct};
use crate::utils::sign::msgpack::Map;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NonceKey {
Nonce,
Time,
}
impl NonceKey {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
NonceKey::Nonce => "nonce",
NonceKey::Time => "time",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UserSigned<'a> {
kind: &'static str,
primary_type: &'static str,
fields: Vec<(&'static str, Field<'a>)>,
nonce_key: NonceKey,
nonce: u64,
}
impl<'a> UserSigned<'a> {
#[must_use]
pub const fn new(kind: &'static str, primary_type: &'static str, nonce_key: NonceKey, nonce: u64) -> Self {
UserSigned { kind, primary_type, fields: Vec::new(), nonce_key, nonce }
}
#[must_use]
pub fn field(mut self, name: &'static str, value: Field<'a>) -> Self {
self.fields.push((name, value));
self
}
#[must_use]
pub const fn kind(&self) -> &'static str {
self.kind
}
#[must_use]
pub const fn nonce(&self) -> u64 {
self.nonce
}
#[must_use]
pub fn action(&self, network: Network) -> Map {
let mut map = Map::new()
.put("type", self.kind)
.put("signatureChainId", network.signature_chain_id_hex())
.put("hyperliquidChain", network.hyperliquid_chain());
for (name, value) in &self.fields {
map = map.put(*name, value.to_wire());
}
map.put(self.nonce_key.as_str(), self.nonce)
}
#[must_use]
pub fn wire(&self, network: Network) -> String {
self.action(network).to_json().compact()
}
#[must_use]
pub fn typed(&self, network: Network) -> TypedStruct<'a> {
let mut data =
TypedStruct::new(self.primary_type).field("hyperliquidChain", Field::String(network.hyperliquid_chain()));
for &(name, value) in &self.fields {
data = data.field(name, value);
}
data.field(self.nonce_key.as_str(), Field::Uint64(self.nonce))
}
#[must_use]
pub fn digest(&self, network: Network) -> [u8; 32] {
self.typed(network).digest(&Domain::user_signed(network))
}
#[must_use]
pub fn wallet_json(&self, network: Network) -> String {
self.typed(network).wallet_json(&Domain::user_signed(network))
}
#[must_use]
pub fn body(&self, signature: &Signature, network: Network) -> String {
body::user_signed(&self.action(network), self.nonce, signature)
}
}
pub trait UserAction {
fn user_signed(&self) -> UserSigned<'_>;
#[must_use]
fn nonce(&self) -> u64 {
self.user_signed().nonce()
}
#[must_use]
fn action(&self, network: Network) -> Map {
self.user_signed().action(network)
}
#[must_use]
fn wire(&self, network: Network) -> String {
self.user_signed().wire(network)
}
#[must_use]
fn typed(&self, network: Network) -> TypedStruct<'_> {
self.user_signed().typed(network)
}
#[must_use]
fn digest(&self, network: Network) -> [u8; 32] {
self.user_signed().digest(network)
}
#[must_use]
fn wallet_json(&self, network: Network) -> String {
self.user_signed().wallet_json(network)
}
#[must_use]
fn body(&self, signature: &Signature, network: Network) -> String {
self.user_signed().body(signature, network)
}
}