sol.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. use std::convert::TryFrom;
  2. use std::str::FromStr;
  3. use async_native_tls::TlsConnector;
  4. use async_std::sync::{Arc, Mutex};
  5. use async_trait::async_trait;
  6. use futures::{SinkExt, StreamExt};
  7. use log::{debug, error, warn};
  8. use rand::rngs::OsRng;
  9. use serde::Serialize;
  10. use serde_json::{json, Value};
  11. use solana_client::{blockhash_query::BlockhashQuery, rpc_client::RpcClient};
  12. use solana_sdk::{
  13. native_token::lamports_to_sol, pubkey::Pubkey, signature::Signer, signer::keypair::Keypair,
  14. system_instruction, transaction::Transaction,
  15. };
  16. use tungstenite::Message;
  17. use crate::rpc::{jsonrpc, jsonrpc::JsonResult, websockets};
  18. use crate::serial::{deserialize, serialize, Decodable, Encodable};
  19. use crate::{Error, Result};
  20. use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
  21. #[derive(Serialize)]
  22. struct SubscribeParams {
  23. encoding: Value,
  24. commitment: Value,
  25. }
  26. pub struct SolClient {
  27. keypair: Keypair,
  28. // Subscriptions vector of pubkey
  29. subscriptions: Arc<Mutex<Vec<Pubkey>>>,
  30. notify_channel: (
  31. async_channel::Sender<TokenNotification>,
  32. async_channel::Receiver<TokenNotification>,
  33. ),
  34. rpc_server: &'static str,
  35. wss_server: &'static str,
  36. }
  37. impl SolClient {
  38. pub async fn new(keypair: Vec<u8>, network: &str) -> Result<Arc<Self>> {
  39. let keypair: Keypair = deserialize(&keypair)?;
  40. let notify_channel = async_channel::unbounded();
  41. let (rpc_server, wss_server) = match network {
  42. "mainnet" => (
  43. "https://api.mainnet-beta.solana.com",
  44. "wss://api.devnet.solana.com",
  45. ),
  46. "devnet" => (
  47. "https://api.devnet.solana.com",
  48. "wss://api.devnet.solana.com",
  49. ),
  50. "testnet" => (
  51. "https://api.testnet.solana.com",
  52. "wss://api.testnet.solana.com",
  53. ),
  54. "localhost" => ("http://localhost:8899", "ws://localhost:8900"),
  55. _ => return Err(Error::NotSupportedNetwork),
  56. };
  57. Ok(Arc::new(Self {
  58. keypair,
  59. subscriptions: Arc::new(Mutex::new(Vec::new())),
  60. notify_channel,
  61. rpc_server,
  62. wss_server,
  63. }))
  64. }
  65. // TODO: Make this function more robust. Currently we just call it
  66. // and put it in the background. This means no errors are actually
  67. // handled, and it just fails silently.
  68. async fn handle_subscribe_request(
  69. self: Arc<Self>,
  70. keypair: Keypair,
  71. is_token: bool,
  72. ) -> Result<()> {
  73. debug!(target: "SOL BRIDGE", "handle_subscribe_request()");
  74. // Check if we're already subscribed
  75. if self.subscriptions.lock().await.contains(&keypair.pubkey()) {
  76. return Ok(());
  77. }
  78. let rpc = RpcClient::new(self.rpc_server.to_string());
  79. // Fetch the current balance.
  80. let prev_balance = if !is_token {
  81. rpc.get_balance(&keypair.pubkey())
  82. .map_err(|err| SolFailed::from(err))?
  83. } else {
  84. // TODO: SPL Token balance
  85. 0
  86. };
  87. let mut cur_balance = prev_balance;
  88. let mut decimals: Option<u64> = None;
  89. let mut mint: Option<&str> = None;
  90. // WebSocket connection
  91. let builder = native_tls::TlsConnector::builder();
  92. let tls = TlsConnector::from(builder);
  93. let (mut stream, _) = websockets::connect(self.wss_server, tls).await?;
  94. // Subscription request build
  95. let sub_params = SubscribeParams {
  96. encoding: json!("jsonParsed"),
  97. commitment: json!("finalized"),
  98. };
  99. let subscription = jsonrpc::request(
  100. json!("accountSubscribe"),
  101. json!([json!(keypair.pubkey().to_string()), json!(sub_params)]),
  102. );
  103. debug!(target: "SOLANA RPC", "--> {}", serde_json::to_string(&subscription)?);
  104. stream
  105. .send(Message::text(serde_json::to_string(&subscription)?))
  106. .await?;
  107. // Declare params here for longer variable lifetime.
  108. let params: Value;
  109. // Subscription ID used for unsubscribing later.
  110. let mut sub_id: i64 = 0;
  111. loop {
  112. let message = stream.next().await.ok_or_else(|| Error::TungsteniteError)?;
  113. let message = message.unwrap();
  114. debug!(target: "SOLANA SUBSCRIPTION", "<-- {}", message.clone().into_text()?);
  115. match serde_json::from_slice(&message.into_data())? {
  116. JsonResult::Resp(r) => {
  117. // ACK
  118. debug!(target: "SOLANA RPC", "<-- {}", serde_json::to_string(&r)?);
  119. self.subscriptions.lock().await.push(keypair.pubkey());
  120. sub_id = r.result.as_i64().unwrap();
  121. }
  122. JsonResult::Err(e) => {
  123. debug!(target: "SOLANA RPC", "<-- {}", serde_json::to_string(&e)?);
  124. // TODO: Try removing pubkey from subscriptions here?
  125. return Err(Error::JsonRpcError(e.error.message.to_string()));
  126. }
  127. JsonResult::Notif(n) => {
  128. // Account updated
  129. debug!(target: "SOLANA RPC", "Got WebSocket notification");
  130. params = n.params["result"]["value"].clone();
  131. if is_token {
  132. cur_balance = params["data"]["info"]["tokenAmount"]["amount"]
  133. .as_u64()
  134. .unwrap();
  135. decimals = Some(
  136. params["data"]["info"]["tokenAmount"]["decimals"]
  137. .as_u64()
  138. .unwrap(),
  139. );
  140. mint = Some(params["data"]["info"]["mint"].as_str().unwrap());
  141. } else {
  142. cur_balance = params["lamports"].as_u64().unwrap();
  143. decimals = None;
  144. mint = None;
  145. }
  146. break;
  147. }
  148. }
  149. }
  150. // I miss goto/defer.
  151. let index = self
  152. .subscriptions
  153. .lock()
  154. .await
  155. .iter()
  156. .position(|p| p == &keypair.pubkey());
  157. if let Some(ind) = index {
  158. debug!("Removing subscription from list");
  159. self.subscriptions.lock().await.remove(ind);
  160. }
  161. let unsubscription = jsonrpc::request(json!("accountUnsubscribe"), json!([sub_id]));
  162. stream
  163. .send(Message::text(serde_json::to_string(&unsubscription)?))
  164. .await?;
  165. if cur_balance - prev_balance <= 0 {
  166. error!("Current balance is not positive");
  167. return Err(Error::ServicesError("Current balance is not positive"));
  168. }
  169. if is_token {
  170. debug!(target: "SOL BRIDGE", "Received {} {:?} tokens",
  171. (cur_balance - prev_balance) * decimals.unwrap(), mint.unwrap());
  172. self.send_tok_to_main_wallet(mint.unwrap(), cur_balance, keypair)
  173. } else {
  174. debug!(target: "SOL BRIDGE", "Received {} SOL", lamports_to_sol(cur_balance - prev_balance));
  175. self.send_sol_to_main_wallet(cur_balance, &keypair)
  176. }
  177. }
  178. // TODO
  179. fn send_tok_to_main_wallet(
  180. self: Arc<Self>,
  181. mint: &str,
  182. amount: u64,
  183. keypair: Keypair,
  184. ) -> Result<()> {
  185. debug!(target: "SOL BRIDGE", "Sending tokens to main wallet");
  186. Ok(())
  187. }
  188. fn send_sol_to_main_wallet(self: Arc<Self>, amount: u64, keypair: &Keypair) -> Result<()> {
  189. debug!(target: "SOL BRIDGE", "Sending {} SOL to main wallet", lamports_to_sol(amount));
  190. let rpc = RpcClient::new(self.rpc_server.to_string());
  191. let fee = rpc
  192. .get_fees()
  193. .unwrap()
  194. .fee_calculator
  195. .lamports_per_signature;
  196. if fee >= amount {
  197. warn!(target: "SOL BRIDGE", "Insufficient funds on {:?} to send tx", &keypair.pubkey());
  198. return Ok(());
  199. }
  200. let amnt_to_transfer = amount - fee;
  201. let ix = system_instruction::transfer(
  202. &keypair.pubkey(),
  203. &self.keypair.pubkey(),
  204. amnt_to_transfer,
  205. );
  206. let mut tx = Transaction::new_with_payer(&[ix], Some(&keypair.pubkey()));
  207. let bhq = BlockhashQuery::default();
  208. match bhq.get_blockhash_and_fee_calculator(&rpc, rpc.commitment()) {
  209. Err(_) => panic!("Couldn't connect to RPC"),
  210. Ok(v) => tx.sign(&[keypair], v.0),
  211. }
  212. let signature = rpc.send_and_confirm_transaction(&tx);
  213. debug!(target: "SOL BRIDGE", "Sent to main wallet: {}", signature.unwrap());
  214. Ok(())
  215. }
  216. }
  217. #[async_trait]
  218. impl NetworkClient for SolClient {
  219. async fn subscribe(self: Arc<Self>) -> Result<TokenSubscribtion> {
  220. let keypair = Keypair::generate(&mut OsRng);
  221. let public_key = keypair.pubkey().to_string();
  222. let secret_key = serialize(&keypair);
  223. let self2 = self.clone();
  224. // TODO: true/false depending on is_token
  225. smol::spawn(self2.handle_subscribe_request(keypair, false)).detach();
  226. Ok(TokenSubscribtion {
  227. secret_key,
  228. public_key,
  229. })
  230. }
  231. // in solana case private key it's the same as keypair
  232. async fn subscribe_with_keypair(
  233. self: Arc<Self>,
  234. private_key: Vec<u8>,
  235. _public_key: Vec<u8>,
  236. ) -> Result<String> {
  237. let keypair: Keypair = deserialize(&private_key)?;
  238. let public_key = keypair.pubkey().to_string();
  239. let self2 = self.clone();
  240. // TODO: true/false depending on is_token
  241. smol::spawn(self2.handle_subscribe_request(keypair, false)).detach();
  242. Ok(public_key)
  243. }
  244. async fn get_notifier(self: Arc<Self>) -> Result<async_channel::Receiver<TokenNotification>> {
  245. Ok(self.notify_channel.1.clone())
  246. }
  247. async fn send(self: Arc<Self>, address: Vec<u8>, amount: u64) -> Result<()> {
  248. let rpc = RpcClient::new(self.rpc_server.to_string());
  249. let address: Pubkey = deserialize(&address)?;
  250. let instruction = system_instruction::transfer(&self.keypair.pubkey(), &address, amount);
  251. let mut tx = Transaction::new_with_payer(&[instruction], Some(&self.keypair.pubkey()));
  252. let bhq = BlockhashQuery::default();
  253. match bhq.get_blockhash_and_fee_calculator(&rpc, rpc.commitment()) {
  254. Err(_) => panic!("Couldn't connect to RPC"),
  255. Ok(v) => tx.sign(&[&self.keypair], v.0),
  256. }
  257. let _signature = rpc
  258. .send_and_confirm_transaction(&tx)
  259. .map_err(|err| SolFailed::from(err))?;
  260. Ok(())
  261. }
  262. }
  263. /// Derive an associated token address from given owner and mint
  264. pub fn get_associated_token_account(owner: &Pubkey, mint: &Pubkey) -> (Pubkey, u8) {
  265. let associated_token =
  266. Pubkey::from_str("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL").unwrap();
  267. Pubkey::find_program_address(
  268. &[
  269. &owner.to_bytes(),
  270. &spl_token::id().to_bytes(),
  271. &mint.to_bytes(),
  272. ],
  273. &associated_token,
  274. )
  275. }
  276. /// Check if given account is a valid token mint
  277. pub fn account_is_initialized_mint(rpc_server: String, mint: &Pubkey) -> bool {
  278. let rpc = RpcClient::new(rpc_server);
  279. match rpc.get_token_supply(mint) {
  280. Ok(_) => return true,
  281. Err(_) => return false,
  282. }
  283. }
  284. impl Encodable for Keypair {
  285. fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
  286. let key: Vec<u8> = self.to_bytes().to_vec();
  287. let len = key.encode(s)?;
  288. Ok(len)
  289. }
  290. }
  291. impl Decodable for Keypair {
  292. fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
  293. let key: Vec<u8> = Decodable::decode(&mut d)?;
  294. let key = Keypair::from_bytes(key.as_slice()).map_err(|_| {
  295. crate::Error::from(SolFailed::DecodeAndEncodeError(
  296. "load keypair from slice".into(),
  297. ))
  298. })?;
  299. Ok(key)
  300. }
  301. }
  302. impl Encodable for Pubkey {
  303. fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
  304. let key = self.to_string();
  305. let len = key.encode(s)?;
  306. Ok(len)
  307. }
  308. }
  309. impl Decodable for Pubkey {
  310. fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
  311. let key: String = Decodable::decode(&mut d)?;
  312. let key = Pubkey::try_from(key.as_str()).map_err(|_| {
  313. crate::Error::from(SolFailed::DecodeAndEncodeError(
  314. "load public key from slice".into(),
  315. ))
  316. })?;
  317. Ok(key)
  318. }
  319. }
  320. #[derive(Debug)]
  321. pub enum SolFailed {
  322. NotEnoughValue(u64),
  323. BadSolAddress(String),
  324. DecodeAndEncodeError(String),
  325. WebSocketError(String),
  326. SolClientError(String),
  327. ParseError(String),
  328. SolError(String),
  329. }
  330. impl std::error::Error for SolFailed {}
  331. impl std::fmt::Display for SolFailed {
  332. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  333. match self {
  334. SolFailed::NotEnoughValue(i) => {
  335. write!(f, "There is no enough value {}", i)
  336. }
  337. SolFailed::BadSolAddress(ref err) => {
  338. write!(f, "Bad Sol Address: {}", err)
  339. }
  340. SolFailed::DecodeAndEncodeError(ref err) => {
  341. write!(f, "Decode and decode keys error: {}", err)
  342. }
  343. SolFailed::WebSocketError(i) => {
  344. write!(f, "WebSocket Error: {}", i)
  345. }
  346. SolFailed::ParseError(i) => {
  347. write!(f, "Parse Error: {}", i)
  348. }
  349. SolFailed::SolClientError(i) => {
  350. write!(f, "Solana Client Error: {}", i)
  351. }
  352. SolFailed::SolError(i) => {
  353. write!(f, "SolFailed: {}", i)
  354. }
  355. }
  356. }
  357. }
  358. impl From<solana_sdk::pubkey::ParsePubkeyError> for SolFailed {
  359. fn from(err: solana_sdk::pubkey::ParsePubkeyError) -> SolFailed {
  360. SolFailed::ParseError(err.to_string())
  361. }
  362. }
  363. impl From<tungstenite::Error> for SolFailed {
  364. fn from(err: tungstenite::Error) -> SolFailed {
  365. SolFailed::WebSocketError(err.to_string())
  366. }
  367. }
  368. impl From<solana_client::client_error::ClientError> for SolFailed {
  369. fn from(err: solana_client::client_error::ClientError) -> SolFailed {
  370. SolFailed::SolError(err.to_string())
  371. }
  372. }
  373. impl From<crate::error::Error> for SolFailed {
  374. fn from(err: crate::error::Error) -> SolFailed {
  375. SolFailed::SolError(err.to_string())
  376. }
  377. }
  378. pub type SolResult<T> = std::result::Result<T, SolFailed>;