Skip to main content

percolator_sdk/
client.rs

1use solana_client::rpc_client::RpcClient;
2use solana_sdk::{
3    pubkey::Pubkey,
4    signature::{Keypair, Signer},
5    transaction::Transaction,
6};
7use crate::instructions::{build_deposit_ix, build_withdraw_ix, build_execute_trade_ix, build_keeper_crank_ix};
8use crate::accounts::{fetch_risk_engine, RiskEngine, Account};
9
10/// A high-level client for interacting with the Percolator risk engine via RPC.
11/// 
12/// `PercolatorClient` simplifies the process of sending transactions and fetching
13/// protocol state by encapsulating the RPC client and instruction building.
14pub struct PercolatorClient {
15    /// The underlying Solana RPC client.
16    pub client: RpcClient,
17    /// The public key of the Percolator program (or its wrapper).
18    pub program_id: Pubkey,
19    /// The public key of the RiskEngine state account.
20    pub engine_state: Pubkey,
21}
22
23impl PercolatorClient {
24    /// Creates a new `PercolatorClient`.
25    pub fn new(rpc_url: &str, program_id: Pubkey, engine_state: Pubkey) -> Self {
26        Self {
27            client: RpcClient::new(rpc_url.to_string()),
28            program_id,
29            engine_state,
30        }
31    }
32
33    /// Fetches the current `RiskEngine` state from the blockchain.
34    pub fn get_engine_state(&self) -> Result<RiskEngine, Box<dyn std::error::Error>> {
35        fetch_risk_engine(&self.client, &self.engine_state)
36    }
37
38    /// Fetches a specific `Account` from the engine by its index.
39    pub fn get_account(&self, index: usize) -> Result<Option<Account>, Box<dyn std::error::Error>> {
40        let engine = self.get_engine_state()?;
41        if index < crate::percolator::MAX_ACCOUNTS && engine.is_used(index) {
42            Ok(Some(engine.accounts[index].clone()))
43        } else {
44            Ok(None)
45        }
46    }
47
48    /// Sends a `Deposit` transaction.
49    pub fn send_deposit(
50        &self,
51        payer: &Keypair,
52        account_index: u64,
53        amount: u128,
54        now_slot: u64,
55    ) -> Result<String, Box<dyn std::error::Error>> {
56        let ix = build_deposit_ix(
57            self.program_id,
58            self.engine_state,
59            payer.pubkey(),
60            account_index,
61            amount,
62            now_slot,
63        );
64
65        let recent_blockhash = self.client.get_latest_blockhash()?;
66        let tx = Transaction::new_signed_with_payer(
67            &[ix],
68            Some(&payer.pubkey()),
69            &[payer],
70            recent_blockhash,
71        );
72
73        let sig = self.client.send_and_confirm_transaction(&tx)?;
74        Ok(sig.to_string())
75    }
76
77    /// Sends a `Withdraw` transaction.
78    pub fn send_withdraw(
79        &self,
80        payer: &Keypair,
81        account_index: u64,
82        amount: u128,
83        now_slot: u64,
84        oracle_price: u64,
85    ) -> Result<String, Box<dyn std::error::Error>> {
86        let ix = build_withdraw_ix(
87            self.program_id,
88            self.engine_state,
89            payer.pubkey(),
90            account_index,
91            amount,
92            now_slot,
93            oracle_price,
94        );
95
96        let recent_blockhash = self.client.get_latest_blockhash()?;
97        let tx = Transaction::new_signed_with_payer(
98            &[ix],
99            Some(&payer.pubkey()),
100            &[payer],
101            recent_blockhash,
102        );
103
104        let sig = self.client.send_and_confirm_transaction(&tx)?;
105        Ok(sig.to_string())
106    }
107
108    /// Sends an `ExecuteTrade` transaction.
109    pub fn send_trade(
110        &self,
111        payer: &Keypair,
112        lp_index: u64,
113        user_index: u64,
114        now_slot: u64,
115        oracle_price: u64,
116        size: i128,
117    ) -> Result<String, Box<dyn std::error::Error>> {
118        let ix = build_execute_trade_ix(
119            self.program_id,
120            self.engine_state,
121            payer.pubkey(),
122            lp_index,
123            user_index,
124            now_slot,
125            oracle_price,
126            size,
127        );
128
129        let recent_blockhash = self.client.get_latest_blockhash()?;
130        let tx = Transaction::new_signed_with_payer(
131            &[ix],
132            Some(&payer.pubkey()),
133            &[payer],
134            recent_blockhash,
135        );
136
137        let sig = self.client.send_and_confirm_transaction(&tx)?;
138        Ok(sig.to_string())
139    }
140
141    /// Sends a `KeeperCrank` transaction.
142    pub fn send_crank(
143        &self,
144        payer: &Keypair,
145        caller_index: u64,
146        now_slot: u64,
147        oracle_price: u64,
148        funding_rate: i64,
149        allow_panic: bool,
150    ) -> Result<String, Box<dyn std::error::Error>> {
151        let ix = build_keeper_crank_ix(
152            self.program_id,
153            self.engine_state,
154            payer.pubkey(),
155            caller_index,
156            now_slot,
157            oracle_price,
158            funding_rate,
159            allow_panic,
160        );
161
162        let recent_blockhash = self.client.get_latest_blockhash()?;
163        let tx = Transaction::new_signed_with_payer(
164            &[ix],
165            Some(&payer.pubkey()),
166            &[payer],
167            recent_blockhash,
168        );
169
170        let sig = self.client.send_and_confirm_transaction(&tx)?;
171        Ok(sig.to_string())
172    }
173}