jrpc.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. use crate::rate::Rate;
  2. use crate::symbol_state::SymbolState;
  3. use crate::time::TimestampUs;
  4. use crate::PriceFeedId;
  5. use crate::{api::Channel, price::Price};
  6. use serde::{Deserialize, Serialize};
  7. use std::time::Duration;
  8. #[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)]
  9. #[serde(untagged)]
  10. pub enum JrpcId {
  11. String(String),
  12. Int(i64),
  13. #[default]
  14. Null,
  15. }
  16. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
  17. pub struct PythLazerAgentJrpcV1 {
  18. pub jsonrpc: JsonRpcVersion,
  19. #[serde(flatten)]
  20. pub params: JrpcCall,
  21. #[serde(default)]
  22. pub id: JrpcId,
  23. }
  24. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
  25. #[serde(tag = "method", content = "params")]
  26. #[serde(rename_all = "snake_case")]
  27. pub enum JrpcCall {
  28. PushUpdate(FeedUpdateParams),
  29. PushUpdates(Vec<FeedUpdateParams>),
  30. GetMetadata(GetMetadataParams),
  31. }
  32. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
  33. pub struct FeedUpdateParams {
  34. pub feed_id: PriceFeedId,
  35. pub source_timestamp: TimestampUs,
  36. pub update: UpdateParams,
  37. }
  38. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
  39. #[serde(tag = "type")]
  40. pub enum UpdateParams {
  41. #[serde(rename = "price")]
  42. PriceUpdate {
  43. price: Price,
  44. best_bid_price: Option<Price>,
  45. best_ask_price: Option<Price>,
  46. },
  47. #[serde(rename = "funding_rate")]
  48. FundingRateUpdate {
  49. price: Option<Price>,
  50. rate: Rate,
  51. #[serde(default = "default_funding_rate_interval", with = "humantime_serde")]
  52. funding_rate_interval: Option<Duration>,
  53. },
  54. }
  55. fn default_funding_rate_interval() -> Option<Duration> {
  56. None
  57. }
  58. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
  59. pub struct Filter {
  60. pub name: Option<String>,
  61. pub asset_type: Option<String>,
  62. }
  63. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
  64. pub struct GetMetadataParams {
  65. pub names: Option<Vec<String>>,
  66. pub asset_types: Option<Vec<String>>,
  67. }
  68. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
  69. pub enum JsonRpcVersion {
  70. #[serde(rename = "2.0")]
  71. V2,
  72. }
  73. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
  74. #[serde(untagged)]
  75. pub enum JrpcResponse<T> {
  76. Success(JrpcSuccessResponse<T>),
  77. Error(JrpcErrorResponse),
  78. }
  79. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
  80. pub struct JrpcSuccessResponse<T> {
  81. pub jsonrpc: JsonRpcVersion,
  82. pub result: T,
  83. pub id: JrpcId,
  84. }
  85. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
  86. pub struct JrpcErrorResponse {
  87. pub jsonrpc: JsonRpcVersion,
  88. pub error: JrpcErrorObject,
  89. pub id: JrpcId,
  90. }
  91. #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
  92. pub struct JrpcErrorObject {
  93. pub code: i64,
  94. pub message: String,
  95. #[serde(skip_serializing_if = "Option::is_none")]
  96. pub data: Option<serde_json::Value>,
  97. }
  98. #[derive(Debug, Eq, PartialEq)]
  99. pub enum JrpcError {
  100. ParseError(String),
  101. InternalError(String),
  102. SendUpdateError(FeedUpdateParams),
  103. }
  104. // note: error codes can be found in the rfc https://www.jsonrpc.org/specification#error_object
  105. impl From<JrpcError> for JrpcErrorObject {
  106. fn from(error: JrpcError) -> Self {
  107. match error {
  108. JrpcError::ParseError(error_message) => JrpcErrorObject {
  109. code: -32700,
  110. message: "Parse error".to_string(),
  111. data: Some(error_message.into()),
  112. },
  113. JrpcError::InternalError(error_message) => JrpcErrorObject {
  114. code: -32603,
  115. message: "Internal error".to_string(),
  116. data: Some(error_message.into()),
  117. },
  118. JrpcError::SendUpdateError(feed_update_params) => JrpcErrorObject {
  119. code: -32000,
  120. message: "Internal error".to_string(),
  121. data: Some(serde_json::to_value(feed_update_params).unwrap()),
  122. },
  123. }
  124. }
  125. }
  126. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
  127. pub struct SymbolMetadata {
  128. pub pyth_lazer_id: PriceFeedId,
  129. pub name: String,
  130. pub symbol: String,
  131. pub description: String,
  132. pub asset_type: String,
  133. pub exponent: i16,
  134. pub cmc_id: Option<u32>,
  135. #[serde(default, with = "humantime_serde", alias = "interval")]
  136. pub funding_rate_interval: Option<Duration>,
  137. pub min_publishers: u16,
  138. pub min_channel: Channel,
  139. pub state: SymbolState,
  140. pub hermes_id: Option<String>,
  141. pub quote_currency: Option<String>,
  142. }
  143. #[cfg(test)]
  144. mod tests {
  145. use super::*;
  146. use crate::jrpc::JrpcCall::{GetMetadata, PushUpdate};
  147. #[test]
  148. fn test_push_update_price() {
  149. let json = r#"
  150. {
  151. "jsonrpc": "2.0",
  152. "method": "push_update",
  153. "params": {
  154. "feed_id": 1,
  155. "source_timestamp": 124214124124,
  156. "update": {
  157. "type": "price",
  158. "price": 1234567890,
  159. "best_bid_price": 1234567891,
  160. "best_ask_price": 1234567892
  161. }
  162. },
  163. "id": 1
  164. }
  165. "#;
  166. let expected = PythLazerAgentJrpcV1 {
  167. jsonrpc: JsonRpcVersion::V2,
  168. params: PushUpdate(FeedUpdateParams {
  169. feed_id: PriceFeedId(1),
  170. source_timestamp: TimestampUs::from_micros(124214124124),
  171. update: UpdateParams::PriceUpdate {
  172. price: Price::from_integer(1234567890, 0).unwrap(),
  173. best_bid_price: Some(Price::from_integer(1234567891, 0).unwrap()),
  174. best_ask_price: Some(Price::from_integer(1234567892, 0).unwrap()),
  175. },
  176. }),
  177. id: JrpcId::Int(1),
  178. };
  179. assert_eq!(
  180. serde_json::from_str::<PythLazerAgentJrpcV1>(json).unwrap(),
  181. expected
  182. );
  183. }
  184. #[test]
  185. fn test_push_update_price_string_id() {
  186. let json = r#"
  187. {
  188. "jsonrpc": "2.0",
  189. "method": "push_update",
  190. "params": {
  191. "feed_id": 1,
  192. "source_timestamp": 124214124124,
  193. "update": {
  194. "type": "price",
  195. "price": 1234567890,
  196. "best_bid_price": 1234567891,
  197. "best_ask_price": 1234567892
  198. }
  199. },
  200. "id": "b6bb54a0-ea8d-439d-97a7-3b06befa0e76"
  201. }
  202. "#;
  203. let expected = PythLazerAgentJrpcV1 {
  204. jsonrpc: JsonRpcVersion::V2,
  205. params: PushUpdate(FeedUpdateParams {
  206. feed_id: PriceFeedId(1),
  207. source_timestamp: TimestampUs::from_micros(124214124124),
  208. update: UpdateParams::PriceUpdate {
  209. price: Price::from_integer(1234567890, 0).unwrap(),
  210. best_bid_price: Some(Price::from_integer(1234567891, 0).unwrap()),
  211. best_ask_price: Some(Price::from_integer(1234567892, 0).unwrap()),
  212. },
  213. }),
  214. id: JrpcId::String("b6bb54a0-ea8d-439d-97a7-3b06befa0e76".to_string()),
  215. };
  216. assert_eq!(
  217. serde_json::from_str::<PythLazerAgentJrpcV1>(json).unwrap(),
  218. expected
  219. );
  220. }
  221. #[test]
  222. fn test_push_update_price_null_id() {
  223. let json = r#"
  224. {
  225. "jsonrpc": "2.0",
  226. "method": "push_update",
  227. "params": {
  228. "feed_id": 1,
  229. "source_timestamp": 124214124124,
  230. "update": {
  231. "type": "price",
  232. "price": 1234567890,
  233. "best_bid_price": 1234567891,
  234. "best_ask_price": 1234567892
  235. }
  236. },
  237. "id": null
  238. }
  239. "#;
  240. let expected = PythLazerAgentJrpcV1 {
  241. jsonrpc: JsonRpcVersion::V2,
  242. params: PushUpdate(FeedUpdateParams {
  243. feed_id: PriceFeedId(1),
  244. source_timestamp: TimestampUs::from_micros(124214124124),
  245. update: UpdateParams::PriceUpdate {
  246. price: Price::from_integer(1234567890, 0).unwrap(),
  247. best_bid_price: Some(Price::from_integer(1234567891, 0).unwrap()),
  248. best_ask_price: Some(Price::from_integer(1234567892, 0).unwrap()),
  249. },
  250. }),
  251. id: JrpcId::Null,
  252. };
  253. assert_eq!(
  254. serde_json::from_str::<PythLazerAgentJrpcV1>(json).unwrap(),
  255. expected
  256. );
  257. }
  258. #[test]
  259. fn test_push_update_price_without_id() {
  260. let json = r#"
  261. {
  262. "jsonrpc": "2.0",
  263. "method": "push_update",
  264. "params": {
  265. "feed_id": 1,
  266. "source_timestamp": 745214124124,
  267. "update": {
  268. "type": "price",
  269. "price": 5432,
  270. "best_bid_price": 5432,
  271. "best_ask_price": 5432
  272. }
  273. }
  274. }
  275. "#;
  276. let expected = PythLazerAgentJrpcV1 {
  277. jsonrpc: JsonRpcVersion::V2,
  278. params: PushUpdate(FeedUpdateParams {
  279. feed_id: PriceFeedId(1),
  280. source_timestamp: TimestampUs::from_micros(745214124124),
  281. update: UpdateParams::PriceUpdate {
  282. price: Price::from_integer(5432, 0).unwrap(),
  283. best_bid_price: Some(Price::from_integer(5432, 0).unwrap()),
  284. best_ask_price: Some(Price::from_integer(5432, 0).unwrap()),
  285. },
  286. }),
  287. id: JrpcId::Null,
  288. };
  289. assert_eq!(
  290. serde_json::from_str::<PythLazerAgentJrpcV1>(json).unwrap(),
  291. expected
  292. );
  293. }
  294. #[test]
  295. fn test_push_update_price_without_bid_ask() {
  296. let json = r#"
  297. {
  298. "jsonrpc": "2.0",
  299. "method": "push_update",
  300. "params": {
  301. "feed_id": 1,
  302. "source_timestamp": 124214124124,
  303. "update": {
  304. "type": "price",
  305. "price": 1234567890
  306. }
  307. },
  308. "id": 1
  309. }
  310. "#;
  311. let expected = PythLazerAgentJrpcV1 {
  312. jsonrpc: JsonRpcVersion::V2,
  313. params: PushUpdate(FeedUpdateParams {
  314. feed_id: PriceFeedId(1),
  315. source_timestamp: TimestampUs::from_micros(124214124124),
  316. update: UpdateParams::PriceUpdate {
  317. price: Price::from_integer(1234567890, 0).unwrap(),
  318. best_bid_price: None,
  319. best_ask_price: None,
  320. },
  321. }),
  322. id: JrpcId::Int(1),
  323. };
  324. assert_eq!(
  325. serde_json::from_str::<PythLazerAgentJrpcV1>(json).unwrap(),
  326. expected
  327. );
  328. }
  329. #[test]
  330. fn test_push_update_funding_rate() {
  331. let json = r#"
  332. {
  333. "jsonrpc": "2.0",
  334. "method": "push_update",
  335. "params": {
  336. "feed_id": 1,
  337. "source_timestamp": 124214124124,
  338. "update": {
  339. "type": "funding_rate",
  340. "price": 1234567890,
  341. "rate": 1234567891,
  342. "funding_rate_interval": "8h"
  343. }
  344. },
  345. "id": 1
  346. }
  347. "#;
  348. let expected = PythLazerAgentJrpcV1 {
  349. jsonrpc: JsonRpcVersion::V2,
  350. params: PushUpdate(FeedUpdateParams {
  351. feed_id: PriceFeedId(1),
  352. source_timestamp: TimestampUs::from_micros(124214124124),
  353. update: UpdateParams::FundingRateUpdate {
  354. price: Some(Price::from_integer(1234567890, 0).unwrap()),
  355. rate: Rate::from_integer(1234567891, 0).unwrap(),
  356. funding_rate_interval: Duration::from_secs(28800).into(),
  357. },
  358. }),
  359. id: JrpcId::Int(1),
  360. };
  361. assert_eq!(
  362. serde_json::from_str::<PythLazerAgentJrpcV1>(json).unwrap(),
  363. expected
  364. );
  365. }
  366. #[test]
  367. fn test_push_update_funding_rate_without_price() {
  368. let json = r#"
  369. {
  370. "jsonrpc": "2.0",
  371. "method": "push_update",
  372. "params": {
  373. "feed_id": 1,
  374. "source_timestamp": 124214124124,
  375. "update": {
  376. "type": "funding_rate",
  377. "rate": 1234567891
  378. }
  379. },
  380. "id": 1
  381. }
  382. "#;
  383. let expected = PythLazerAgentJrpcV1 {
  384. jsonrpc: JsonRpcVersion::V2,
  385. params: PushUpdate(FeedUpdateParams {
  386. feed_id: PriceFeedId(1),
  387. source_timestamp: TimestampUs::from_micros(124214124124),
  388. update: UpdateParams::FundingRateUpdate {
  389. price: None,
  390. rate: Rate::from_integer(1234567891, 0).unwrap(),
  391. funding_rate_interval: None,
  392. },
  393. }),
  394. id: JrpcId::Int(1),
  395. };
  396. assert_eq!(
  397. serde_json::from_str::<PythLazerAgentJrpcV1>(json).unwrap(),
  398. expected
  399. );
  400. }
  401. #[test]
  402. fn test_send_get_metadata() {
  403. let json = r#"
  404. {
  405. "jsonrpc": "2.0",
  406. "method": "get_metadata",
  407. "params": {
  408. "names": ["BTC/USD"],
  409. "asset_types": ["crypto"]
  410. },
  411. "id": 1
  412. }
  413. "#;
  414. let expected = PythLazerAgentJrpcV1 {
  415. jsonrpc: JsonRpcVersion::V2,
  416. params: GetMetadata(GetMetadataParams {
  417. names: Some(vec!["BTC/USD".to_string()]),
  418. asset_types: Some(vec!["crypto".to_string()]),
  419. }),
  420. id: JrpcId::Int(1),
  421. };
  422. assert_eq!(
  423. serde_json::from_str::<PythLazerAgentJrpcV1>(json).unwrap(),
  424. expected
  425. );
  426. }
  427. #[test]
  428. fn test_get_metadata_without_filters() {
  429. let json = r#"
  430. {
  431. "jsonrpc": "2.0",
  432. "method": "get_metadata",
  433. "params": {},
  434. "id": 1
  435. }
  436. "#;
  437. let expected = PythLazerAgentJrpcV1 {
  438. jsonrpc: JsonRpcVersion::V2,
  439. params: GetMetadata(GetMetadataParams {
  440. names: None,
  441. asset_types: None,
  442. }),
  443. id: JrpcId::Int(1),
  444. };
  445. assert_eq!(
  446. serde_json::from_str::<PythLazerAgentJrpcV1>(json).unwrap(),
  447. expected
  448. );
  449. }
  450. #[test]
  451. fn test_response_format_error() {
  452. let response = serde_json::from_str::<JrpcErrorResponse>(
  453. r#"
  454. {
  455. "jsonrpc": "2.0",
  456. "id": 2,
  457. "error": {
  458. "message": "Internal error",
  459. "code": -32603
  460. }
  461. }
  462. "#,
  463. )
  464. .unwrap();
  465. assert_eq!(
  466. response,
  467. JrpcErrorResponse {
  468. jsonrpc: JsonRpcVersion::V2,
  469. error: JrpcErrorObject {
  470. code: -32603,
  471. message: "Internal error".to_string(),
  472. data: None,
  473. },
  474. id: JrpcId::Int(2),
  475. }
  476. );
  477. }
  478. #[test]
  479. fn test_response_format_error_string_id() {
  480. let response = serde_json::from_str::<JrpcErrorResponse>(
  481. r#"
  482. {
  483. "jsonrpc": "2.0",
  484. "id": "62b627dc-5599-43dd-b2c2-9c4d30f4fdb4",
  485. "error": {
  486. "message": "Internal error",
  487. "code": -32603
  488. }
  489. }
  490. "#,
  491. )
  492. .unwrap();
  493. assert_eq!(
  494. response,
  495. JrpcErrorResponse {
  496. jsonrpc: JsonRpcVersion::V2,
  497. error: JrpcErrorObject {
  498. code: -32603,
  499. message: "Internal error".to_string(),
  500. data: None,
  501. },
  502. id: JrpcId::String("62b627dc-5599-43dd-b2c2-9c4d30f4fdb4".to_string())
  503. }
  504. );
  505. }
  506. #[test]
  507. pub fn test_response_format_success() {
  508. let response = serde_json::from_str::<JrpcSuccessResponse<String>>(
  509. r#"
  510. {
  511. "jsonrpc": "2.0",
  512. "id": 2,
  513. "result": "success"
  514. }
  515. "#,
  516. )
  517. .unwrap();
  518. assert_eq!(
  519. response,
  520. JrpcSuccessResponse::<String> {
  521. jsonrpc: JsonRpcVersion::V2,
  522. result: "success".to_string(),
  523. id: JrpcId::Int(2),
  524. }
  525. );
  526. }
  527. #[test]
  528. pub fn test_response_format_success_string_id() {
  529. let response = serde_json::from_str::<JrpcSuccessResponse<String>>(
  530. r#"
  531. {
  532. "jsonrpc": "2.0",
  533. "id": "62b627dc-5599-43dd-b2c2-9c4d30f4fdb4",
  534. "result": "success"
  535. }
  536. "#,
  537. )
  538. .unwrap();
  539. assert_eq!(
  540. response,
  541. JrpcSuccessResponse::<String> {
  542. jsonrpc: JsonRpcVersion::V2,
  543. result: "success".to_string(),
  544. id: JrpcId::String("62b627dc-5599-43dd-b2c2-9c4d30f4fdb4".to_string()),
  545. }
  546. );
  547. }
  548. #[test]
  549. pub fn test_parse_response() {
  550. let success_response = serde_json::from_str::<JrpcResponse<String>>(
  551. r#"
  552. {
  553. "jsonrpc": "2.0",
  554. "id": 2,
  555. "result": "success"
  556. }"#,
  557. )
  558. .unwrap();
  559. assert_eq!(
  560. success_response,
  561. JrpcResponse::Success(JrpcSuccessResponse::<String> {
  562. jsonrpc: JsonRpcVersion::V2,
  563. result: "success".to_string(),
  564. id: JrpcId::Int(2),
  565. })
  566. );
  567. let error_response = serde_json::from_str::<JrpcResponse<String>>(
  568. r#"
  569. {
  570. "jsonrpc": "2.0",
  571. "id": 3,
  572. "error": {
  573. "code": -32603,
  574. "message": "Internal error"
  575. }
  576. }"#,
  577. )
  578. .unwrap();
  579. assert_eq!(
  580. error_response,
  581. JrpcResponse::Error(JrpcErrorResponse {
  582. jsonrpc: JsonRpcVersion::V2,
  583. error: JrpcErrorObject {
  584. code: -32603,
  585. message: "Internal error".to_string(),
  586. data: None,
  587. },
  588. id: JrpcId::Int(3),
  589. })
  590. );
  591. }
  592. #[test]
  593. pub fn test_parse_response_string_id() {
  594. let success_response = serde_json::from_str::<JrpcResponse<String>>(
  595. r#"
  596. {
  597. "jsonrpc": "2.0",
  598. "id": "id-2",
  599. "result": "success"
  600. }"#,
  601. )
  602. .unwrap();
  603. assert_eq!(
  604. success_response,
  605. JrpcResponse::Success(JrpcSuccessResponse::<String> {
  606. jsonrpc: JsonRpcVersion::V2,
  607. result: "success".to_string(),
  608. id: JrpcId::String("id-2".to_string()),
  609. })
  610. );
  611. let error_response = serde_json::from_str::<JrpcResponse<String>>(
  612. r#"
  613. {
  614. "jsonrpc": "2.0",
  615. "id": "id-3",
  616. "error": {
  617. "code": -32603,
  618. "message": "Internal error"
  619. }
  620. }"#,
  621. )
  622. .unwrap();
  623. assert_eq!(
  624. error_response,
  625. JrpcResponse::Error(JrpcErrorResponse {
  626. jsonrpc: JsonRpcVersion::V2,
  627. error: JrpcErrorObject {
  628. code: -32603,
  629. message: "Internal error".to_string(),
  630. data: None,
  631. },
  632. id: JrpcId::String("id-3".to_string()),
  633. })
  634. );
  635. }
  636. }