Coverage Report

Created: 2022-11-10 19:56

/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(&currency).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>(&currency);
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
}