mwc_web3/api/
personal.rs

1//! `Personal` namespace
2
3use crate::{
4    api::Namespace,
5    helpers::{self, CallFuture},
6    types::{Address, RawTransaction, TransactionRequest, H256},
7    Transport,
8};
9
10/// `Personal` namespace
11#[derive(Debug, Clone)]
12pub struct Personal<T> {
13    transport: T,
14}
15
16impl<T: Transport> Namespace<T> for Personal<T> {
17    fn new(transport: T) -> Self
18    where
19        Self: Sized,
20    {
21        Personal { transport }
22    }
23
24    fn transport(&self) -> &T {
25        &self.transport
26    }
27}
28
29impl<T: Transport> Personal<T> {
30    /// Returns a list of available accounts.
31    pub fn list_accounts(&self) -> CallFuture<Vec<Address>, T::Out> {
32        CallFuture::new(self.transport.execute("personal_listAccounts", vec![]))
33    }
34
35    /// Creates a new account and protects it with given password.
36    /// Returns the address of created account.
37    pub fn new_account(&self, password: &str) -> CallFuture<Address, T::Out> {
38        let password = helpers::serialize(&password);
39        CallFuture::new(self.transport.execute("personal_newAccount", vec![password]))
40    }
41
42    /// Unlocks the account with given password for some period of time (or single transaction).
43    /// Returns `true` if the call was successful.
44    pub fn unlock_account(&self, address: Address, password: &str, duration: Option<u16>) -> CallFuture<bool, T::Out> {
45        let address = helpers::serialize(&address);
46        let password = helpers::serialize(&password);
47        let duration = helpers::serialize(&duration);
48        CallFuture::new(
49            self.transport
50                .execute("personal_unlockAccount", vec![address, password, duration]),
51        )
52    }
53
54    /// Sends a transaction from locked account.
55    /// Returns transaction hash.
56    pub fn send_transaction(&self, transaction: TransactionRequest, password: &str) -> CallFuture<H256, T::Out> {
57        let transaction = helpers::serialize(&transaction);
58        let password = helpers::serialize(&password);
59        CallFuture::new(
60            self.transport
61                .execute("personal_sendTransaction", vec![transaction, password]),
62        )
63    }
64
65    /// Signs a transaction without dispatching it to the network.
66    /// The account does not need to be unlocked to make this call, and will not be left unlocked after.
67    /// Returns a signed transaction in raw bytes along with it's details.
68    pub fn sign_transaction(
69        &self,
70        transaction: TransactionRequest,
71        password: &str,
72    ) -> CallFuture<RawTransaction, T::Out> {
73        let transaction = helpers::serialize(&transaction);
74        let password = helpers::serialize(&password);
75        CallFuture::new(
76            self.transport
77                .execute("personal_signTransaction", vec![transaction, password]),
78        )
79    }
80
81    /// Imports a raw key and protects it with the given password.
82    /// Returns the address of created account.
83    pub fn import_raw_key(&self, private_key: &[u8; 32], password: &str) -> CallFuture<Address, T::Out> {
84        let private_key = hex::encode(private_key);
85        let private_key = helpers::serialize(&private_key);
86        let password = helpers::serialize(&password);
87
88        CallFuture::new(
89            self.transport
90                .execute("personal_importRawKey", vec![private_key, password]),
91        )
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::Personal;
98    use crate::{
99        api::Namespace,
100        rpc::Value,
101        types::{Address, RawTransaction, TransactionRequest},
102    };
103    use hex_literal::hex;
104
105    const EXAMPLE_TX: &str = r#"{
106    "raw": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675",
107    "tx": {
108      "hash": "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b",
109      "nonce": "0x0",
110      "blockHash": "0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b",
111      "blockNumber": "0x15df",
112      "transactionIndex": "0x1",
113      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
114      "to": "0x853f43d8a49eeb85d32cf465507dd71d507100c1",
115      "value": "0x7f110",
116      "gas": "0x7f110",
117      "gasPrice": "0x09184e72a000",
118      "input": "0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360"
119    }
120  }"#;
121
122    rpc_test! (
123      Personal:list_accounts => "personal_listAccounts";
124      Value::Array(vec![Value::String("0x0000000000000000000000000000000000000123".into())]) => vec![Address::from_low_u64_be(0x123)]
125    );
126
127    rpc_test! (
128      Personal:new_account, "hunter2" => "personal_newAccount", vec![r#""hunter2""#];
129      Value::String("0x0000000000000000000000000000000000000123".into()) => Address::from_low_u64_be(0x123)
130    );
131
132    rpc_test! (
133      Personal:unlock_account, Address::from_low_u64_be(0x123), "hunter2", None
134      =>
135      "personal_unlockAccount", vec![r#""0x0000000000000000000000000000000000000123""#, r#""hunter2""#, r#"null"#];
136      Value::Bool(true) => true
137    );
138
139    rpc_test! (
140      Personal:send_transaction, TransactionRequest {
141        from: Address::from_low_u64_be(0x123), to: Some(Address::from_low_u64_be(0x123)),
142        gas: None, gas_price: Some(0x1.into()),
143        value: Some(0x1.into()), data: None,
144        nonce: None, condition: None,
145      }, "hunter2"
146      =>
147      "personal_sendTransaction", vec![r#"{"from":"0x0000000000000000000000000000000000000123","gasPrice":"0x1","to":"0x0000000000000000000000000000000000000123","value":"0x1"}"#, r#""hunter2""#];
148      Value::String("0x0000000000000000000000000000000000000000000000000000000000000123".into()) => Address::from_low_u64_be(0x123)
149    );
150
151    rpc_test! (
152      Personal:sign_transaction, TransactionRequest {
153        from: hex!("407d73d8a49eeb85d32cf465507dd71d507100c1").into(),
154        to: Some(hex!("853f43d8a49eeb85d32cf465507dd71d507100c1").into()),
155        gas: Some(0x7f110.into()),
156        gas_price: Some(0x09184e72a000u64.into()),
157        value: Some(0x7f110.into()),
158        data: Some(hex!("603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360").into()),
159        nonce: Some(0x0.into()),
160        condition: None,
161      }, "hunter2"
162      =>
163      "personal_signTransaction", vec![r#"{"data":"0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360","from":"0x407d73d8a49eeb85d32cf465507dd71d507100c1","gas":"0x7f110","gasPrice":"0x9184e72a000","nonce":"0x0","to":"0x853f43d8a49eeb85d32cf465507dd71d507100c1","value":"0x7f110"}"#, r#""hunter2""#];
164      ::serde_json::from_str(EXAMPLE_TX).unwrap()
165      => ::serde_json::from_str::<RawTransaction>(EXAMPLE_TX).unwrap()
166    );
167
168    rpc_test! {
169      Personal:import_raw_key, &[0u8; 32], "hunter2" =>
170      "personal_importRawKey", vec![r#""0000000000000000000000000000000000000000000000000000000000000000""#, r#""hunter2""#];
171      Value::String("0x0000000000000000000000000000000000000123".into()) => Address::from_low_u64_be(0x123)
172    }
173}