Skip to main content

df_papertrade_core/trade/writes/
submit.rs

1//! What every write goes through: the lane's next nonce from the store, the wallet's signature
2//! checked against the owner, and one post to the relayer that must keep the intent.
3
4use df_hyperliquid_core::client::Http;
5use df_hyperliquid_core::utils::Address;
6
7use crate::account::store::{Slot, Store};
8use crate::account::wallet::{Purpose, SignRequest, Wallet};
9use crate::client::{Accepted, Client, Error};
10use crate::trade::{Intent, Signed};
11use crate::utils::{Lane, nonce};
12
13impl<H: Http + Clone> Client<H> {
14    /// Returns `lane`'s next nonce for `owner` and stores the one after it. An unused lane
15    /// starts at a random point.
16    pub async fn take_nonce(&self, store: &impl Store, owner: Address, lane: Lane) -> Result<u128, Error> {
17        let held = store.get(owner, Slot::Nonce(lane)).await?;
18        let next = match held.as_deref().and_then(|bytes| <[u8; 16]>::try_from(bytes).ok()) {
19            Some(bytes) => u128::from_be_bytes(bytes),
20            None => nonce::first(self.random::<3>()?),
21        };
22        store.put(owner, Slot::Nonce(lane), next.saturating_add(1).to_be_bytes().to_vec(), None).await?;
23        Ok(next)
24    }
25
26    /// Has `wallet` sign `intent` and checks the signature recovers to `owner`.
27    pub(crate) async fn wallet_sign(
28        &self,
29        wallet: &impl Wallet,
30        owner: Address,
31        intent: Intent,
32        purpose: Purpose,
33    ) -> Result<Signed, Error> {
34        if wallet.address() != owner {
35            return Err(Error::WrongWallet { wallet: wallet.address().to_hex(), owner: owner.to_hex() });
36        }
37        if intent.user() != owner {
38            return Err(Error::WrongWallet { wallet: intent.user().to_hex(), owner: owner.to_hex() });
39        }
40        let request = SignRequest { purpose, json: intent.wallet_json(), digest: intent.digest() };
41        let signature = wallet.sign(&request).await?;
42        Signed::recover(intent, signature, owner).map_err(|e| Error::Signature(e.to_string()))
43    }
44
45    /// Posts `signed` once. An answer that does not keep it is an error.
46    pub(crate) async fn submit(&self, signed: &Signed) -> Result<Accepted, Error> {
47        let accepted = self.relayer().submit(signed).await?;
48        if accepted.kept() { Ok(accepted) } else { Err(Error::NotKept(accepted)) }
49    }
50}
51
52#[cfg(test)]
53#[path = "tests/submit.rs"]
54mod tests;