1use crate::{
4 api::Namespace,
5 helpers::{self, CallFuture},
6 transports::ic_http_client::CallOptions,
7 types::{Address, Bytes, RawTransaction, TransactionRequest, H256, H520},
8 Transport,
9};
10
11#[derive(Debug, Clone)]
13pub struct Personal<T> {
14 transport: T,
15}
16
17impl<T: Transport> Namespace<T> for Personal<T> {
18 fn new(transport: T) -> Self
19 where
20 Self: Sized,
21 {
22 Personal { transport }
23 }
24
25 fn transport(&self) -> &T {
26 &self.transport
27 }
28}
29
30impl<T: Transport> Personal<T> {
31 pub fn list_accounts(&self, options: CallOptions) -> CallFuture<Vec<Address>, T::Out> {
33 CallFuture::new(self.transport.execute("personal_listAccounts", vec![], options))
34 }
35
36 pub fn new_account(&self, password: &str, options: CallOptions) -> CallFuture<Address, T::Out> {
39 let password = helpers::serialize(&password);
40 CallFuture::new(self.transport.execute("personal_newAccount", vec![password], options))
41 }
42
43 pub fn unlock_account(
46 &self,
47 address: Address,
48 password: &str,
49 duration: Option<u16>,
50 options: CallOptions,
51 ) -> CallFuture<bool, T::Out> {
52 let address = helpers::serialize(&address);
53 let password = helpers::serialize(&password);
54 let duration = helpers::serialize(&duration);
55 CallFuture::new(
56 self.transport
57 .execute("personal_unlockAccount", vec![address, password, duration], options),
58 )
59 }
60
61 pub fn send_transaction(
64 &self,
65 transaction: TransactionRequest,
66 password: &str,
67 options: CallOptions,
68 ) -> CallFuture<H256, T::Out> {
69 let transaction = helpers::serialize(&transaction);
70 let password = helpers::serialize(&password);
71 CallFuture::new(
72 self.transport
73 .execute("personal_sendTransaction", vec![transaction, password], options),
74 )
75 }
76
77 pub fn sign(
82 &self,
83 data: Bytes,
84 account: Address,
85 password: &str,
86 options: CallOptions,
87 ) -> CallFuture<H520, T::Out> {
88 let data = helpers::serialize(&data);
89 let address = helpers::serialize(&account);
90 let password = helpers::serialize(&password);
91 CallFuture::new(
92 self.transport
93 .execute("personal_sign", vec![data, address, password], options),
94 )
95 }
96
97 pub fn sign_transaction(
101 &self,
102 transaction: TransactionRequest,
103 password: &str,
104 options: CallOptions,
105 ) -> CallFuture<RawTransaction, T::Out> {
106 let transaction = helpers::serialize(&transaction);
107 let password = helpers::serialize(&password);
108 CallFuture::new(
109 self.transport
110 .execute("personal_signTransaction", vec![transaction, password], options),
111 )
112 }
113
114 pub fn import_raw_key(
117 &self,
118 private_key: &[u8; 32],
119 password: &str,
120 options: CallOptions,
121 ) -> CallFuture<Address, T::Out> {
122 let private_key = hex::encode(private_key);
123 let private_key = helpers::serialize(&private_key);
124 let password = helpers::serialize(&password);
125
126 CallFuture::new(
127 self.transport
128 .execute("personal_importRawKey", vec![private_key, password], options),
129 )
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::Personal;
136 use crate::{
137 api::Namespace,
138 rpc::Value,
139 transports::ic_http_client::CallOptions,
140 types::{Address, Bytes, RawTransaction, TransactionRequest, H160, H520},
141 };
142 use hex_literal::hex;
143
144 const EXAMPLE_TX: &str = r#"{
145 "raw": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675",
146 "tx": {
147 "hash": "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b",
148 "nonce": "0x0",
149 "blockHash": "0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b",
150 "blockNumber": "0x15df",
151 "transactionIndex": "0x1",
152 "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
153 "to": "0x853f43d8a49eeb85d32cf465507dd71d507100c1",
154 "value": "0x7f110",
155 "gas": "0x7f110",
156 "gasPrice": "0x09184e72a000",
157 "input": "0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360"
158 }
159 }"#;
160
161 rpc_test! (
162 Personal:list_accounts,CallOptions::default() => "personal_listAccounts",Vec::<String>::new();
163 Value::Array(vec![Value::String("0x0000000000000000000000000000000000000123".into())]) => vec![Address::from_low_u64_be(0x123)]
164 );
165
166 rpc_test! (
167 Personal:new_account, "hunter2",CallOptions::default() => "personal_newAccount", vec![r#""hunter2""#];
168 Value::String("0x0000000000000000000000000000000000000123".into()) => Address::from_low_u64_be(0x123)
169 );
170
171 rpc_test! (
172 Personal:unlock_account, Address::from_low_u64_be(0x123), "hunter2", None,CallOptions::default()
173 =>
174 "personal_unlockAccount", vec![r#""0x0000000000000000000000000000000000000123""#, r#""hunter2""#, r#"null"#];
175 Value::Bool(true) => true
176 );
177
178 rpc_test! (
179 Personal:send_transaction, TransactionRequest {
180 from: Address::from_low_u64_be(0x123), to: Some(Address::from_low_u64_be(0x123)),
181 gas: None, gas_price: Some(0x1.into()),
182 value: Some(0x1.into()), data: None,
183 nonce: None, condition: None,
184 transaction_type: None, access_list: None,
185 max_fee_per_gas: None, max_priority_fee_per_gas: None,
186 }, "hunter2",CallOptions::default()
187 =>
188 "personal_sendTransaction", vec![r#"{"from":"0x0000000000000000000000000000000000000123","gasPrice":"0x1","to":"0x0000000000000000000000000000000000000123","value":"0x1"}"#, r#""hunter2""#];
189 Value::String("0x0000000000000000000000000000000000000000000000000000000000000123".into()) => Address::from_low_u64_be(0x123)
190 );
191
192 rpc_test! (
193 Personal:sign_transaction, TransactionRequest {
194 from: hex!("407d73d8a49eeb85d32cf465507dd71d507100c1").into(),
195 to: Some(hex!("853f43d8a49eeb85d32cf465507dd71d507100c1").into()),
196 gas: Some(0x7f110.into()),
197 gas_price: Some(0x09184e72a000u64.into()),
198 value: Some(0x7f110.into()),
199 data: Some(hex!("603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360").into()),
200 nonce: Some(0x0.into()),
201 condition: None,
202 transaction_type: None,
203 access_list: None,
204 max_fee_per_gas: None,
205 max_priority_fee_per_gas: None,
206 }, "hunter2",CallOptions::default()
207 =>
208 "personal_signTransaction", vec![r#"{"data":"0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360","from":"0x407d73d8a49eeb85d32cf465507dd71d507100c1","gas":"0x7f110","gasPrice":"0x9184e72a000","nonce":"0x0","to":"0x853f43d8a49eeb85d32cf465507dd71d507100c1","value":"0x7f110"}"#, r#""hunter2""#];
209 ::serde_json::from_str(EXAMPLE_TX).unwrap()
210 => ::serde_json::from_str::<RawTransaction>(EXAMPLE_TX).unwrap()
211 );
212
213 rpc_test! {
214 Personal:import_raw_key, &[0u8; 32], "hunter2",CallOptions::default() =>
215 "personal_importRawKey", vec![r#""0000000000000000000000000000000000000000000000000000000000000000""#, r#""hunter2""#];
216 Value::String("0x0000000000000000000000000000000000000123".into()) => Address::from_low_u64_be(0x123)
217 }
218
219 rpc_test! {
220 Personal:sign, Bytes(hex!("7f0d39b8347598e20466233ce2fb3e824f0f93dfbf233125d3ab09b172c62591ec24dc84049242e364895c3abdbbd843d4a0a188").to_vec()), H160(hex!("7f0d39b8347598e20466233ce2fb3e824f0f93df")), "hunter2",CallOptions::default()
221 =>
222 "personal_sign", vec![r#""0x7f0d39b8347598e20466233ce2fb3e824f0f93dfbf233125d3ab09b172c62591ec24dc84049242e364895c3abdbbd843d4a0a188""#, r#""0x7f0d39b8347598e20466233ce2fb3e824f0f93df""#, r#""hunter2""#];
223 Value::String("0xdac1cba443d72e2088ed0cd2e6608ce696eb4728caf119dcfeea752f57a1163274de0b25007aa70201d0d80190071b26be2287b4a473767e5f7bc443c080b4fc1c".into()) => H520(hex!("dac1cba443d72e2088ed0cd2e6608ce696eb4728caf119dcfeea752f57a1163274de0b25007aa70201d0d80190071b26be2287b4a473767e5f7bc443c080b4fc1c"))
224 }
225}