elements_rpc/
lib.rs

1mod image;
2
3use elements::{Address, BlockHash};
4use serde::{de::DeserializeOwned, Deserialize};
5
6#[jsonrpc_client::api]
7pub trait Rpc {
8    // blockchain
9    async fn getbestblockhash(&self) -> BlockHash;
10    async fn getblockcount(&self) -> u64;
11    // control
12    // generating
13    async fn generatetoaddress(
14        &self,
15        nblocks: usize,
16        address: &str,
17        maxtries: Option<u64>,
18    ) -> Vec<BlockHash>;
19    async fn sendtoaddress(
20        &self,
21        address: &str,
22        amount: f64,
23        comment: Option<&str>,
24        comment_to: Option<&str>,
25        subtractfeefromamount: Option<bool>,
26        replaceable: Option<bool>,
27        conf_target: Option<u64>,
28        estimate_mode: Option<&str>,
29        avoid_reuse: Option<bool>,
30        assetlabel: Option<&str>,
31        ignoreblindfail: Option<bool>,
32        fee_rate: Option<f64>,
33        verbose: Option<bool>,
34    ) -> SendToAddress;
35    // mining
36    // network
37    // raw transactions
38    // signer
39    // util
40    async fn estimatesmartfee(&self, conf_target: u64, estimate_mode: &str) -> EstimateSmartFee;
41    // wallet
42    async fn createwallet(
43        &self,
44        wallet_name: &str,
45        disable_private_keys: Option<bool>,
46        blank: Option<bool>,
47        passphrase: Option<&str>,
48        avoid_reuse: Option<bool>,
49        descriptors: Option<bool>,
50        load_on_startup: Option<bool>,
51        external_signer: Option<bool>,
52    ) -> CreateWallet;
53    async fn getaddressinfo(&self, address: &str) -> AddressInfo;
54    async fn getnewaddress(&self, label: Option<&str>, address_type: Option<&str>) -> String;
55    async fn getwalletinfo(&self) -> WalletInfo;
56    // zmq
57}
58
59#[derive(Debug, Deserialize)]
60pub struct CreateWallet {
61    name: String,
62    warning: String,
63}
64
65#[derive(Debug, Deserialize)]
66pub struct EstimateSmartFee {
67    feerate: Option<f64>,
68    errors: Option<Vec<String>>,
69    blocks: u64,
70}
71
72#[derive(Debug, Deserialize)]
73pub struct AddressInfo {
74    address: String,
75    #[serde(rename = "scriptPubKey")]
76    script_pubkey: String,
77    ismine: bool,
78    iswatchonly: bool,
79    solvable: bool,
80    desc: Option<String>,
81    parent_desc: Option<String>,
82    isscript: bool,
83    ischange: bool,
84    iswitness: bool,
85    witness_version: Option<u64>,
86    witness_program: Option<String>,
87    script: Option<String>,
88    hex: Option<String>,
89    pubkeys: Option<Vec<String>>,
90    sigsrequired: Option<u64>,
91    pubkey: Option<String>,
92    // embedded:
93    iscompressed: Option<bool>,
94    confidential_key: String,
95    unconfidential: String,
96    confidential: String,
97    timestamp: u64,
98    hdkeypath: Option<String>,
99    hdseedid: Option<String>,
100    hdmasterfingerprint: Option<String>,
101    labels: Vec<String>,
102}
103
104#[derive(Debug, Deserialize)]
105pub struct SendToAddress {
106    txid: String,
107    fee_reason: String,
108}
109
110#[derive(Debug, Deserialize)]
111pub struct WalletInfo {
112    walletname: String,
113    walletversion: u64,
114    format: String,
115    balance: f64,
116    unconfirmed_balance: f64,
117    immature_balance: f64,
118    txcount: u64,
119    keypoololdest: u64,
120    keypoolsize: u64,
121    keypoolsize_hd_internal: u64,
122    unlocked_until: Option<u64>,
123    paytxfee: f64,
124    hdseedid: Option<String>,
125    private_keys_enabled: bool,
126    avoid_reuse: bool,
127    // scanning: Scanning,
128    descriptors: bool,
129}
130
131#[jsonrpc_client::implement(Rpc)]
132pub struct Client {
133    inner: reqwest::Client,
134    base_url: reqwest::Url,
135}
136
137impl Client {
138    pub fn new(base_url: String) -> Result<Self, url::ParseError> {
139        Ok(Self {
140            inner: reqwest::Client::new(),
141            base_url: base_url.parse()?,
142        })
143    }
144}
145
146#[cfg(test)]
147mod test {
148    use testcontainers::clients;
149
150    use super::*;
151
152    #[tokio::test]
153    async fn simple() {
154        let docker = clients::Cli::default();
155        let container = docker.run(image::Elementsd::default());
156        let port = container.get_host_port_ipv4(18444);
157
158        let client = Client::new(format!("http://user:pass@127.0.0.1:{}", port)).unwrap();
159
160        let wallet_name = "asdf".to_string();
161        let wallet = client
162            .createwallet(&wallet_name, None, None, None, None, None, None, None)
163            .await
164            .unwrap();
165        dbg!(&wallet);
166        assert_eq!(wallet.name, wallet_name);
167        assert!(wallet.warning.is_empty());
168
169        let address = client.getnewaddress(None, None).await.unwrap();
170        dbg!(address);
171        let address = client.getnewaddress(None, Some("legacy")).await.unwrap();
172        dbg!(address);
173        let address = client
174            .getnewaddress(None, Some("p2sh-segwit"))
175            .await
176            .unwrap();
177        dbg!(address);
178        let address = client.getnewaddress(None, Some("bech32")).await.unwrap();
179        dbg!(&address);
180
181        let hash = client.getbestblockhash().await.unwrap();
182        dbg!(hash);
183
184        let count = client.getblockcount().await.unwrap();
185        dbg!(count);
186        assert_eq!(count, 0);
187
188        let nblocks = 501;
189        let hashes = client
190            .generatetoaddress(nblocks, &address, None)
191            .await
192            .unwrap();
193        dbg!(&hashes);
194        assert_eq!(hashes.len(), nblocks);
195
196        let info = client.getwalletinfo().await.unwrap();
197        dbg!(&info);
198
199        let sent = client
200            .sendtoaddress(
201                &address,
202                0.1,
203                None,
204                None,
205                None,
206                None,
207                None,
208                None,
209                None,
210                None,
211                None,
212                None,
213                Some(true),
214            )
215            .await
216            .unwrap();
217        dbg!(&sent);
218
219        let info = client.getaddressinfo(&address).await.unwrap();
220        dbg!(&info);
221
222        let temp = client.estimatesmartfee(1, "conservative").await.unwrap();
223        dbg!(&temp);
224    }
225}