Skip to main content

df_hyperliquid_core/earn/lending/
action.rs

1//! `borrowLend`: `{type, operation, token, amount}`, keys in that order.
2
3use serde::Deserialize;
4
5use crate::account::{ActionKind, Signers};
6use crate::utils::Dec;
7use crate::utils::sign::{L1Action, Map, Value};
8
9/// What a `borrowLend` does to a reserve, as the action and the ledger row spell it.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)]
11#[serde(rename_all = "lowercase")]
12pub enum Operation {
13    Supply,
14    Withdraw,
15    Borrow,
16    Repay,
17}
18
19impl Operation {
20    pub const ALL: [Operation; 4] = [Operation::Supply, Operation::Withdraw, Operation::Borrow, Operation::Repay];
21
22    #[must_use]
23    pub const fn wire(self) -> &'static str {
24        match self {
25            Operation::Supply => "supply",
26            Operation::Withdraw => "withdraw",
27            Operation::Borrow => "borrow",
28            Operation::Repay => "repay",
29        }
30    }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
34pub enum BadLend {
35    #[error("{0} is not an amount above zero")]
36    NotPositive(Dec),
37}
38
39/// `borrowLend`. `token` is the spot token's index from the caller's
40/// `spotMeta`, a number, not its `NAME:0x..`: USDC is 0 on both networks
41/// (`crate::client::USDC_INDEX`), HYPE 150 on mainnet and 1105 on testnet. An amount
42/// of `None` goes as `null`: all of it, a whole withdraw or a whole repay.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct BorrowLend {
45    operation: Operation,
46    token: u32,
47    amount: Option<Dec>,
48}
49
50impl BorrowLend {
51    /// L1: an agent may sign it for its master.
52    pub const KEY: Signers = ActionKind::BorrowLend.signers();
53
54    /// `amount` in the token, already cut to its `weiDecimals`; it goes on
55    /// the wire with no trailing zeros.
56    pub fn new(operation: Operation, token: u32, amount: Dec) -> Result<BorrowLend, BadLend> {
57        match amount.is_positive() {
58            true => Ok(BorrowLend { operation, token, amount: Some(amount) }),
59            false => Err(BadLend::NotPositive(amount)),
60        }
61    }
62
63    pub fn supply(token: u32, amount: Dec) -> Result<BorrowLend, BadLend> {
64        BorrowLend::new(Operation::Supply, token, amount)
65    }
66
67    pub fn withdraw(token: u32, amount: Dec) -> Result<BorrowLend, BadLend> {
68        BorrowLend::new(Operation::Withdraw, token, amount)
69    }
70
71    pub fn borrow(token: u32, amount: Dec) -> Result<BorrowLend, BadLend> {
72        BorrowLend::new(Operation::Borrow, token, amount)
73    }
74
75    pub fn repay(token: u32, amount: Dec) -> Result<BorrowLend, BadLend> {
76        BorrowLend::new(Operation::Repay, token, amount)
77    }
78
79    /// Everything supplied, with what it earned.
80    #[must_use]
81    pub const fn withdraw_all(token: u32) -> BorrowLend {
82        BorrowLend { operation: Operation::Withdraw, token, amount: None }
83    }
84
85    /// Everything owed, interest included.
86    #[must_use]
87    pub const fn repay_all(token: u32) -> BorrowLend {
88        BorrowLend { operation: Operation::Repay, token, amount: None }
89    }
90
91    #[must_use]
92    pub const fn operation(&self) -> Operation {
93        self.operation
94    }
95
96    #[must_use]
97    pub const fn token(&self) -> u32 {
98        self.token
99    }
100
101    #[must_use]
102    pub const fn amount(&self) -> Option<Dec> {
103        self.amount
104    }
105}
106
107impl L1Action for BorrowLend {
108    fn action(&self) -> Map {
109        let amount = match self.amount {
110            Some(amount) => Value::from(amount.to_wire()),
111            None => Value::Nil,
112        };
113        Map::new()
114            .put("type", "borrowLend")
115            .put("operation", self.operation.wire())
116            .put("token", self.token)
117            .put("amount", amount)
118    }
119}