Coverage Report

Created: 2022-11-10 19:56

/home/runner/work/creditcoin/creditcoin/pallets/creditcoin/src/migrations/v1.rs
Line
Count
Source (jump to first uncovered line)
1
// First `LoanTerms` rework. `maturity` is replaced with `term_length`,
2
// and `InterestRate` changed from a type alias = u64 to a new struct `InterestRate`
3
4
use crate::{
5
  loan_terms::{Decimals, Duration},
6
  AddressId, Config, ExternalAmount, OfferId, RatePerPeriod, TransferId,
7
};
8
use codec::{Decode, Encode};
9
use frame_support::generate_storage_alias;
10
use frame_support::pallet_prelude::*;
11
use frame_support::{Identity, Twox64Concat};
12
use sp_runtime::traits::{Saturating, UniqueSaturatedInto};
13
14
type OldInterestRate = u64;
15
16
const OLD_INTEREST_RATE_DECIMALS: u64 = 4;
17
18
3
#[derive(Encode, Decode)]
19
struct OldLoanTerms<Moment> {
20
  amount: ExternalAmount,
21
  interest_rate: OldInterestRate,
22
  maturity: Moment,
23
}
24
25
1
#[derive(Encode, Decode)]
26
1
struct OldAskTerms<Moment>(OldLoanTerms<Moment>);
27
28
1
#[derive(Encode, Decode)]
29
1
struct OldBidTerms<Moment>(OldLoanTerms<Moment>);
30
31
1
#[derive(Encode, Decode)]
32
struct OldAskOrder<AccountId, BlockNum, Hash, Moment> {
33
  blockchain: Blockchain,
34
  lender_address_id: AddressId<Hash>,
35
  terms: OldAskTerms<Moment>,
36
  expiration_block: BlockNum,
37
  block: BlockNum,
38
  lender: AccountId,
39
}
40
41
1
#[derive(Encode, Decode)]
42
struct OldBidOrder<AccountId, BlockNum, Hash, Moment> {
43
  blockchain: Blockchain,
44
  borrower_address_id: AddressId<Hash>,
45
  terms: OldBidTerms<Moment>,
46
  expiration_block: BlockNum,
47
  block: BlockNum,
48
  borrower: AccountId,
49
}
50
51
1
#[derive(Encode, Decode)]
52
struct OldDealOrder<AccountId, BlockNum, Hash, Moment> {
53
  blockchain: Blockchain,
54
  offer_id: OfferId<BlockNum, Hash>,
55
  lender_address_id: AddressId<Hash>,
56
  borrower_address_id: AddressId<Hash>,
57
  terms: OldLoanTerms<Moment>,
58
  expiration_block: BlockNum,
59
  timestamp: Moment,
60
  funding_transfer_id: Option<TransferId<Hash>>,
61
  repayment_transfer_id: Option<TransferId<Hash>>,
62
  lock: Option<AccountId>,
63
  borrower: AccountId,
64
}
65
66
2
#[derive(Encode, Decode)]
67
1
#[cfg_attr(test, derive(
Debug0
, PartialEq, Eq))]
68
pub struct DealOrder<AccountId, BlockNum, Hash, Moment> {
69
  pub blockchain: Blockchain,
70
  pub offer_id: OfferId<BlockNum, Hash>,
71
  pub lender_address_id: AddressId<Hash>,
72
  pub borrower_address_id: AddressId<Hash>,
73
  pub terms: LoanTerms,
74
  pub expiration_block: BlockNum,
75
  pub timestamp: Moment,
76
  pub funding_transfer_id: Option<TransferId<Hash>>,
77
  pub repayment_transfer_id: Option<TransferId<Hash>>,
78
  pub lock: Option<AccountId>,
79
  pub borrower: AccountId,
80
}
81
82
8
#[derive(Encode, Decode)]
83
4
#[cfg_attr(test, derive(
Debug0
, PartialEq, Eq))]
84
pub struct InterestRate {
85
  pub rate_per_period: RatePerPeriod,
86
  pub decimals: Decimals,
87
  pub period: Duration,
88
}
89
90
8
#[derive(Encode, Decode)]
91
4
#[cfg_attr(test, derive(
Debug0
, PartialEq, Eq))]
92
pub struct LoanTerms {
93
  pub amount: ExternalAmount,
94
  pub interest_rate: InterestRate,
95
  pub term_length: Duration,
96
}
97
98
2
#[derive(Encode, Decode)]
99
1
#[cfg_attr(test, derive(
Debug0
, PartialEq, Eq))]
100
2
pub struct AskTerms(pub LoanTerms);
101
102
2
#[derive(Encode, Decode)]
103
1
#[cfg_attr(test, derive(
Debug0
, PartialEq, Eq))]
104
2
pub struct BidTerms(pub LoanTerms);
105
106
2
#[derive(Encode, Decode)]
107
1
#[cfg_attr(test, derive(
Debug0
, PartialEq, Eq))]
108
pub struct AskOrder<AccountId, BlockNum, Hash> {
109
  pub blockchain: Blockchain,
110
  pub lender_address_id: AddressId<Hash>,
111
  pub terms: AskTerms,
112
  pub expiration_block: BlockNum,
113
  pub block: BlockNum,
114
  pub lender: AccountId,
115
}
116
117
2
#[derive(Encode, Decode)]
118
1
#[cfg_attr(test, derive(
Debug0
, PartialEq, Eq))]
119
pub struct BidOrder<AccountId, BlockNum, Hash> {
120
  pub blockchain: Blockchain,
121
  pub borrower_address_id: AddressId<Hash>,
122
  pub terms: BidTerms,
123
  pub expiration_block: BlockNum,
124
  pub block: BlockNum,
125
  pub borrower: AccountId,
126
}
127
128
type OtherChainLen = ConstU32<256>;
129
pub type OtherChain = BoundedVec<u8, OtherChainLen>;
130
131
50
#[derive(
E40
ncod
e40
, Decode)]
132
9
#[cfg_attr(test, derive(
D0
ebu
g0
, PartialEq, Eq,
C4
lon
e4
))]
133
pub enum Blockchain {
134
  Ethereum,
135
  Rinkeby,
136
  Luniverse,
137
  Bitcoin,
138
  Other(OtherChain),
139
}
140
141
impl Blockchain {
142
  #[allow(dead_code)]
143
32
  pub fn as_bytes(&self) -> &[u8] {
144
32
    match self {
145
24
      Blockchain::Ethereum => b"ethereum",
146
8
      Blockchain::Rinkeby => b"rinkeby",
147
0
      Blockchain::Luniverse => b"luniverse",
148
0
      Blockchain::Bitcoin => b"bitcoin",
149
0
      Blockchain::Other(chain) => chain.as_slice(),
150
    }
151
32
  }
152
}
153
154
generate_storage_alias!(
155
  Creditcoin,
156
  DealOrders<T: Config> => DoubleMap<(Twox64Concat, T::BlockNumber), (Identity, T::Hash), DealOrder<T::AccountId, T::BlockNumber, T::Hash, T::Moment>>
157
);
158
159
generate_storage_alias!(
160
  Creditcoin,
161
  AskOrders<T: Config> => DoubleMap<(Twox64Concat, T::BlockNumber), (Identity, T::Hash), AskOrder<T::AccountId, T::BlockNumber, T::Hash>>
162
);
163
164
generate_storage_alias!(
165
  Creditcoin,
166
  BidOrders<T: Config> => DoubleMap<(Twox64Concat, T::BlockNumber), (Identity, T::Hash), BidOrder<T::AccountId, T::BlockNumber, T::Hash>>
167
);
168
169
3
pub(crate) fn migrate<T: Config>() -> Weight {
170
3
  let mut weight: Weight = 0;
171
3
  let weight_each = T::DbWeight::get().reads_writes(1, 1);
172
3
  AskOrders::<T>::translate::<OldAskOrder<T::AccountId, T::BlockNumber, T::Hash, T::Moment>, _>(
173
3
    |_expiration, _hash, ask| {
174
1
      weight = weight.saturating_add(weight_each);
175
1
      Some(AskOrder {
176
1
        block: ask.block,
177
1
        blockchain: ask.blockchain,
178
1
        expiration_block: ask.expiration_block,
179
1
        lender: ask.lender,
180
1
        lender_address_id: ask.lender_address_id,
181
1
        terms: AskTerms(LoanTerms {
182
1
          amount: ask.terms.0.amount,
183
1
          interest_rate: InterestRate {
184
1
            rate_per_period: ask.terms.0.interest_rate,
185
1
            decimals: OLD_INTEREST_RATE_DECIMALS,
186
1
            period: Duration::from_millis(ask.terms.0.maturity.unique_saturated_into()),
187
1
          },
188
1
          term_length: Duration::from_millis(
189
1
            ask.terms.0.maturity.unique_saturated_into(),
190
1
          ),
191
1
        }),
192
1
      })
193
3
    },
194
3
  );
195
3
196
3
  BidOrders::<T>::translate::<OldBidOrder<T::AccountId, T::BlockNumber, T::Hash, T::Moment>, _>(
197
3
    |_expiration, _hash, bid| {
198
1
      weight = weight.saturating_add(weight_each);
199
1
      Some(BidOrder {
200
1
        block: bid.block,
201
1
        blockchain: bid.blockchain,
202
1
        expiration_block: bid.expiration_block,
203
1
        borrower: bid.borrower,
204
1
        borrower_address_id: bid.borrower_address_id,
205
1
        terms: BidTerms(LoanTerms {
206
1
          amount: bid.terms.0.amount,
207
1
          interest_rate: InterestRate {
208
1
            rate_per_period: bid.terms.0.interest_rate,
209
1
            decimals: OLD_INTEREST_RATE_DECIMALS,
210
1
            period: Duration::from_millis(bid.terms.0.maturity.unique_saturated_into()),
211
1
          },
212
1
          term_length: Duration::from_millis(
213
1
            bid.terms.0.maturity.unique_saturated_into(),
214
1
          ),
215
1
        }),
216
1
      })
217
3
    },
218
3
  );
219
3
220
3
  DealOrders::<T>::translate::<OldDealOrder<T::AccountId, T::BlockNumber, T::Hash, T::Moment>, _>(
221
3
    |_expiration, _hash, deal| {
222
1
      weight = weight.saturating_add(weight_each);
223
1
      Some(DealOrder {
224
1
        blockchain: deal.blockchain,
225
1
        offer_id: deal.offer_id,
226
1
        lender_address_id: deal.lender_address_id,
227
1
        borrower_address_id: deal.borrower_address_id,
228
1
        terms: LoanTerms {
229
1
          amount: deal.terms.amount,
230
1
          interest_rate: InterestRate {
231
1
            rate_per_period: deal.terms.interest_rate,
232
1
            decimals: OLD_INTEREST_RATE_DECIMALS,
233
1
            period: Duration::from_millis(deal.terms.maturity.unique_saturated_into()),
234
1
          },
235
1
          term_length: Duration::from_millis(
236
1
            deal.terms.maturity.saturating_sub(deal.timestamp).unique_saturated_into(),
237
1
          ),
238
1
        },
239
1
        expiration_block: deal.expiration_block,
240
1
        timestamp: deal.timestamp,
241
1
        funding_transfer_id: deal.funding_transfer_id,
242
1
        repayment_transfer_id: deal.repayment_transfer_id,
243
1
        lock: deal.lock,
244
1
        borrower: deal.borrower,
245
1
      })
246
3
    },
247
3
  );
248
3
  weight
249
3
}
250
251
#[cfg(test)]
252
mod tests {
253
  use sp_core::U256;
254
255
  use crate::{
256
    mock::{ExtBuilder, Test},
257
    tests::TestInfo,
258
    AskOrderId, BidOrderId, DealOrderId, DoubleMapExt, OfferId,
259
  };
260
261
  use super::{
262
    generate_storage_alias, AskOrder, AskTerms, BidOrder, BidTerms, Blockchain, Config,
263
    Duration, Identity, InterestRate, LoanTerms, OldAskOrder, OldAskTerms, OldBidOrder,
264
    OldBidTerms, OldDealOrder, OldLoanTerms, Twox64Concat, OLD_INTEREST_RATE_DECIMALS,
265
  };
266
267
  generate_storage_alias!(
268
    Creditcoin,
269
    DealOrders<T: Config> => DoubleMap<(Twox64Concat, T::BlockNumber), (Identity, T::Hash), OldDealOrder<T::AccountId, T::BlockNumber, T::Hash, T::Moment>>
270
  );
271
272
  type OldDealOrders = DealOrders<Test>;
273
274
  generate_storage_alias!(
275
    Creditcoin,
276
    AskOrders<T: Config> => DoubleMap<(Twox64Concat, T::BlockNumber), (Identity, T::Hash), OldAskOrder<T::AccountId, T::BlockNumber, T::Hash, T::Moment>>
277
  );
278
279
  type OldAskOrders = AskOrders<Test>;
280
281
  generate_storage_alias!(
282
    Creditcoin,
283
    BidOrders<T: Config> => DoubleMap<(Twox64Concat, T::BlockNumber), (Identity, T::Hash), OldBidOrder<T::AccountId, T::BlockNumber, T::Hash, T::Moment>>
284
  );
285
286
  type OldBidOrders = BidOrders<Test>;
287
288
1
  #[test]
289
1
  fn ask_order_migrates() {
290
1
    ExtBuilder::default().build_and_execute(|| {
291
1
      let ask_id = AskOrderId::new::<Test>(100, "asdf".as_bytes());
292
1
      let test_info = TestInfo::new_defaults();
293
1
      let old_ask = OldAskOrder {
294
1
        blockchain: Blockchain::Ethereum,
295
1
        lender_address_id: test_info.lender.address_id.clone(),
296
1
        terms: OldAskTerms(OldLoanTerms {
297
1
          amount: 1000u64.into(),
298
1
          interest_rate: 1000,
299
1
          maturity: 2000,
300
1
        }),
301
1
        expiration_block: 100,
302
1
        block: 0,
303
1
        lender: test_info.lender.account_id,
304
1
      };
305
1
      OldAskOrders::insert_id(&ask_id, &old_ask);
306
1
307
1
      super::migrate::<Test>();
308
1
309
1
      let ask = super::AskOrders::<Test>::try_get_id(&ask_id).unwrap();
310
1
311
1
      assert_eq!(
312
1
        ask,
313
1
        AskOrder {
314
1
          blockchain: old_ask.blockchain,
315
1
          lender_address_id: old_ask.lender_address_id,
316
1
          terms: AskTerms(LoanTerms {
317
1
            amount: old_ask.terms.0.amount,
318
1
            interest_rate: InterestRate {
319
1
              rate_per_period: old_ask.terms.0.interest_rate,
320
1
              decimals: OLD_INTEREST_RATE_DECIMALS,
321
1
              period: Duration::from_millis(old_ask.terms.0.maturity)
322
1
            },
323
1
            term_length: Duration::from_millis(old_ask.terms.0.maturity)
324
1
          }),
325
1
          expiration_block: old_ask.expiration_block,
326
1
          block: old_ask.block,
327
1
          lender: old_ask.lender,
328
1
        }
329
1
      )
330
1
    });
331
1
  }
332
333
1
  #[test]
334
1
  fn bid_order_migrates() {
335
1
    ExtBuilder::default().build_and_execute(|| {
336
1
      let bid_id = BidOrderId::new::<Test>(100, "asdf".as_bytes());
337
1
      let test_info = TestInfo::new_defaults();
338
1
      let address_id = test_info.borrower.address_id.clone();
339
1
      let expiration_block = 100;
340
1
      let block = 0;
341
1
      let amount: U256 = 1000u64.into();
342
1
343
1
      let old_bid = OldBidOrder {
344
1
        blockchain: Blockchain::Ethereum,
345
1
        borrower_address_id: address_id,
346
1
        terms: OldBidTerms(OldLoanTerms { amount, interest_rate: 1000, maturity: 2000 }),
347
1
        expiration_block,
348
1
        block,
349
1
        borrower: test_info.borrower.account_id.clone(),
350
1
      };
351
1
      OldBidOrders::insert_id(&bid_id, &old_bid);
352
1
353
1
      super::migrate::<Test>();
354
1
355
1
      let bid = super::BidOrders::<Test>::try_get_id(&bid_id).unwrap();
356
1
357
1
      assert_eq!(
358
1
        bid,
359
1
        BidOrder {
360
1
          blockchain: old_bid.blockchain,
361
1
          borrower_address_id: old_bid.borrower_address_id,
362
1
          terms: BidTerms(LoanTerms {
363
1
            amount,
364
1
            interest_rate: InterestRate {
365
1
              rate_per_period: old_bid.terms.0.interest_rate,
366
1
              decimals: OLD_INTEREST_RATE_DECIMALS,
367
1
              period: Duration::from_millis(old_bid.terms.0.maturity)
368
1
            },
369
1
            term_length: Duration::from_millis(old_bid.terms.0.maturity)
370
1
          }),
371
1
          expiration_block,
372
1
          block,
373
1
          borrower: test_info.borrower.account_id,
374
1
        }
375
1
      )
376
1
    })
377
1
  }
378
379
1
  #[test]
380
1
  fn deal_order_migrates() {
381
1
    ExtBuilder::default().build_and_execute(|| {
382
1
      let offer_id = OfferId::with_expiration_hash::<Test>(100, [1; 32].into());
383
1
      let deal_id = DealOrderId::with_expiration_hash::<Test>(100, [0; 32].into());
384
1
      let test_info = TestInfo::new_defaults();
385
1
      let old_deal = OldDealOrder {
386
1
        blockchain: Blockchain::Ethereum,
387
1
        lender_address_id: test_info.lender.address_id.clone(),
388
1
        terms: OldLoanTerms { amount: 1000u64.into(), interest_rate: 1000, maturity: 2000 },
389
1
        expiration_block: 100,
390
1
        offer_id,
391
1
        borrower_address_id: test_info.borrower.address_id.clone(),
392
1
        timestamp: 0,
393
1
        funding_transfer_id: None,
394
1
        repayment_transfer_id: None,
395
1
        lock: None,
396
1
        borrower: test_info.borrower.account_id,
397
1
      };
398
1
399
1
      OldDealOrders::insert_id(&deal_id, &old_deal);
400
1
401
1
      super::migrate::<Test>();
402
1
403
1
      let deal = super::DealOrders::<Test>::try_get_id(&deal_id).unwrap();
404
1
405
1
      assert_eq!(
406
1
        deal,
407
1
        super::DealOrder {
408
1
          blockchain: old_deal.blockchain,
409
1
          lender_address_id: old_deal.lender_address_id,
410
1
          terms: LoanTerms {
411
1
            amount: old_deal.terms.amount,
412
1
            interest_rate: InterestRate {
413
1
              rate_per_period: old_deal.terms.interest_rate,
414
1
              decimals: OLD_INTEREST_RATE_DECIMALS,
415
1
              period: Duration::from_millis(old_deal.terms.maturity)
416
1
            },
417
1
            term_length: Duration::from_millis(
418
1
              old_deal.terms.maturity.saturating_sub(old_deal.timestamp)
419
1
            )
420
1
          },
421
1
          offer_id: old_deal.offer_id,
422
1
          borrower_address_id: old_deal.borrower_address_id,
423
1
          expiration_block: old_deal.expiration_block,
424
1
          timestamp: old_deal.timestamp,
425
1
          funding_transfer_id: old_deal.funding_transfer_id,
426
1
          repayment_transfer_id: old_deal.repayment_transfer_id,
427
1
          lock: old_deal.lock,
428
1
          borrower: old_deal.borrower
429
1
        }
430
1
      );
431
1
    });
432
1
  }
433
}