use crate::account::signers::ActionKind;
use crate::client::Network;
use crate::transfer::amount::Amount;
use crate::transfer::keeping::Keeping;
use crate::transfer::plan::Key;
use crate::utils::Address;
use crate::utils::sign::{Field, L1Action, Map, NonceKey, UserAction, UserSigned};
pub const SPOT_DEX: &str = "spot";
pub const PERP_DEX: &str = "";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SendAsset {
destination: String,
source_dex: String,
destination_dex: String,
token: String,
amount: Amount,
from_sub_account: String,
nonce: u64,
}
impl SendAsset {
pub const KEY: Key = ActionKind::SendAsset.signers();
#[must_use]
pub fn new(
destination: Address,
source_dex: &str,
destination_dex: &str,
token: &str,
amount: Amount,
nonce: u64,
) -> SendAsset {
SendAsset {
destination: destination.to_hex(),
source_dex: source_dex.to_owned(),
destination_dex: destination_dex.to_owned(),
token: token.to_owned(),
amount,
from_sub_account: String::new(),
nonce,
}
}
#[must_use]
pub fn usdc(
network: Network,
destination: Address,
source_dex: &str,
destination_dex: &str,
amount: Amount,
nonce: u64,
) -> SendAsset {
SendAsset::new(destination, source_dex, destination_dex, network.usdc().wire, amount, nonce)
}
#[must_use]
pub fn from_sub_account(mut self, sub_account: Address) -> SendAsset {
self.from_sub_account = sub_account.to_hex();
self
}
#[must_use]
pub const fn amount(&self) -> &Amount {
&self.amount
}
}
impl UserAction for SendAsset {
fn user_signed(&self) -> UserSigned<'_> {
UserSigned::new("sendAsset", "HyperliquidTransaction:SendAsset", NonceKey::Nonce, self.nonce)
.field("destination", Field::String(&self.destination))
.field("sourceDex", Field::String(&self.source_dex))
.field("destinationDex", Field::String(&self.destination_dex))
.field("token", Field::String(&self.token))
.field("amount", Field::String(self.amount.as_str()))
.field("fromSubAccount", Field::String(&self.from_sub_account))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpotSend {
destination: String,
token: String,
amount: Amount,
time: u64,
}
impl SpotSend {
pub const KEY: Key = ActionKind::SpotSend.signers();
#[must_use]
pub fn new(destination: Address, token: &str, amount: Amount, time: u64) -> SpotSend {
SpotSend { destination: destination.to_hex(), token: token.to_owned(), amount, time }
}
}
impl UserAction for SpotSend {
fn user_signed(&self) -> UserSigned<'_> {
UserSigned::new("spotSend", "HyperliquidTransaction:SpotSend", NonceKey::Time, self.time)
.field("destination", Field::String(&self.destination))
.field("token", Field::String(&self.token))
.field("amount", Field::String(self.amount.as_str()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UsdSend {
destination: String,
amount: Amount,
time: u64,
}
impl UsdSend {
pub const KEY: Key = ActionKind::UsdSend.signers();
#[must_use]
pub fn new(destination: Address, amount: Amount, time: u64) -> UsdSend {
UsdSend { destination: destination.to_hex(), amount, time }
}
}
impl UserAction for UsdSend {
fn user_signed(&self) -> UserSigned<'_> {
UserSigned::new("usdSend", "HyperliquidTransaction:UsdSend", NonceKey::Time, self.time)
.field("destination", Field::String(&self.destination))
.field("amount", Field::String(self.amount.as_str()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UsdClassTransfer {
amount: Amount,
to_perp: bool,
nonce: u64,
}
impl UsdClassTransfer {
pub const KEY: Key = ActionKind::UsdClassTransfer.signers();
#[must_use]
pub const fn new(amount: Amount, to_perp: bool, nonce: u64) -> UsdClassTransfer {
UsdClassTransfer { amount, to_perp, nonce }
}
}
impl UserAction for UsdClassTransfer {
fn user_signed(&self) -> UserSigned<'_> {
UserSigned::new("usdClassTransfer", "HyperliquidTransaction:UsdClassTransfer", NonceKey::Nonce, self.nonce)
.field("amount", Field::String(self.amount.as_str()))
.field("toPerp", Field::Bool(self.to_perp))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Withdraw3 {
destination: String,
amount: Amount,
time: u64,
}
impl Withdraw3 {
pub const KEY: Key = ActionKind::Withdraw3.signers();
#[must_use]
pub fn new(destination: Address, amount: Amount, time: u64) -> Withdraw3 {
Withdraw3 { destination: destination.to_hex(), amount, time }
}
#[must_use]
pub const fn amount(&self) -> &Amount {
&self.amount
}
}
impl UserAction for Withdraw3 {
fn user_signed(&self) -> UserSigned<'_> {
UserSigned::new("withdraw3", "HyperliquidTransaction:Withdraw", NonceKey::Time, self.time)
.field("destination", Field::String(&self.destination))
.field("amount", Field::String(self.amount.as_str()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SpotOut {
SpotSend(SpotSend),
SendAsset(SendAsset),
}
impl SpotOut {
pub const KEY: Key = SpotSend::KEY;
#[must_use]
pub fn new(destination: Address, token: &str, amount: Amount, keeping: Keeping, nonce: u64) -> SpotOut {
match keeping {
Keeping::Apart => SpotOut::SpotSend(SpotSend::new(destination, token, amount, nonce)),
Keeping::Pooled | Keeping::Unknown => {
SpotOut::SendAsset(SendAsset::new(destination, SPOT_DEX, SPOT_DEX, token, amount, nonce))
}
}
}
}
impl UserAction for SpotOut {
fn user_signed(&self) -> UserSigned<'_> {
match self {
SpotOut::SpotSend(send) => send.user_signed(),
SpotOut::SendAsset(send) => send.user_signed(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AgentSendAsset {
account: Address,
source_dex: String,
destination_dex: String,
token: String,
amount: Amount,
nonce: u64,
}
impl AgentSendAsset {
pub const KEY: Key = ActionKind::AgentSendAsset.signers();
#[must_use]
pub fn new(
account: Address,
source_dex: &str,
destination_dex: &str,
token: &str,
amount: Amount,
nonce: u64,
) -> AgentSendAsset {
AgentSendAsset {
account,
source_dex: source_dex.to_owned(),
destination_dex: destination_dex.to_owned(),
token: token.to_owned(),
amount,
nonce,
}
}
#[must_use]
pub const fn nonce(&self) -> u64 {
self.nonce
}
#[must_use]
pub const fn amount(&self) -> &Amount {
&self.amount
}
}
impl L1Action for AgentSendAsset {
fn action(&self) -> Map {
Map::new()
.put("type", "agentSendAsset")
.put("destination", self.account.to_hex())
.put("sourceDex", self.source_dex.clone())
.put("destinationDex", self.destination_dex.clone())
.put("token", self.token.clone())
.put("amount", self.amount.as_str().to_owned())
.put("fromSubAccount", "")
.put("nonce", self.nonce)
}
}
#[cfg(test)]
#[path = "tests/actions.rs"]
mod tests;