Skip to content

Commit 0132c10

Browse files
committed
[xrpl_sdk_ws] Add support for book subscriptions
1 parent e75ba41 commit 0132c10

File tree

6 files changed

+129
-36
lines changed

6 files changed

+129
-36
lines changed

xrpl_api/src/api/book_offers.rs

+5-36
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,8 @@
11
use serde::{Deserialize, Serialize};
2-
use xrpl_types::{Currency, Offer};
2+
use xrpl_types::{Currency, CurrencySpec, Offer};
33

44
use crate::Request;
55

6-
#[derive(Clone, Serialize)]
7-
pub struct CurrencyParams {
8-
pub currency: String, // TODO: hm, consider name.
9-
#[serde(skip_serializing_if = "Option::is_none")]
10-
pub issuer: Option<String>,
11-
}
12-
13-
impl Default for CurrencyParams {
14-
fn default() -> Self {
15-
Self {
16-
currency: "XRP".to_string(),
17-
issuer: None,
18-
}
19-
}
20-
}
21-
22-
impl CurrencyParams {
23-
pub fn from_currency(c: &Currency) -> Self {
24-
match c {
25-
Currency::Xrp => CurrencyParams {
26-
currency: "XRP".to_owned(),
27-
issuer: None,
28-
},
29-
Currency::Issued { name, issuer } => CurrencyParams {
30-
currency: name.clone(),
31-
issuer: Some(issuer.clone()),
32-
},
33-
}
34-
}
35-
}
36-
376
/// - https://xrpl.org/book_offers.html
387
#[derive(Default, Clone, Serialize)]
398
pub struct BookOffersRequest {
@@ -45,8 +14,8 @@ pub struct BookOffersRequest {
4514
limit: Option<u32>,
4615
#[serde(skip_serializing_if = "Option::is_none")]
4716
taker: Option<String>,
48-
taker_gets: CurrencyParams,
49-
taker_pays: CurrencyParams,
17+
taker_gets: CurrencySpec,
18+
taker_pays: CurrencySpec,
5019
}
5120

5221
impl Request for BookOffersRequest {
@@ -60,8 +29,8 @@ impl Request for BookOffersRequest {
6029
impl BookOffersRequest {
6130
pub fn new(taker_gets: &Currency, taker_pays: &Currency) -> Self {
6231
Self {
63-
taker_gets: CurrencyParams::from_currency(taker_gets),
64-
taker_pays: CurrencyParams::from_currency(taker_pays),
32+
taker_gets: CurrencySpec::from_currency(taker_gets),
33+
taker_pays: CurrencySpec::from_currency(taker_pays),
6534
..Default::default()
6635
}
6736
}

xrpl_api/src/api/subscribe.rs

+15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
use crate::Request;
77
use serde::{Deserialize, Serialize};
8+
use xrpl_types::Book;
89

910
#[derive(Default, Clone, Serialize)]
1011
pub struct SubscribeRequest {
@@ -13,6 +14,8 @@ pub struct SubscribeRequest {
1314
#[serde(skip_serializing_if = "Option::is_none")]
1415
accounts: Option<Vec<String>>,
1516
#[serde(skip_serializing_if = "Option::is_none")]
17+
books: Option<Vec<Book>>,
18+
#[serde(skip_serializing_if = "Option::is_none")]
1619
url: Option<String>,
1720
#[serde(skip_serializing_if = "Option::is_none")]
1821
url_username: Option<String>,
@@ -33,6 +36,9 @@ impl SubscribeRequest {
3336
Self::default()
3437
}
3538

39+
/// The ledger stream only sends ledgerClosed messages when the consensus
40+
/// process declares a new validated ledger. The message identifies the
41+
/// ledger and provides some information about its contents.
3642
pub fn streams(streams: &[&str]) -> Self {
3743
let streams = streams.iter().map(|s| s.to_string()).collect();
3844
Self {
@@ -48,6 +54,15 @@ impl SubscribeRequest {
4854
..Default::default()
4955
}
5056
}
57+
58+
/// When you subscribe to one or more order books with the books field, you
59+
/// get back any transactions that affect those order books.
60+
pub fn books(books: &[Book]) -> Self {
61+
Self {
62+
books: Some(books.to_vec()),
63+
..Default::default()
64+
}
65+
}
5166
}
5267

5368
#[derive(Debug, Deserialize)]

xrpl_sdk_ws/src/client_tests.rs

+30
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ mod tests {
44
use crate::client::DEFAULT_WS_URL;
55
use futures_util::StreamExt;
66
use xrpl_api::{AccountInfoRequest, SubscribeRequest};
7+
use xrpl_types::Book;
8+
use xrpl_types::Currency;
79

810
#[tokio::test]
911
async fn client_can_request_account_info() {
@@ -40,4 +42,32 @@ mod tests {
4042
count += 1;
4143
}
4244
}
45+
46+
#[tokio::test]
47+
async fn client_can_subscribe_to_books() {
48+
let mut client = Client::connect(DEFAULT_WS_URL)
49+
.await
50+
.expect("cannot connect");
51+
52+
let book = Book::new(
53+
&Currency::Xrp,
54+
&Currency::issued("USD", "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"),
55+
"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
56+
)
57+
.snapshot(true);
58+
59+
let req = SubscribeRequest::books(&[book]);
60+
61+
client.call(req).await.expect("cannot subscribe");
62+
63+
let mut count = 0;
64+
65+
while let Some(msg) = client.messages.next().await {
66+
if count > 5 {
67+
break;
68+
}
69+
dbg!(&msg);
70+
count += 1;
71+
}
72+
}
4373
}

xrpl_types/src/book.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use crate::{Currency, CurrencySpec};
2+
use serde::Serialize;
3+
4+
/// A book on the ledger.
5+
#[derive(Default, Debug, Clone, Serialize)]
6+
pub struct Book {
7+
/// Specification of which currency the account taking the Offer would pay.
8+
pub taker_gets: CurrencySpec,
9+
/// Specification of which currency the account taking the Offer would receive.
10+
pub taker_pays: CurrencySpec,
11+
/// Unique account address to use as a perspective for viewing offers, in the XRP Ledger's base58 format.
12+
pub taker: String,
13+
/// If true, return the current state of the order book once when you subscribe before sending updates.
14+
/// The default is false.
15+
pub snapshot: Option<bool>,
16+
/// If true, return both sides of the order book. The default is false.
17+
pub both: Option<bool>,
18+
}
19+
20+
impl Book {
21+
pub fn new(taker_gets: &Currency, taker_pays: &Currency, taker: &str) -> Self {
22+
Self {
23+
taker_gets: CurrencySpec::from_currency(taker_gets),
24+
taker_pays: CurrencySpec::from_currency(taker_pays),
25+
taker: taker.to_owned(),
26+
snapshot: None,
27+
both: None,
28+
}
29+
}
30+
31+
pub fn snapshot(self, snapshot: bool) -> Self {
32+
Self {
33+
snapshot: Some(snapshot),
34+
..self
35+
}
36+
}
37+
38+
pub fn both(self, both: bool) -> Self {
39+
Self {
40+
both: Some(both),
41+
..self
42+
}
43+
}
44+
}

xrpl_types/src/currency.rs

+33
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,36 @@ impl Currency {
3737
!self.is_xrp()
3838
}
3939
}
40+
41+
// #TODO implement from/into.
42+
/// Currency specification for books.
43+
#[derive(Debug, Clone, Serialize)]
44+
pub struct CurrencySpec {
45+
pub currency: String, // TODO: hm, consider name.
46+
#[serde(skip_serializing_if = "Option::is_none")]
47+
pub issuer: Option<String>,
48+
}
49+
50+
impl Default for CurrencySpec {
51+
fn default() -> Self {
52+
Self {
53+
currency: "XRP".to_string(),
54+
issuer: None,
55+
}
56+
}
57+
}
58+
59+
impl CurrencySpec {
60+
pub fn from_currency(c: &Currency) -> Self {
61+
match c {
62+
Currency::Xrp => CurrencySpec {
63+
currency: "XRP".to_owned(),
64+
issuer: None,
65+
},
66+
Currency::Issued { name, issuer } => CurrencySpec {
67+
currency: name.clone(),
68+
issuer: Some(issuer.clone()),
69+
},
70+
}
71+
}
72+
}

xrpl_types/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
44
pub mod account;
55
pub mod amount;
6+
pub mod book;
67
pub mod currency;
78
pub mod offer;
89
pub mod transaction;
910

1011
pub use account::*;
1112
pub use amount::*;
13+
pub use book::*;
1214
pub use currency::*;
1315
pub use offer::*;
1416
pub use transaction::*;

0 commit comments

Comments
 (0)