Skip to main content

df_hyperliquid_core/earn/vaults/
transfer.rs

1//! `vaultTransfer`: `{type, vaultAddress, isDeposit, usd}`, keys in that order, `usd` in
2//! millionths. HLP's address and a vault's lockup differ between the networks.
3
4use crate::account::{ActionKind, Signers};
5use crate::client::Network;
6use crate::utils::sign::{L1Action, Map};
7use crate::utils::{Address, BadUnits, Dec, USDC_DECIMALS, exact_units};
8
9const fn address(text: &str) -> Address {
10    match Address::parse_const(text) {
11        Some(address) => address,
12        None => Address::ZERO,
13    }
14}
15
16/// The protocol vault on mainnet, which any account can deposit into.
17pub const MAINNET_HLP: Address = address("0xdfc24b077bc1425ad1dea75bcb6f8158e10df303");
18
19/// Testnet's protocol vault, not in the docs; its `vaultDetails` names it
20/// "Hyperliquidity Provider (HLP)".
21pub const TESTNET_HLP: Address = address("0xa15099a30bbf2e68942d6f4c43d70d04faeab0a0");
22
23/// How long a deposit into HLP is locked, on both networks: four days, as
24/// every single-deposit follower of either shows.
25pub const HLP_LOCKUP_MS: u64 = 4 * 86_400_000;
26
27/// How long a deposit into any other vault on mainnet is locked: a day.
28pub const MAINNET_VAULT_LOCKUP_MS: u64 = 86_400_000;
29
30/// Testnet's lockup for any other vault: twenty seconds, as the single-deposit
31/// followers of its user vaults show.
32pub const TESTNET_VAULT_LOCKUP_MS: u64 = 20_000;
33
34/// The network's HLP.
35#[must_use]
36pub const fn hlp(network: Network) -> Address {
37    match network {
38        Network::Mainnet => MAINNET_HLP,
39        Network::Testnet => TESTNET_HLP,
40    }
41}
42
43/// The lockup a deposit into `vault` on `network` starts.
44#[must_use]
45pub fn lockup_ms(network: Network, vault: Address) -> u64 {
46    match (vault == hlp(network), network) {
47        (true, _) => HLP_LOCKUP_MS,
48        (false, Network::Mainnet) => MAINNET_VAULT_LOCKUP_MS,
49        (false, Network::Testnet) => TESTNET_VAULT_LOCKUP_MS,
50    }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct VaultTransfer {
55    vault: Address,
56    is_deposit: bool,
57    usd: u64,
58}
59
60impl VaultTransfer {
61    /// L1: an agent may sign it for its master.
62    pub const KEY: Signers = ActionKind::VaultTransfer.signers();
63
64    /// USDC from the perp balance into `vault`, at most six places: refused
65    /// rather than rounded when finer.
66    pub fn deposit(vault: Address, usdc: Dec) -> Result<VaultTransfer, BadUnits> {
67        Ok(VaultTransfer { vault, is_deposit: true, usd: exact_units(usdc, USDC_DECIMALS)? })
68    }
69
70    /// USDC out of `vault` once its lockup has passed. A whole equity runs
71    /// past six places; cut it first (`crate::utils::cut_units`) or take
72    /// `maxWithdrawable` from [`crate::earn::vaults::vault_details`].
73    pub fn withdraw(vault: Address, usdc: Dec) -> Result<VaultTransfer, BadUnits> {
74        Ok(VaultTransfer { vault, is_deposit: false, usd: exact_units(usdc, USDC_DECIMALS)? })
75    }
76
77    /// From a count of millionths already made.
78    #[must_use]
79    pub const fn from_micros(vault: Address, is_deposit: bool, usd: u64) -> VaultTransfer {
80        VaultTransfer { vault, is_deposit, usd }
81    }
82
83    #[must_use]
84    pub const fn usd(&self) -> u64 {
85        self.usd
86    }
87
88    #[must_use]
89    pub const fn vault(&self) -> Address {
90        self.vault
91    }
92}
93
94impl L1Action for VaultTransfer {
95    fn action(&self) -> Map {
96        Map::new()
97            .put("type", "vaultTransfer")
98            .put("vaultAddress", self.vault.to_hex())
99            .put("isDeposit", self.is_deposit)
100            .put("usd", self.usd)
101    }
102}