/home/runner/work/creditcoin/creditcoin/pallets/creditcoin/src/helpers/register_transfer.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use crate::{ |
2 | | pallet::*, types::AddressId, Blockchain, Currency, CurrencyId, DealOrderId, Error, |
3 | | EvmCurrencyType, EvmSupportedTransferKinds, EvmTransferKind, ExternalAmount, ExternalTxId, Id, |
4 | | LegacyTransferKind, Task, TaskId, Transfer, TransferId, TransferKind, UnverifiedTransfer, |
5 | | }; |
6 | | |
7 | | use frame_support::{ensure, traits::Get}; |
8 | | use frame_system::pallet_prelude::*; |
9 | | use sp_runtime::traits::Saturating; |
10 | | use sp_std::prelude::*; |
11 | | |
12 | | impl<T: Config> Pallet<T> { |
13 | 53 | pub fn register_transfer_internal( |
14 | 53 | who: T::AccountId, |
15 | 53 | from_id: AddressId<T::Hash>, |
16 | 53 | to_id: AddressId<T::Hash>, |
17 | 53 | transfer_kind: TransferKind, |
18 | 53 | amount: ExternalAmount, |
19 | 53 | deal_order_id: DealOrderId<T::BlockNumber, T::Hash>, |
20 | 53 | blockchain_tx_id: ExternalTxId, |
21 | 53 | currency: &CurrencyId<T::Hash>, |
22 | 53 | ) -> Result< |
23 | 53 | (TransferId<T::Hash>, Transfer<T::AccountId, BlockNumberFor<T>, T::Hash, T::Moment>), |
24 | 53 | crate::Error<T>, |
25 | 53 | > { |
26 | 53 | let from52 = Self::get_address(&from_id)?1 ; |
27 | 52 | let to51 = Self::get_address(&to_id)?1 ; |
28 | | |
29 | 51 | ensure!(from.owner == who, Error::<T>::NotAddressOwner1 ); |
30 | | |
31 | 50 | ensure!(from.blockchain == to.blockchain, Error::<T>::AddressBlockchainMismatch1 ); |
32 | | |
33 | 49 | let transfer_id = TransferId::new::<T>(&from.blockchain, &blockchain_tx_id); |
34 | 49 | ensure!(!Transfers::<T>::contains_key(&transfer_id), Error::<T>::TransferAlreadyRegistered1 ); |
35 | | |
36 | 48 | let currency = Currencies::<T>::get(¤cy).ok_or(Error::<T>::CurrencyNotRegistered)?0 ; |
37 | | |
38 | 48 | ensure!(currency.supports(&transfer_kind), Error::<T>::UnsupportedTransferKind1 ); |
39 | | |
40 | 47 | let block = Self::block_number(); |
41 | 47 | |
42 | 47 | let transfer = Transfer { |
43 | 47 | blockchain: from.blockchain, |
44 | 47 | kind: transfer_kind, |
45 | 47 | amount, |
46 | 47 | block, |
47 | 47 | from: from_id, |
48 | 47 | to: to_id, |
49 | 47 | deal_order_id, |
50 | 47 | is_processed: false, |
51 | 47 | account_id: who, |
52 | 47 | tx_id: blockchain_tx_id, |
53 | 47 | timestamp: None, |
54 | 47 | }; |
55 | 47 | |
56 | 47 | let deadline = block.saturating_add(T::UnverifiedTaskTimeout::get()); |
57 | 47 | |
58 | 47 | let pending = UnverifiedTransfer { |
59 | 47 | from_external: from.value, |
60 | 47 | to_external: to.value, |
61 | 47 | transfer: transfer.clone(), |
62 | 47 | deadline, |
63 | 47 | currency_to_check: crate::CurrencyOrLegacyTransferKind::Currency(currency), |
64 | 47 | }; |
65 | 47 | let task_id = TaskId::from(transfer_id.clone()); |
66 | 47 | let pending = Task::from(pending); |
67 | 47 | PendingTasks::<T>::insert(&deadline, &task_id, &pending); |
68 | 47 | |
69 | 47 | Ok((transfer_id, transfer)) |
70 | 53 | } |
71 | | |
72 | 9 | pub fn register_transfer_internal_legacy( |
73 | 9 | who: T::AccountId, |
74 | 9 | from_id: AddressId<T::Hash>, |
75 | 9 | to_id: AddressId<T::Hash>, |
76 | 9 | transfer_kind: LegacyTransferKind, |
77 | 9 | amount: ExternalAmount, |
78 | 9 | deal_order_id: DealOrderId<T::BlockNumber, T::Hash>, |
79 | 9 | blockchain_tx_id: ExternalTxId, |
80 | 9 | ) -> Result< |
81 | 9 | (TransferId<T::Hash>, Transfer<T::AccountId, BlockNumberFor<T>, T::Hash, T::Moment>), |
82 | 9 | crate::Error<T>, |
83 | 9 | > { |
84 | 9 | let from8 = Self::get_address(&from_id)?1 ; |
85 | 8 | let to7 = Self::get_address(&to_id)?1 ; |
86 | | |
87 | 7 | ensure!(from.owner == who, Error::<T>::NotAddressOwner1 ); |
88 | | |
89 | 6 | ensure!(from.blockchain == to.blockchain, Error::<T>::AddressBlockchainMismatch1 ); |
90 | | |
91 | 5 | ensure!(from.blockchain.supports(&transfer_kind), Error::<T>::UnsupportedTransferKind1 ); |
92 | | |
93 | 4 | let transfer_id = TransferId::new::<T>(&from.blockchain, &blockchain_tx_id); |
94 | 4 | ensure!(!Transfers::<T>::contains_key(&transfer_id), Error::<T>::TransferAlreadyRegistered1 ); |
95 | | |
96 | 3 | let block = Self::block_number(); |
97 | 3 | |
98 | 3 | DealOrders::<T>::mutate( |
99 | 3 | deal_order_id.expiration(), |
100 | 3 | &deal_order_id.hash(), |
101 | 3 | |deal_order| -> Result<(), crate::Error<T>> { |
102 | 3 | let mut deal_order = deal_order.as_mut().ok_or(Error::<T>::NonExistentDealOrder)?0 ; |
103 | 3 | let currency = match &from.blockchain { |
104 | 3 | Blockchain::Evm(info) => match transfer_kind.clone() { |
105 | 3 | LegacyTransferKind::Ethless(contract) => Currency::Evm( |
106 | 3 | EvmCurrencyType::SmartContract( |
107 | 3 | contract, |
108 | 3 | EvmSupportedTransferKinds::try_from(vec![EvmTransferKind::Ethless]) |
109 | 3 | .expect("length 1 is less than the bound 2; qed"), |
110 | 3 | ), |
111 | 3 | info.clone(), |
112 | 3 | ), |
113 | 0 | LegacyTransferKind::Erc20(contract) => Currency::Evm( |
114 | 0 | EvmCurrencyType::SmartContract( |
115 | 0 | contract, |
116 | 0 | EvmSupportedTransferKinds::try_from(vec![EvmTransferKind::Erc20]) |
117 | 0 | .expect("length 1 is less than the bound 2; qed"), |
118 | 0 | ), |
119 | 0 | info.clone(), |
120 | 0 | ), |
121 | 0 | _ => return Err(Error::<T>::UnsupportedTransferKind), |
122 | | }, |
123 | | }; |
124 | 3 | let currency_id = CurrencyId::new::<T>(¤cy); |
125 | 3 | deal_order.terms.currency = currency_id; |
126 | 3 | Ok(()) |
127 | 3 | }, |
128 | 3 | )?0 ; |
129 | | |
130 | 3 | let transfer = Transfer { |
131 | 3 | blockchain: from.blockchain, |
132 | 3 | kind: transfer_kind |
133 | 3 | .clone() |
134 | 3 | .try_into() |
135 | 3 | .map_err(|()| Error::<T>::UnsupportedTransferKind0 )?0 , |
136 | 3 | amount, |
137 | 3 | block, |
138 | 3 | from: from_id, |
139 | 3 | to: to_id, |
140 | 3 | deal_order_id, |
141 | 3 | is_processed: false, |
142 | 3 | account_id: who, |
143 | 3 | tx_id: blockchain_tx_id, |
144 | 3 | timestamp: None, |
145 | 3 | }; |
146 | 3 | |
147 | 3 | let deadline = block.saturating_add(T::UnverifiedTaskTimeout::get()); |
148 | 3 | |
149 | 3 | let pending = UnverifiedTransfer { |
150 | 3 | from_external: from.value, |
151 | 3 | to_external: to.value, |
152 | 3 | transfer: transfer.clone(), |
153 | 3 | deadline, |
154 | 3 | currency_to_check: crate::CurrencyOrLegacyTransferKind::TransferKind(transfer_kind), |
155 | 3 | }; |
156 | 3 | let task_id = TaskId::from(transfer_id.clone()); |
157 | 3 | let pending = Task::from(pending); |
158 | 3 | PendingTasks::<T>::insert(&deadline, &task_id, &pending); |
159 | 3 | |
160 | 3 | Ok((transfer_id, transfer)) |
161 | 9 | } |
162 | | } |
163 | | |
164 | | #[cfg(test)] |
165 | | mod tests { |
166 | | use super::*; |
167 | | use crate::mock::{ExtBuilder, Test}; |
168 | | use crate::pallet::Pallet as Creditcoin; |
169 | | use crate::tests::{IntoBounded, RegisteredAddress, TestInfo}; |
170 | | use frame_support::BoundedVec; |
171 | | |
172 | 1 | #[test] |
173 | 1 | fn register_transfer_internal_legacy_should_error_with_non_existent_lender_address() { |
174 | 1 | ExtBuilder::default().build_and_execute(|| { |
175 | 1 | let test_info = TestInfo::new_defaults(); |
176 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
177 | 1 | let tx = "0xabcabcabc"; |
178 | 1 | let bogus_address = |
179 | 1 | AddressId::new::<Test>(&Blockchain::RINKEBY, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); |
180 | 1 | |
181 | 1 | let result = Creditcoin::register_transfer_internal_legacy( |
182 | 1 | test_info.lender.account_id, |
183 | 1 | bogus_address, |
184 | 1 | deal_order.borrower_address_id, |
185 | 1 | LegacyTransferKind::Native, |
186 | 1 | deal_order.terms.amount, |
187 | 1 | deal_order_id, |
188 | 1 | tx.as_bytes().into_bounded(), |
189 | 1 | ) |
190 | 1 | .unwrap_err(); |
191 | 1 | |
192 | 1 | assert_eq!(result, crate::Error::<Test>::NonExistentAddress); |
193 | 1 | }) |
194 | 1 | } |
195 | | |
196 | 1 | #[test] |
197 | 1 | fn register_transfer_internal_legacy_should_error_with_non_existent_borrower_address() { |
198 | 1 | ExtBuilder::default().build_and_execute(|| { |
199 | 1 | let test_info = TestInfo::new_defaults(); |
200 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
201 | 1 | let tx = "0xabcabcabc"; |
202 | 1 | let bogus_address = |
203 | 1 | AddressId::new::<Test>(&Blockchain::RINKEBY, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); |
204 | 1 | |
205 | 1 | let result = Creditcoin::register_transfer_internal_legacy( |
206 | 1 | test_info.lender.account_id, |
207 | 1 | deal_order.lender_address_id, |
208 | 1 | bogus_address, |
209 | 1 | LegacyTransferKind::Native, |
210 | 1 | deal_order.terms.amount, |
211 | 1 | deal_order_id, |
212 | 1 | tx.as_bytes().into_bounded(), |
213 | 1 | ) |
214 | 1 | .unwrap_err(); |
215 | 1 | |
216 | 1 | assert_eq!(result, crate::Error::<Test>::NonExistentAddress); |
217 | 1 | }) |
218 | 1 | } |
219 | | |
220 | 1 | #[test] |
221 | 1 | fn register_transfer_internal_legacy_should_error_when_signer_doesnt_own_from_address() { |
222 | 1 | ExtBuilder::default().build_and_execute(|| { |
223 | 1 | let test_info = TestInfo::new_defaults(); |
224 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
225 | 1 | let tx = "0xabcabcabc"; |
226 | 1 | |
227 | 1 | let result = Creditcoin::register_transfer_internal_legacy( |
228 | 1 | test_info.lender.account_id, |
229 | 1 | deal_order.borrower_address_id, // should match 1st argument |
230 | 1 | deal_order.lender_address_id, |
231 | 1 | LegacyTransferKind::Native, |
232 | 1 | deal_order.terms.amount, |
233 | 1 | deal_order_id, |
234 | 1 | tx.as_bytes().into_bounded(), |
235 | 1 | ) |
236 | 1 | .unwrap_err(); |
237 | 1 | |
238 | 1 | assert_eq!(result, crate::Error::<Test>::NotAddressOwner); |
239 | 1 | }) |
240 | 1 | } |
241 | | |
242 | 1 | #[test] |
243 | 1 | fn register_transfer_internal_legacy_should_error_when_addresses_are_not_on_the_same_blockchain( |
244 | 1 | ) { |
245 | 1 | ExtBuilder::default().build_and_execute(|| { |
246 | 1 | let test_info = TestInfo::new_defaults(); |
247 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
248 | 1 | let second_borrower = RegisteredAddress::new("borrower2", Blockchain::LUNIVERSE); |
249 | 1 | let tx = "0xabcabcabc"; |
250 | 1 | |
251 | 1 | let result = Creditcoin::register_transfer_internal_legacy( |
252 | 1 | test_info.lender.account_id, |
253 | 1 | deal_order.lender_address_id, |
254 | 1 | second_borrower.address_id, |
255 | 1 | LegacyTransferKind::Native, |
256 | 1 | deal_order.terms.amount, |
257 | 1 | deal_order_id, |
258 | 1 | tx.as_bytes().into_bounded(), |
259 | 1 | ) |
260 | 1 | .unwrap_err(); |
261 | 1 | |
262 | 1 | assert_eq!(result, crate::Error::<Test>::AddressBlockchainMismatch); |
263 | 1 | }) |
264 | 1 | } |
265 | | |
266 | 1 | #[test] |
267 | 1 | fn register_transfer_internal_legacy_should_error_when_transfer_kind_is_not_supported() { |
268 | 1 | ExtBuilder::default().build_and_execute(|| { |
269 | 1 | let test_info = TestInfo::new_defaults(); |
270 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
271 | 1 | let tx = "0xabcabcabc"; |
272 | 1 | |
273 | 1 | let result = Creditcoin::register_transfer_internal_legacy( |
274 | 1 | test_info.lender.account_id, |
275 | 1 | deal_order.lender_address_id, |
276 | 1 | deal_order.borrower_address_id, |
277 | 1 | // not supported on Blockchain::RINKEBY |
278 | 1 | LegacyTransferKind::Other(BoundedVec::try_from(b"other".to_vec()).unwrap()), |
279 | 1 | deal_order.terms.amount, |
280 | 1 | deal_order_id, |
281 | 1 | tx.as_bytes().into_bounded(), |
282 | 1 | ) |
283 | 1 | .unwrap_err(); |
284 | 1 | |
285 | 1 | assert_eq!(result, crate::Error::<Test>::UnsupportedTransferKind); |
286 | 1 | }) |
287 | 1 | } |
288 | | |
289 | 1 | #[test] |
290 | 1 | fn register_transfer_internal_legacy_should_error_when_transfer_is_already_registered() { |
291 | 1 | ExtBuilder::default().build_and_execute(|| { |
292 | 1 | let test_info = TestInfo::new_defaults(); |
293 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
294 | 1 | let (_transfer_id, transfer) = test_info.create_funding_transfer(&deal_order_id); |
295 | 1 | |
296 | 1 | let result = Creditcoin::register_transfer_internal_legacy( |
297 | 1 | test_info.lender.account_id, |
298 | 1 | deal_order.lender_address_id, |
299 | 1 | deal_order.borrower_address_id, |
300 | 1 | LegacyTransferKind::Native, |
301 | 1 | deal_order.terms.amount, |
302 | 1 | deal_order_id, |
303 | 1 | transfer.tx_id, |
304 | 1 | ) |
305 | 1 | .unwrap_err(); |
306 | 1 | |
307 | 1 | assert_eq!(result, crate::Error::<Test>::TransferAlreadyRegistered); |
308 | 1 | }) |
309 | 1 | } |
310 | | |
311 | 1 | #[test] |
312 | 1 | fn register_transfer_internal_should_error_with_non_existent_lender_address() { |
313 | 1 | ExtBuilder::default().build_and_execute(|| { |
314 | 1 | let test_info = TestInfo::new_defaults(); |
315 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
316 | 1 | let tx = "0xabcabcabc"; |
317 | 1 | let bogus_address = |
318 | 1 | AddressId::new::<Test>(&Blockchain::RINKEBY, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); |
319 | 1 | |
320 | 1 | let result = Creditcoin::register_transfer_internal( |
321 | 1 | test_info.lender.account_id, |
322 | 1 | bogus_address, |
323 | 1 | deal_order.borrower_address_id, |
324 | 1 | EvmTransferKind::Ethless.into(), |
325 | 1 | deal_order.terms.amount, |
326 | 1 | deal_order_id, |
327 | 1 | tx.as_bytes().into_bounded(), |
328 | 1 | &test_info.currency.to_id::<Test>(), |
329 | 1 | ) |
330 | 1 | .unwrap_err(); |
331 | 1 | |
332 | 1 | assert_eq!(result, crate::Error::<Test>::NonExistentAddress); |
333 | 1 | }) |
334 | 1 | } |
335 | | |
336 | 1 | #[test] |
337 | 1 | fn register_transfer_internal_should_error_with_non_existent_borrower_address() { |
338 | 1 | ExtBuilder::default().build_and_execute(|| { |
339 | 1 | let test_info = TestInfo::new_defaults(); |
340 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
341 | 1 | let tx = "0xabcabcabc"; |
342 | 1 | let bogus_address = |
343 | 1 | AddressId::new::<Test>(&Blockchain::RINKEBY, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); |
344 | 1 | |
345 | 1 | let result = Creditcoin::register_transfer_internal( |
346 | 1 | test_info.lender.account_id, |
347 | 1 | deal_order.lender_address_id, |
348 | 1 | bogus_address, |
349 | 1 | EvmTransferKind::Ethless.into(), |
350 | 1 | deal_order.terms.amount, |
351 | 1 | deal_order_id, |
352 | 1 | tx.as_bytes().into_bounded(), |
353 | 1 | &test_info.currency.to_id::<Test>(), |
354 | 1 | ) |
355 | 1 | .unwrap_err(); |
356 | 1 | |
357 | 1 | assert_eq!(result, crate::Error::<Test>::NonExistentAddress); |
358 | 1 | }) |
359 | 1 | } |
360 | | |
361 | 1 | #[test] |
362 | 1 | fn register_transfer_internal_should_error_when_signer_doesnt_own_from_address() { |
363 | 1 | ExtBuilder::default().build_and_execute(|| { |
364 | 1 | let test_info = TestInfo::new_defaults(); |
365 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
366 | 1 | let tx = "0xabcabcabc"; |
367 | 1 | |
368 | 1 | let result = Creditcoin::register_transfer_internal( |
369 | 1 | test_info.lender.account_id, |
370 | 1 | deal_order.borrower_address_id, // should match 1st argument |
371 | 1 | deal_order.lender_address_id, |
372 | 1 | EvmTransferKind::Ethless.into(), |
373 | 1 | deal_order.terms.amount, |
374 | 1 | deal_order_id, |
375 | 1 | tx.as_bytes().into_bounded(), |
376 | 1 | &test_info.currency.to_id::<Test>(), |
377 | 1 | ) |
378 | 1 | .unwrap_err(); |
379 | 1 | |
380 | 1 | assert_eq!(result, crate::Error::<Test>::NotAddressOwner); |
381 | 1 | }) |
382 | 1 | } |
383 | | |
384 | 1 | #[test] |
385 | 1 | fn register_transfer_internal_should_error_when_addresses_are_not_on_the_same_blockchain() { |
386 | 1 | ExtBuilder::default().build_and_execute(|| { |
387 | 1 | let test_info = TestInfo::new_defaults(); |
388 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
389 | 1 | let second_borrower = RegisteredAddress::new("borrower2", Blockchain::LUNIVERSE); |
390 | 1 | let tx = "0xabcabcabc"; |
391 | 1 | |
392 | 1 | let result = Creditcoin::register_transfer_internal( |
393 | 1 | test_info.lender.account_id, |
394 | 1 | deal_order.lender_address_id, |
395 | 1 | second_borrower.address_id, |
396 | 1 | EvmTransferKind::Ethless.into(), |
397 | 1 | deal_order.terms.amount, |
398 | 1 | deal_order_id, |
399 | 1 | tx.as_bytes().into_bounded(), |
400 | 1 | &test_info.currency.to_id::<Test>(), |
401 | 1 | ) |
402 | 1 | .unwrap_err(); |
403 | 1 | |
404 | 1 | assert_eq!(result, crate::Error::<Test>::AddressBlockchainMismatch); |
405 | 1 | }) |
406 | 1 | } |
407 | | |
408 | 1 | #[test] |
409 | 1 | fn register_transfer_internal_should_error_when_transfer_kind_is_not_supported() { |
410 | 1 | ExtBuilder::default().build_and_execute(|| { |
411 | 1 | let test_info = TestInfo::new_defaults(); |
412 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
413 | 1 | let tx = "0xabcabcabc"; |
414 | 1 | |
415 | 1 | let result = Creditcoin::register_transfer_internal( |
416 | 1 | test_info.lender.account_id, |
417 | 1 | deal_order.lender_address_id, |
418 | 1 | deal_order.borrower_address_id, |
419 | 1 | // not supported on Blockchain::RINKEBY |
420 | 1 | EvmTransferKind::Erc20.into(), |
421 | 1 | deal_order.terms.amount, |
422 | 1 | deal_order_id, |
423 | 1 | tx.as_bytes().into_bounded(), |
424 | 1 | &test_info.currency.to_id::<Test>(), |
425 | 1 | ) |
426 | 1 | .unwrap_err(); |
427 | 1 | |
428 | 1 | assert_eq!(result, crate::Error::<Test>::UnsupportedTransferKind); |
429 | 1 | }) |
430 | 1 | } |
431 | | |
432 | 1 | #[test] |
433 | 1 | fn register_transfer_internal_should_error_when_transfer_is_already_registered() { |
434 | 1 | ExtBuilder::default().build_and_execute(|| { |
435 | 1 | let test_info = TestInfo::new_defaults(); |
436 | 1 | let (deal_order_id, deal_order) = test_info.create_deal_order(); |
437 | 1 | let (_transfer_id, transfer) = test_info.create_funding_transfer(&deal_order_id); |
438 | 1 | |
439 | 1 | let result = Creditcoin::register_transfer_internal( |
440 | 1 | test_info.lender.account_id, |
441 | 1 | deal_order.lender_address_id, |
442 | 1 | deal_order.borrower_address_id, |
443 | 1 | EvmTransferKind::Ethless.into(), |
444 | 1 | deal_order.terms.amount, |
445 | 1 | deal_order_id, |
446 | 1 | transfer.tx_id, |
447 | 1 | &test_info.currency.to_id::<Test>(), |
448 | 1 | ) |
449 | 1 | .unwrap_err(); |
450 | 1 | |
451 | 1 | assert_eq!(result, crate::Error::<Test>::TransferAlreadyRegistered); |
452 | 1 | }) |
453 | 1 | } |
454 | | } |