Coverage Report

Created: 2022-11-10 19:56

/home/runner/work/creditcoin/creditcoin/pallets/creditcoin/src/lib.rs
Line
Count
Source (jump to first uncovered line)
1
1
#![cfg_attr(not(feature = "std"), no_std)]#![cfg_attr(not(feature = "std"), no_std)]
2
#![allow(clippy::too_many_arguments, clippy::type_complexity)]
3
4
extern crate alloc;
5
6
use frame_support::traits::StorageVersion;
7
pub use pallet::*;
8
use sp_io::crypto::secp256k1_ecdsa_recover_compressed;
9
use sp_io::KillStorageResult;
10
use sp_runtime::KeyTypeId;
11
use sp_std::prelude::*;
12
13
#[cfg(test)]
14
mod mock;
15
16
#[allow(clippy::unnecessary_cast)]
17
pub mod weights;
18
19
mod benchmarking;
20
#[cfg(test)]
21
mod tests;
22
23
#[macro_use]
24
mod helpers;
25
mod migrations;
26
pub mod ocw;
27
mod types;
28
29
use ocw::tasks::collect_coins::GCreContract;
30
pub use types::*;
31
32
pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"ctcs");
33
34
pub mod crypto {
35
  use crate::KEY_TYPE;
36
  use sp_runtime::{
37
    app_crypto::{app_crypto, sr25519},
38
    MultiSignature, MultiSigner,
39
  };
40
41
0
  app_crypto!(sr25519, KEY_TYPE);
42
43
  pub struct CtcAuthId;
44
45
  impl frame_system::offchain::AppCrypto<MultiSigner, MultiSignature> for CtcAuthId {
46
    type RuntimeAppPublic = Public;
47
48
    type GenericPublic = sp_core::sr25519::Public;
49
50
    type GenericSignature = sp_core::sr25519::Signature;
51
  }
52
}
53
54
pub type BalanceFor<T> = <T as pallet_balances::Config>::Balance;
55
56
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(6);
57
58
483
#[fr
ame_support::pallet0
]370
59
0
pub mod pallet {
60
0
61
0
  use crate::helpers::non_paying_error;
62
0
63
0
  use super::*;
64
0
  use frame_support::{
65
0
    dispatch::DispatchResult,
66
0
    pallet_prelude::*,
67
0
    traits::tokens::{currency::Currency as CurrencyT, fungible::Mutate, ExistenceRequirement},
68
0
    transactional,
69
0
    weights::PostDispatchInfo,
70
0
  };
71
0
  use frame_system::{
72
0
    ensure_signed,
73
0
    offchain::{AppCrypto, CreateSignedTransaction},
74
0
    pallet_prelude::*,
75
0
  };
76
0
  use ocw::errors::VerificationFailureCause;
77
0
  use sp_runtime::offchain::storage_lock::{BlockAndTime, StorageLock};
78
0
  use sp_runtime::offchain::Duration;
79
0
  use sp_runtime::traits::{
80
0
    IdentifyAccount, SaturatedConversion, Saturating, UniqueSaturatedFrom, UniqueSaturatedInto,
81
0
    Verify,
82
0
  };
83
0
  use tracing as log;
84
0
85
0
  /// Configure the pallet by specifying the parameters and types on which it depends.
86
0
  #[pallet::config]
87
0
  pub trait Config:
88
0
    frame_system::Config
89
0
    + pallet_balances::Config
90
0
    + pallet_timestamp::Config
91
0
    + CreateSignedTransaction<Call<Self>>
92
0
  where
93
0
    <Self as frame_system::Config>::BlockNumber: UniqueSaturatedInto<u64>,
94
0
    <Self as pallet_timestamp::Config>::Moment:
95
0
      UniqueSaturatedInto<u64> + UniqueSaturatedFrom<u64>,
96
0
  {
97
0
    /// Because this pallet emits events, it depends on the runtime's definition of an event.
98
0
    type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
99
0
100
0
    type Call: From<Call<Self>>;
101
0
102
0
    type AuthorityId: AppCrypto<
103
0
      Self::Public,
104
0
      <Self as frame_system::offchain::SigningTypes>::Signature,
105
0
    >;
106
0
107
0
    type Signer: From<sp_core::ecdsa::Public>
108
0
      + IdentifyAccount<AccountId = <Self as frame_system::Config>::AccountId>
109
0
      + Parameter;
110
0
111
0
    type SignerSignature: Verify<Signer = Self::Signer>
112
0
      + From<sp_core::ecdsa::Signature>
113
0
      + Parameter;
114
0
115
0
    type FromAccountId: From<sp_core::sr25519::Public>
116
0
      + IsType<Self::AccountId>
117
0
      + Clone
118
0
      + core::fmt::Debug
119
0
      + PartialEq<Self::FromAccountId>
120
0
      + AsRef<[u8; 32]>;
121
0
122
0
    type InternalPublic: sp_core::crypto::UncheckedFrom<[u8; 32]>;
123
0
124
0
    type PublicSigning: From<Self::InternalPublic> + Into<Self::Public>;
125
0
126
0
    // in order to turn a `Hash` into a U256 for checking the nonces on
127
0
    // ethless transfers we need the `Hash` type to implement
128
0
    // the BigEndianHash trait. This effectively constrains the Hash
129
0
    // type to H256, which sort of defeats the purpose of it being an associated type.
130
0
    // However a lot of code refers to Config::Hash, so right now this is the least invasive way
131
0
    // to get the compiler to let us do the Config::Hash -> U256 conversion
132
0
    type HashIntoNonce: IsType<<Self as frame_system::Config>::Hash>
133
0
      + ethereum_types::BigEndianHash<Uint = sp_core::U256>
134
0
      + Clone;
135
0
136
0
    type UnverifiedTaskTimeout: Get<<Self as frame_system::Config>::BlockNumber>;
137
0
138
0
    type WeightInfo: WeightInfo;
139
0
  }
140
0
141
0
  pub trait WeightInfo {
142
0
    fn on_initialize(a: u32, b: u32, o: u32, d: u32, f: u32, u: u32, c: u32) -> Weight;
143
0
    fn register_address() -> Weight;
144
0
    fn claim_legacy_wallet() -> Weight;
145
0
    fn add_ask_order() -> Weight;
146
0
    fn add_bid_order() -> Weight;
147
0
    fn add_offer() -> Weight;
148
0
    fn add_deal_order() -> Weight;
149
0
    fn add_authority() -> Weight;
150
0
    fn persist_transfer() -> Weight;
151
0
    fn fail_transfer() -> Weight;
152
0
    fn fund_deal_order() -> Weight;
153
0
    fn lock_deal_order() -> Weight;
154
0
    fn register_funding_transfer() -> Weight;
155
0
    fn register_repayment_transfer() -> Weight;
156
0
    fn register_funding_transfer_legacy() -> Weight;
157
0
    fn register_repayment_transfer_legacy() -> Weight;
158
0
    fn close_deal_order() -> Weight;
159
0
    fn exempt() -> Weight;
160
0
    fn register_deal_order() -> Weight;
161
0
    fn request_collect_coins() -> Weight;
162
0
    fn persist_collect_coins() -> Weight;
163
0
    fn fail_collect_coins() -> Weight;
164
0
    fn remove_authority() -> Weight;
165
0
    fn set_collect_coins_contract() -> Weight;
166
0
    fn register_currency() -> Weight;
167
0
  }
168
0
169
6
  #[pallet::pallet]
170
0
  #[pallet::storage_version(STORAGE_VERSION)]
171
0
  #[pallet::generate_store(pub(super) trait Store)]
172
0
  pub struct Pallet<T>(_);
173
0
174
103
  #[pallet::storage]
175
0
  #[pallet::getter(fn authorities)]
176
0
  pub type Authorities<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, ()>;
177
0
178
3
  #[pallet::storage]
179
0
  pub type LegacyWallets<T: Config> = StorageMap<_, Twox128, LegacySighash, T::Balance>;
180
0
181
2
  #[pallet::storage]
182
0
  pub type LegacyBalanceKeeper<T: Config> = StorageValue<_, T::AccountId>;
183
0
184
197
  #[pallet::storage]
185
0
  #[pallet::getter(fn pending_tasks)]
186
0
  pub type PendingTasks<T: Config> = StorageDoubleMap<
187
0
    _,
188
0
    Identity,
189
0
    T::BlockNumber,
190
0
    Identity,
191
0
    TaskId<T::Hash>,
192
0
    Task<T::AccountId, T::BlockNumber, T::Hash, T::Moment>,
193
0
  >;
194
0
195
753
  #[pallet::storage]
196
0
  #[pallet::getter(fn deal_orders)]
197
0
  pub type DealOrders<T: Config> = StorageDoubleMap<
198
0
    _,
199
0
    Twox64Concat,
200
0
    T::BlockNumber,
201
0
    Identity,
202
0
    T::Hash,
203
0
    DealOrder<T::AccountId, T::BlockNumber, T::Hash, T::Moment>,
204
0
  >;
205
0
206
2.32k
  #[pallet::storage]
207
0
  #[pallet::getter(fn addresses)]
208
0
  pub type Addresses<T: Config> =
209
0
    StorageMap<_, Blake2_128Concat, AddressId<T::Hash>, Address<T::AccountId>>;
210
0
211
572
  #[pallet::storage]
212
0
  #[pallet::getter(fn used_guids)]
213
0
  pub type UsedGuids<T: Config> = StorageMap<_, Blake2_128Concat, Guid, ()>;
214
0
215
907
  #[pallet::storage]
216
0
  #[pallet::getter(fn ask_orders)]
217
0
  pub type AskOrders<T: Config> = StorageDoubleMap<
218
0
    _,
219
0
    Twox64Concat,
220
0
    T::BlockNumber,
221
0
    Identity,
222
0
    T::Hash,
223
0
    AskOrder<T::AccountId, T::BlockNumber, T::Hash>,
224
0
  >;
225
0
226
904
  #[pallet::storage]
227
0
  #[pallet::getter(fn bid_orders)]
228
0
  pub type BidOrders<T: Config> = StorageDoubleMap<
229
0
    _,
230
0
    Twox64Concat,
231
0
    T::BlockNumber,
232
0
    Identity,
233
0
    T::Hash,
234
0
    BidOrder<T::AccountId, T::BlockNumber, T::Hash>,
235
0
  >;
236
0
237
731
  #[pallet::storage]
238
0
  #[pallet::getter(fn offers)]
239
0
  pub type Offers<T: Config> = StorageDoubleMap<
240
0
    _,
241
0
    Twox64Concat,
242
0
    T::BlockNumber,
243
0
    Identity,
244
0
    T::Hash,
245
0
    Offer<T::AccountId, T::BlockNumber, T::Hash>,
246
0
  >;
247
0
248
136
  #[pallet::storage]
249
0
  #[pallet::getter(fn transfers)]
250
0
  pub type Transfers<T: Config> = StorageMap<
251
0
    _,
252
0
    Identity,
253
0
    TransferId<T::Hash>,
254
0
    Transfer<T::AccountId, T::BlockNumber, T::Hash, T::Moment>,
255
0
  >;
256
0
257
42
  #[pallet::storage]
258
0
  #[pallet::getter(fn collected_coins)]
259
0
  pub type CollectedCoins<T: Config> = StorageMap<
260
0
    _,
261
0
    Identity,
262
0
    CollectedCoinsId<T::Hash>,
263
0
    types::CollectedCoins<T::Hash, T::Balance>,
264
0
  >;
265
0
266
1.19k
  #[pallet::storage]
267
0
  pub type Currencies<T: Config> = StorageMap<_, Identity, CurrencyId<T::Hash>, Currency>;
268
0
269
51
  #[pallet::storage]
270
0
  #[pallet::getter(fn collect_coins_contract)]
271
0
  pub type CollectCoinsContract<T: Config> = StorageValue<_, GCreContract, ValueQuery>;
272
0
273
0
  #[pallet::event]
274
1.24k
  #[pallet::generate_deposit(pub(super) fn deposit_event)]
275
0
  pub enum Event<T: Config> {
276
0
    /// An address on an external chain has been registered.
277
0
    /// [registered_address_id, registered_address]
278
0
    AddressRegistered(AddressId<T::Hash>, Address<T::AccountId>),
279
0
280
0
    /// Collecting coins from Eth ERC-20 has been registered and will be verified.
281
0
    /// [collected_coins_id, registered_collect_coins]
282
0
    CollectCoinsRegistered(CollectedCoinsId<T::Hash>, types::UnverifiedCollectedCoins),
283
0
284
0
    /// An external transfer has been registered and will be verified.
285
0
    /// [registered_transfer_id, registered_transfer]
286
0
    TransferRegistered(
287
0
      TransferId<T::Hash>,
288
0
      Transfer<T::AccountId, T::BlockNumber, T::Hash, T::Moment>,
289
0
    ),
290
0
291
0
    /// An external transfer has been successfully verified.
292
0
    /// [verified_transfer_id]
293
0
    TransferVerified(TransferId<T::Hash>),
294
0
295
0
    /// CollectCoins has been successfully verified and minted.
296
0
    /// [collected_coins_id, collected_coins]
297
0
    CollectedCoinsMinted(
298
0
      types::CollectedCoinsId<T::Hash>,
299
0
      types::CollectedCoins<T::Hash, T::Balance>,
300
0
    ),
301
0
302
0
    /// An external transfer has been processed and marked as part of a loan.
303
0
    /// [processed_transfer_id]
304
0
    TransferProcessed(TransferId<T::Hash>),
305
0
306
0
    /// An ask order has been added by a prospective lender. This indicates that the lender
307
0
    /// is looking to issue a loan with certain terms.
308
0
    /// [ask_order_id, ask_order]
309
0
    AskOrderAdded(
310
0
      AskOrderId<T::BlockNumber, T::Hash>,
311
0
      AskOrder<T::AccountId, T::BlockNumber, T::Hash>,
312
0
    ),
313
0
314
0
    /// A bid order has been added by a prospective borrower. This indicates that the borrower
315
0
    /// is looking for a loan with certain terms.
316
0
    /// [bid_order_id, bid_order]
317
0
    BidOrderAdded(
318
0
      BidOrderId<T::BlockNumber, T::Hash>,
319
0
      BidOrder<T::AccountId, T::BlockNumber, T::Hash>,
320
0
    ),
321
0
322
0
    /// An offer has been added by a lender. This indicates that the lender
323
0
    /// is interested in entering a loan with the owner of the bid order.
324
0
    /// [offer_id, offer]
325
0
    OfferAdded(OfferId<T::BlockNumber, T::Hash>, Offer<T::AccountId, T::BlockNumber, T::Hash>),
326
0
327
0
    /// A deal order has been added by a borrower. This indicates that the borrower
328
0
    /// has accepted a lender's offer and intends to enter the loan.
329
0
    /// [deal_order_id, deal_order]
330
0
    DealOrderAdded(
331
0
      DealOrderId<T::BlockNumber, T::Hash>,
332
0
      DealOrder<T::AccountId, T::BlockNumber, T::Hash, T::Moment>,
333
0
    ),
334
0
335
0
    /// A deal order has been funded by a lender. This indicates that the lender
336
0
    /// has initiated the actual loan by transferring the loan amount to the borrower
337
0
    /// on an external chain.
338
0
    /// [funded_deal_order_id]
339
0
    DealOrderFunded(DealOrderId<T::BlockNumber, T::Hash>),
340
0
341
0
    /// A deal order has been locked by a borrower. This indicates that the borrower
342
0
    /// is preparing to make a repayment and locks the loan from being sold or transferred
343
0
    /// to another party.
344
0
    /// [deal_order_id]
345
0
    DealOrderLocked(DealOrderId<T::BlockNumber, T::Hash>),
346
0
347
0
    /// A deal order has been closed by a borrower. This indicates that the borrower
348
0
    /// has repaid the loan in full and is now closing out the loan.
349
0
    /// [closed_deal_order_id]
350
0
    DealOrderClosed(DealOrderId<T::BlockNumber, T::Hash>),
351
0
352
0
    /// A loan exemption has been granted by a lender. This indicates that the lender
353
0
    /// is releasing all of the outstanding debt on the loan. The borrower
354
0
    /// is no longer responsible for repaying the amount.
355
0
    /// [exempted_deal_order_id]
356
0
    LoanExempted(DealOrderId<T::BlockNumber, T::Hash>),
357
0
358
0
    /// A legacy wallet from Creditcoin 1.X has been claimed. The balance of the legacy wallet
359
0
    /// has been transferred to the owner's Creditcoin 2.0 account.
360
0
    /// [legacy_wallet_claimer, legacy_wallet_sighash, legacy_wallet_balance]
361
0
    LegacyWalletClaimed(T::AccountId, LegacySighash, T::Balance),
362
0
363
0
    TransferFailedVerification(TransferId<T::Hash>, VerificationFailureCause),
364
0
365
0
    /// exchanging vested ERC-20 CC for native CC failed.
366
0
    /// [collected_coins_id, cause]
367
0
    CollectCoinsFailedVerification(CollectedCoinsId<T::Hash>, VerificationFailureCause),
368
0
369
0
    /// A currency has been registered and can now be used in loan terms.
370
0
    /// [currency_id, currency]
371
0
    CurrencyRegistered(CurrencyId<T::Hash>, Currency),
372
0
  }
373
0
374
0
  // Errors inform users that something went wrong.
375
12
  #[derive(PartialEq, Eq)]
376
298
  #[pallet::error]
377
0
  pub enum Error<T> {
378
0
    /// The specified address has already been registered to another account
379
0
    AddressAlreadyRegistered,
380
0
381
0
    /// The specified address does not exist.
382
0
    NonExistentAddress,
383
0
384
0
    /// The specified deal order does not exist.
385
0
    NonExistentDealOrder,
386
0
387
0
    /// The specified ask order does not exist.
388
0
    NonExistentAskOrder,
389
0
390
0
    /// The specified bid order does not exist.
391
0
    NonExistentBidOrder,
392
0
393
0
    /// The specified offer does not exist.
394
0
    NonExistentOffer,
395
0
396
0
    /// The specified transfer does not exist.
397
0
    NonExistentTransfer,
398
0
399
0
    /// The transfer has already been registered.
400
0
    TransferAlreadyRegistered,
401
0
402
0
    /// The coin collection has already been registered.
403
0
    CollectCoinsAlreadyRegistered,
404
0
405
0
    /// The account that registered the transfer does
406
0
    /// not match the account attempting to use the transfer.
407
0
    TransferAccountMismatch,
408
0
409
0
    ///The specified deal order ID does not match the transfer deal order ID.
410
0
    TransferDealOrderMismatch,
411
0
412
0
    ///The amount on the deal order does not match the transfer amount.
413
0
    TransferAmountMismatch,
414
0
415
0
    /// The transfer has already been processed and cannot be used.
416
0
    TransferAlreadyProcessed,
417
0
418
0
    /// The transfer amount is less than the amount in the loan terms.
419
0
    TransferAmountInsufficient,
420
0
421
0
    /// The transfer is malformed and has a block number greater than the
422
0
    /// tip. This is an internal error.
423
0
    MalformedTransfer,
424
0
425
0
    /// The specified transfer type is not currently supported by
426
0
    /// the blockchain the loan is executed on.
427
0
    UnsupportedTransferKind,
428
0
429
0
    /// The node does not have sufficient authority to verify a transfer.
430
0
    InsufficientAuthority,
431
0
432
0
    /// The specified ID has already been used.
433
0
    DuplicateId,
434
0
435
0
    /// The address cannot be used because the user does not own it.
436
0
    NotAddressOwner,
437
0
438
0
    /// Failed to send an offchain callback transaction. This is likely
439
0
    /// an internal error.
440
0
    OffchainSignedTxFailed,
441
0
442
0
    /// The node is an authority but there is no account to create a
443
0
    /// callback transaction. This is likely an internal error.
444
0
    NoLocalAcctForSignedTx,
445
0
446
0
    RepaymentOrderNonZeroGain,
447
0
448
0
    /// The addresses specified are not on compatible external chains.
449
0
    AddressBlockchainMismatch,
450
0
451
0
    /// The account is already an authority.
452
0
    AlreadyAuthority,
453
0
454
0
    /// The account you are trying to remove is not  an authority.
455
0
    NotAnAuthority,
456
0
457
0
    /// The offer has already been made.
458
0
    DuplicateOffer,
459
0
460
0
    /// The deal cannot be locked because it is not funded yet.
461
0
    DealNotFunded,
462
0
463
0
    /// The deal order is already funded and cannot be funded again.
464
0
    DealOrderAlreadyFunded,
465
0
    /// The deal order is already closed and cannot be closed again.
466
0
    DealOrderAlreadyClosed,
467
0
468
0
    /// The deal order is already locked and cannot be locked again.
469
0
    DealOrderAlreadyLocked,
470
0
471
0
    /// The deal order must be locked before it can be closed.
472
0
    DealOrderMustBeLocked,
473
0
474
0
    /// The deal order already exists.
475
0
    DuplicateDealOrder,
476
0
477
0
    /// The deal order has expired and is no longer valid.
478
0
    DealOrderExpired,
479
0
480
0
    /// The ask order has expired and is no longer valid.
481
0
    AskOrderExpired,
482
0
483
0
    /// The bid order has expired and is no longer valid.
484
0
    BidOrderExpired,
485
0
486
0
    /// The offer order has expired and is no longer valid.
487
0
    OfferExpired,
488
0
489
0
    /// The terms of the ask and bid order do not agree.
490
0
    AskBidMismatch,
491
0
492
0
    /// The bid order is owned by the user, a user cannot lend to themself.
493
0
    SameOwner,
494
0
495
0
    /// The signature does not match the public key and message.
496
0
    InvalidSignature,
497
0
498
0
    /// Only the borrower can perform the action.
499
0
    NotBorrower,
500
0
501
0
    /// The deal order is malformed and has a block number greater than the
502
0
    /// tip. This is an internal error.
503
0
    MalformedDealOrder,
504
0
505
0
    /// Only the lender can perform the action.
506
0
    NotLender,
507
0
508
0
    /// Repayment orders are not currently supported.
509
0
    RepaymentOrderUnsupported,
510
0
511
0
    /// The legacy wallet is not owned by the user.
512
0
    NotLegacyWalletOwner,
513
0
514
0
    /// There is no legacy wallet corresponding to the public key.
515
0
    LegacyWalletNotFound,
516
0
517
0
    /// There is no legacy balance keeper, so no legacy wallets can be claimed.
518
0
    /// This is a configuration error and should only occur during local development.
519
0
    LegacyBalanceKeeperMissing,
520
0
521
0
    /// The specified guid has already been used and cannot be re-used.
522
0
    GuidAlreadyUsed,
523
0
524
0
    /// The value of the loan term's term length is zero, which is invalid.
525
0
    InvalidTermLength,
526
0
527
0
    /// The external address is malformed or otherwise invalid for the platform.
528
0
    MalformedExternalAddress,
529
0
530
0
    /// The address format was not recognized for the given blockchain and external address.
531
0
    AddressFormatNotSupported,
532
0
533
0
    /// The address retrieved from the proof-of-ownership signature did not match the external address being registered.
534
0
    OwnershipNotSatisfied,
535
0
536
0
    /// The currency has already been registered.
537
0
    CurrencyAlreadyRegistered,
538
0
539
0
    /// The legacy/deprecated version of an extrinsic was called, the new version should be used instead.
540
0
    DeprecatedExtrinsic,
541
0
542
0
    /// The currency with the given ID has not been registered.
543
0
    CurrencyNotRegistered,
544
0
  }
545
0
546
0
  #[pallet::genesis_config]
547
0
  pub struct GenesisConfig<T: Config> {
548
0
    pub authorities: Vec<T::AccountId>,
549
0
    pub legacy_wallets: Vec<(LegacySighash, T::Balance)>,
550
0
    pub legacy_balance_keeper: Option<T::AccountId>,
551
0
  }
552
0
553
0
  #[cfg(feature = "std")]
554
0
  impl<T: Config> Default for GenesisConfig<T> {
555
3
    fn default() -> Self {
556
3
      Self {
557
3
        authorities: Vec::new(),
558
3
        legacy_wallets: Vec::new(),
559
3
        legacy_balance_keeper: None,
560
3
      }
561
3
    }
562
0
  }
563
0
564
2
  #[pallet::genesis_build]
565
0
  impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
566
303
    fn build(&self) {
567
333
      for 
authority30
in &self.authorities {
568
30
        Authorities::<T>::insert(authority.clone(), ());
569
30
      }
570
304
      for (
sighash, balance1
) in &self.legacy_wallets {
571
1
        LegacyWallets::<T>::insert(sighash, balance);
572
1
      }
573
303
      if let Some(
acct1
) = &self.legacy_balance_keeper {
574
1
        LegacyBalanceKeeper::<T>::put(acct.clone());
575
302
      }
576
303
    }
577
0
  }
578
0
579
0
  #[pallet::hooks]
580
0
  impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
581
75
    fn on_initialize(block_number: T::BlockNumber) -> Weight {
582
75
      log::debug!("Cleaning up expired entries");
583
0
584
75
      let unverified_task_count = match PendingTasks::<T>::remove_prefix(block_number, None) {
585
75
        KillStorageResult::SomeRemaining(
u0
) | KillStorageResult::AllRemoved(u) => u,
586
0
      };
587
0
588
75
      let ask_count = match AskOrders::<T>::remove_prefix(block_number, None) {
589
75
        KillStorageResult::SomeRemaining(
u0
) | KillStorageResult::AllRemoved(u) => u,
590
0
      };
591
75
      let bid_count = match BidOrders::<T>::remove_prefix(block_number, None) {
592
75
        KillStorageResult::SomeRemaining(
u0
) | KillStorageResult::AllRemoved(u) => u,
593
0
      };
594
75
      let offer_count = match Offers::<T>::remove_prefix(block_number, None) {
595
75
        KillStorageResult::SomeRemaining(
u0
) | KillStorageResult::AllRemoved(u) => u,
596
75
      };
597
75
598
75
      let mut deals_count = 0u32;
599
75
      let deals_to_keep: Vec<_> = DealOrders::<T>::drain_prefix(block_number)
600
75
        .filter_map(|(hash, deal)| {
601
14
          deals_count = deals_count.saturating_add(1);
602
14
          if deal.funding_transfer_id.is_some() {
603
7
            Some((DealOrderId::with_expiration_hash::<T>(block_number, hash), deal))
604
0
          } else {
605
7
            None
606
0
          }
607
75
        })
608
75
        .collect();
609
75
      let funded_deals_count = deals_to_keep.len().unique_saturated_into();
610
75
      let deals_count = deals_count.saturating_sub(funded_deals_count);
611
82
      for (
key, deal7
) in deals_to_keep {
612
7
        DealOrders::<T>::insert_id(key, deal);
613
7
      }
614
0
615
75
      <T as Config>::WeightInfo::on_initialize(
616
75
        ask_count,
617
75
        bid_count,
618
75
        offer_count,
619
75
        deals_count,
620
75
        funded_deals_count,
621
75
        unverified_task_count,
622
75
        0,
623
75
      )
624
75
    }
625
0
626
30
    fn offchain_worker(block_number: T::BlockNumber) {
627
30
      let 
auth_id29
= match Self::authority_id() {
628
0
        None => {
629
1
          log::debug!(target: "OCW", "Not authority, skipping off chain work");
630
1
          return;
631
0
        },
632
29
        Some(auth) => T::FromAccountId::from(auth),
633
0
      };
634
0
635
59
      for (
deadline, id, task30
) in PendingTasks::<T>::iter() {
636
30
        let storage_key = crate::ocw::tasks::storage_key(&id);
637
30
        let offset =
638
30
          T::UnverifiedTaskTimeout::get().saturated_into::<u32>().saturating_sub(2u32);
639
30
640
30
        let mut lock = StorageLock::<BlockAndTime<frame_system::Pallet<T>>>::with_block_and_time_deadline(
641
30
          &storage_key,
642
30
          offset,
643
30
          Duration::from_millis(0),
644
30
        );
645
0
646
30
        let 
guard16
= match lock.try_lock() {
647
16
          Ok(g) => g,
648
14
          Err(_) => continue,
649
0
        };
650
0
651
16
        if match &id {
652
9
          TaskId::VerifyTransfer(id) => Transfers::<T>::contains_key(id),
653
7
          TaskId::CollectCoins(id) => CollectedCoins::<T>::contains_key(id),
654
0
        } {
655
1
          log::debug!("Already handled Task ({:?}, {:?})", deadline, id);
656
1
          guard.forget();
657
1
          continue;
658
15
        }
659
15
660
15
        let result = task.verify_ocw::<T>();
661
15
662
15
        log::trace!(target: "OCW", "@{block_number:?} Task {:8?}", id);
663
0
664
7
        match result {
665
8
          Ok(task_data) => {
666
8
            let output = task_data.into_output::<T>();
667
8
            match Self::submit_txn_with_synced_nonce(auth_id.clone(), |_| {
668
8
              Call::persist_task_output { deadline, task_output: output.clone() }
669
8
            }) {
670
7
              Ok(_) => guard.forget(),
671
1
              Err(e) => {
672
1
                log::error!(
673
1
                  "Failed to send persist dispatchable transaction: {:?}",
674
1
                  e
675
1
                )
676
0
              },
677
0
            }
678
0
          },
679
5
          Err((task, ocw::OffchainError::InvalidTask(cause))) => {
680
5
            log::warn!("Failed to verify pending task {:?} : {:?}", task, cause);
681
5
            if cause.is_fatal() {
682
3
              match Self::submit_txn_with_synced_nonce(auth_id.clone(), |_| {
683
3
                Call::fail_task { deadline, task_id: id.clone(), cause }
684
3
              }) {
685
1
                Err(e) => log::error!(
686
1
                  "Failed to send fail dispatchable transaction: {:?}",
687
1
                  e
688
1
                ),
689
2
                Ok(_) => guard.forget(),
690
0
              }
691
2
            }
692
0
          },
693
2
          Err(error) => {
694
2
            log::error!("Task verification encountered an error {:?}", error);
695
0
          },
696
0
        }
697
0
      }
698
30
    }
699
0
700
0
    fn on_runtime_upgrade() -> Weight {
701
0
      migrations::migrate::<T>()
702
0
    }
703
0
  }
704
0
705
31
  #[pallet::
c1
all]
706
  impl<T: Config> Pallet<T> {
707
    /// Claims legacy wallet and transfers the balance to the sender's account.
708
    #[pallet::weight(<T as Config>::WeightInfo::claim_legacy_wallet())]
709
1
    pub fn claim_legacy_wallet(
710
1
      origin: OriginFor<T>,
711
1
      public_key: sp_core::ecdsa::Public,
712
1
    ) -> DispatchResultWithPostInfo {
713
1
      let who = ensure_signed(origin)
?0
;
714
1
      let account_id_of_key = T::Signer::from(public_key.clone()).into_account();
715
1
      ensure!(account_id_of_key == who, 
Error::<T>::NotLegacyWalletOwner0
);
716
717
1
      let sighash = LegacySighash::from(&public_key);
718
719
1
      let legacy_balance =
720
1
        LegacyWallets::<T>::get(&sighash).ok_or(Error::<T>::LegacyWalletNotFound)
?0
;
721
722
1
      let legacy_keeper =
723
1
        LegacyBalanceKeeper::<T>::get().ok_or(Error::<T>::LegacyBalanceKeeperMissing)
?0
;
724
725
1
      <pallet_balances::Pallet<T> as CurrencyT<T::AccountId>>::transfer(
726
1
        &legacy_keeper,
727
1
        &who,
728
1
        legacy_balance,
729
1
        ExistenceRequirement::AllowDeath,
730
1
      )
?0
;
731
1
      LegacyWallets::<T>::remove(&sighash);
732
1
      Self::deposit_event(Event::<T>::LegacyWalletClaimed(who, sighash, legacy_balance));
733
1
734
1
      Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::No })
735
1
    }
736
737
    /// Registers an external address on `blockchain` and `network` with value `address`
738
    #[pallet::weight(<T as Config>::WeightInfo::register_address())]
739
427
    pub fn register_address(
740
427
      origin: OriginFor<T>,
741
427
      blockchain: Blockchain,
742
427
      address: ExternalAddress,
743
427
      ownership_proof: sp_core::ecdsa::Signature,
744
427
    ) -> DispatchResult {
745
427
      let 
who426
= ensure_signed(origin)
?1
;
746
747
426
      let message = sp_io::hashing::sha2_256(who.encode().as_slice());
748
426
      let message = &sp_io::hashing::blake2_256(message.as_ref());
749
426
      let signature = <[u8; 65]>::from(ownership_proof);
750
426
      let 
raw_pubkey425
= secp256k1_ecdsa_recover_compressed(&signature, message)
751
426
        .map_err(|_| 
Error::<T>::InvalidSignature1
)
?1
;
752
425
      let 
recreated_address424
= helpers::generate_external_address(
753
425
        &blockchain,
754
425
        &address,
755
425
        sp_core::ecdsa::Public::from_raw(raw_pubkey),
756
425
      )
757
425
      .ok_or(Error::<T>::AddressFormatNotSupported)
?1
;
758
424
      ensure!(recreated_address == address, 
Error::<T>::OwnershipNotSatisfied1
);
759
760
423
      let address_id = AddressId::new::<T>(&blockchain, &address);
761
423
      ensure!(
762
423
        !Addresses::<T>::contains_key(&address_id),
763
1
        Error::<T>::AddressAlreadyRegistered
764
      );
765
766
      // note: this error condition is unreachable!
767
      // AddressFormatNotSupported or OwnershipNotSatisfied will error out first
768
422
      ensure!(
769
422
        helpers::address_is_well_formed(&blockchain, &address),
770
0
        Error::<T>::MalformedExternalAddress
771
      );
772
773
422
      let entry = Address { blockchain, value: address, owner: who };
774
422
      Self::deposit_event(Event::<T>::AddressRegistered(address_id.clone(), entry.clone()));
775
422
      <Addresses<T>>::insert(address_id, entry);
776
422
777
422
      Ok(())
778
427
    }
779
780
    #[pallet::weight(<T as Config>::WeightInfo::add_ask_order())]
781
148
    pub fn add_ask_order(
782
148
      origin: OriginFor<T>,
783
148
      address_id: AddressId<T::Hash>,
784
148
      terms: LoanTerms<T::Hash>,
785
148
      expiration_block: BlockNumberFor<T>,
786
148
      guid: Guid,
787
148
    ) -> DispatchResult {
788
148
      let who = ensure_signed(origin)
?0
;
789
790
148
      let head = Self::block_number();
791
148
      ensure!(expiration_block >= head, 
Error::<T>::AskOrderExpired1
);
792
793
147
      let ask_order_id = AskOrderId::new::<T>(expiration_block, &guid);
794
147
      ensure!(!AskOrders::<T>::contains_id(&ask_order_id), 
Error::<T>::DuplicateId1
);
795
796
146
      let address = Self::get_address(&address_id)
?0
;
797
146
      ensure!(address.owner == who, 
Error::<T>::NotAddressOwner0
);
798
799
144
      let currency =
800
146
        Currencies::<T>::get(&terms.currency).ok_or(Error::<T>::CurrencyNotRegistered)
?2
;
801
144
      ensure!(
802
144
        address.blockchain == currency.blockchain(),
803
1
        Error::<T>::AddressBlockchainMismatch
804
      );
805
806
143
      Self::use_guid(&guid)
?2
;
807
808
141
      let ask_order = AskOrder {
809
141
        lender_address_id: address_id,
810
141
        terms: terms.try_into().map_err(Error::<T>::from)
?0
,
811
141
        expiration_block,
812
141
        block: <frame_system::Pallet<T>>::block_number(),
813
141
        lender: who,
814
141
      };
815
141
816
141
      Self::deposit_event(Event::<T>::AskOrderAdded(ask_order_id.clone(), ask_order.clone()));
817
141
      AskOrders::<T>::insert_id(ask_order_id, ask_order);
818
141
      Ok(())
819
148
    }
820
821
    #[pallet::weight(<T as Config>::WeightInfo::add_bid_order())]
822
146
    pub fn add_bid_order(
823
146
      origin: OriginFor<T>,
824
146
      address_id: AddressId<T::Hash>,
825
146
      terms: LoanTerms<T::Hash>,
826
146
      expiration_block: BlockNumberFor<T>,
827
146
      guid: Guid,
828
146
    ) -> DispatchResult {
829
146
      let who = ensure_signed(origin)
?0
;
830
831
146
      let head = Self::block_number();
832
146
      ensure!(expiration_block >= head, 
Error::<T>::BidOrderExpired1
);
833
834
145
      let bid_order_id = BidOrderId::new::<T>(expiration_block, &guid);
835
145
      ensure!(!BidOrders::<T>::contains_id(&bid_order_id), 
Error::<T>::DuplicateId1
);
836
837
144
      let address = Self::get_address(&address_id)
?0
;
838
144
      ensure!(address.owner == who, 
Error::<T>::NotAddressOwner0
);
839
840
143
      let currency =
841
144
        Currencies::<T>::get(&terms.currency).ok_or(Error::<T>::CurrencyNotRegistered)
?1
;
842
143
      ensure!(
843
143
        address.blockchain == currency.blockchain(),
844
1
        Error::<T>::AddressBlockchainMismatch
845
      );
846
847
142
      Self::use_guid(&guid)
?0
;
848
849
141
      let bid_order = BidOrder {
850
142
        borrower_address_id: address_id,
851
142
        terms: terms.try_into().map_err(Error::<T>::from)
?1
,
852
141
        expiration_block,
853
141
        block: <frame_system::Pallet<T>>::block_number(),
854
141
        borrower: who,
855
141
      };
856
141
857
141
      Self::deposit_event(Event::<T>::BidOrderAdded(bid_order_id.clone(), bid_order.clone()));
858
141
      BidOrders::<T>::insert_id(bid_order_id, bid_order);
859
141
      Ok(())
860
146
    }
861
862
    #[pallet::weight(<T as Config>::WeightInfo::add_offer())]
863
132
    pub fn add_offer(
864
132
      origin: OriginFor<T>,
865
132
      ask_order_id: AskOrderId<T::BlockNumber, T::Hash>,
866
132
      bid_order_id: BidOrderId<T::BlockNumber, T::Hash>,
867
132
      expiration_block: BlockNumberFor<T>,
868
132
    ) -> DispatchResult {
869
132
      let who = ensure_signed(origin)
?0
;
870
871
132
      let ask_order = try_get_id!(AskOrders<T>, &ask_order_id, NonExistentAskOrder)
?0
;
872
873
132
      ensure!(ask_order.lender == who, 
Error::<T>::NotLender0
);
874
875
132
      let head = Self::block_number();
876
132
877
132
      ensure!(ask_order.expiration_block >= head, 
Error::<T>::AskOrderExpired0
);
878
879
132
      let bid_order = try_get_id!(BidOrders<T>, &bid_order_id, NonExistentBidOrder)
?0
;
880
881
132
      ensure!(bid_order.borrower != who, 
Error::<T>::SameOwner0
);
882
883
132
      ensure!(bid_order.expiration_block >= head, 
Error::<T>::BidOrderExpired0
);
884
885
132
      let lender_address = Self::get_address(&ask_order.lender_address_id)
?0
;
886
132
      let borrower_address = Self::get_address(&bid_order.borrower_address_id)
?0
;
887
888
132
      ensure!(
889
132
        lender_address.blockchain == borrower_address.blockchain,
890
1
        Error::<T>::AddressBlockchainMismatch
891
      );
892
893
131
      ensure!(ask_order.terms.match_with(&bid_order.terms), 
Error::<T>::AskBidMismatch0
);
894
895
131
      let offer_id = OfferId::new::<T>(expiration_block, &ask_order_id, &bid_order_id);
896
131
897
131
      ensure!(!Offers::<T>::contains_id(&offer_id), 
Error::<T>::DuplicateOffer1
);
898
899
130
      let offer = Offer {
900
130
        ask_id: ask_order_id,
901
130
        bid_id: bid_order_id,
902
130
        block: Self::block_number(),
903
130
        expiration_block,
904
130
        lender: who,
905
130
      };
906
130
907
130
      Self::deposit_event(Event::<T>::OfferAdded(offer_id.clone(), offer.clone()));
908
130
      Offers::<T>::insert_id(offer_id, offer);
909
130
910
130
      Ok(())
911
132
    }
912
913
    #[pallet::weight(<T as Config>::WeightInfo::add_deal_order())]
914
119
    pub fn add_deal_order(
915
119
      origin: OriginFor<T>,
916
119
      offer_id: OfferId<T::BlockNumber, T::Hash>,
917
119
      expiration_block: BlockNumberFor<T>,
918
119
    ) -> DispatchResult {
919
119
      let who = ensure_signed(origin)
?0
;
920
921
119
      let deal_order_id = DealOrderId::new::<T>(expiration_block, &offer_id);
922
119
      ensure!(!DealOrders::<T>::contains_id(&deal_order_id), 
Error::<T>::DuplicateDealOrder1
);
923
924
118
      let offer = try_get_id!(Offers<T>, &offer_id, NonExistentOffer)
?0
;
925
926
118
      let head = Self::block_number();
927
118
928
118
      ensure!(offer.expiration_block >= head, 
Error::<T>::OfferExpired0
);
929
930
118
      let ask_order = try_get_id!(AskOrders<T>, &offer.ask_id, NonExistentAskOrder)
?0
;
931
932
118
      let bid_order = try_get_id!(BidOrders<T>, &offer.bid_id, NonExistentBidOrder)
?0
;
933
934
118
      ensure!(bid_order.borrower == who, 
Error::<T>::NotBorrower0
);
935
936
118
      let agreed_terms = ask_order
937
118
        .terms
938
118
        .agreed_terms(bid_order.terms)
939
118
        .ok_or(Error::<T>::AskBidMismatch)
?0
;
940
941
118
      let deal_order = DealOrder {
942
118
        offer_id,
943
118
        lender_address_id: ask_order.lender_address_id,
944
118
        borrower_address_id: bid_order.borrower_address_id,
945
118
        terms: agreed_terms,
946
118
        expiration_block,
947
118
        block: Some(Self::block_number()),
948
118
        timestamp: Self::timestamp(),
949
118
        borrower: who,
950
118
        funding_transfer_id: None,
951
118
        lock: None,
952
118
        repayment_transfer_id: None,
953
118
      };
954
118
955
118
      Self::deposit_event(Event::<T>::DealOrderAdded(
956
118
        deal_order_id.clone(),
957
118
        deal_order.clone(),
958
118
      ));
959
118
      DealOrders::<T>::insert_id(deal_order_id, deal_order);
960
118
961
118
      Ok(())
962
119
    }
963
964
    #[pallet::weight(<T as Config>::WeightInfo::lock_deal_order())]
965
8
    pub fn lock_deal_order(
966
8
      origin: OriginFor<T>,
967
8
      deal_order_id: DealOrderId<T::BlockNumber, T::Hash>,
968
8
    ) -> DispatchResult {
969
8
      let 
who7
= ensure_signed(origin)
?1
;
970
971
7
      DealOrders::<T>::try_mutate(
972
7
        deal_order_id.expiration(),
973
7
        deal_order_id.hash(),
974
7
        |value| -> DispatchResult {
975
7
          let 
deal_order6
= value.as_mut().ok_or(Error::<T>::NonExistentDealOrder)
?1
;
976
977
6
          ensure!(deal_order.lock.is_none(), 
Error::<T>::DealOrderAlreadyLocked1
);
978
5
          ensure!(deal_order.funding_transfer_id.is_some(), 
Error::<T>::DealNotFunded1
);
979
4
          ensure!(deal_order.borrower == who, 
Error::<T>::NotBorrower1
);
980
981
3
          deal_order.lock = Some(who);
982
3
          Self::deposit_event(Event::<T>::DealOrderLocked(deal_order_id.clone()));
983
3
          Ok(())
984
7
        },
985
7
      )
?4
;
986
987
3
      Ok(())
988
8
    }
989
990
    #[pallet::weight(<T as Config>::WeightInfo::fund_deal_order())]
991
21
    pub fn fund_deal_order(
992
21
      origin: OriginFor<T>,
993
21
      deal_order_id: DealOrderId<T::BlockNumber, T::Hash>,
994
21
      transfer_id: TransferId<T::Hash>,
995
21
    ) -> DispatchResult {
996
21
      let 
who20
= ensure_signed(origin)
?1
;
997
998
20
      Self::try_mutate_deal_order_and_transfer(
999
20
        &deal_order_id,
1000
20
        &transfer_id,
1001
20
        |deal_order| {
1002
19
          let lender =
1003
21
            try_get!(Addresses<T>, &deal_order.lender_address_id, NonExistentAddress)
?1
;
1004
1005
19
          ensure!(lender.owner == who, 
Error::<T>::NotLender1
);
1006
1007
18
          let now = Self::timestamp();
1008
18
          ensure!(now >= deal_order.timestamp, 
Error::<T>::MalformedDealOrder1
);
1009
1010
17
          ensure!(
1011
17
            deal_order.funding_transfer_id.is_none(),
1012
1
            Error::<T>::DealOrderAlreadyFunded
1013
          );
1014
16
          let head = Self::block_number();
1015
16
          ensure!(deal_order.expiration_block >= head, 
Error::<T>::DealOrderExpired1
);
1016
1017
15
          deal_order.funding_transfer_id = Some(transfer_id.clone());
1018
15
          deal_order.timestamp = now;
1019
15
1020
15
          Ok(Some(Event::<T>::DealOrderFunded(deal_order_id.clone())))
1021
20
        },
1022
20
        |transfer, deal_order| {
1023
15
          ensure!(
1024
15
            transfer.deal_order_id == deal_order_id.clone(),
1025
1
            Error::<T>::TransferDealOrderMismatch
1026
          );
1027
14
          ensure!(
1028
14
            transfer.amount == deal_order.terms.amount,
1029
1
            Error::<T>::TransferAmountMismatch
1030
          );
1031
13
          ensure!(transfer.account_id == who, 
Error::<T>::TransferAccountMismatch1
);
1032
12
          ensure!(!transfer.is_processed, 
Error::<T>::TransferAlreadyProcessed1
);
1033
1034
11
          transfer.is_processed = true;
1035
11
          Ok(Some(Event::<T>::TransferProcessed(transfer_id.clone())))
1036
20
        },
1037
20
      )
?9
;
1038
1039
11
      Ok(())
1040
21
    }
1041
1042
    #[pallet::weight(<T as Config>::WeightInfo::register_deal_order())]
1043
14
    pub fn register_deal_order(
1044
14
      origin: OriginFor<T>,
1045
14
      lender_address_id: AddressId<T::Hash>,
1046
14
      borrower_address_id: AddressId<T::Hash>,
1047
14
      terms: LoanTerms<T::Hash>,
1048
14
      expiration_block: BlockNumberFor<T>,
1049
14
      ask_guid: Guid,
1050
14
      bid_guid: Guid,
1051
14
      borrower_key: T::Signer,
1052
14
      borrower_signature: T::SignerSignature,
1053
14
    ) -> DispatchResult {
1054
14
      let 
lender_account13
= ensure_signed(origin)
?1
;
1055
13
      let borrower_account = borrower_key.into_account();
1056
13
1057
13
      let message = expiration_block
1058
13
        .encode()
1059
13
        .into_iter()
1060
13
        .chain(ask_guid.encode())
1061
13
        .chain(bid_guid.encode())
1062
13
        .chain(terms.encode())
1063
13
        .collect::<Vec<u8>>();
1064
13
1065
13
      ensure!(
1066
13
        borrower_signature.verify(message.as_slice(), &borrower_account),
1067
1
        Error::<T>::InvalidSignature
1068
      );
1069
1070
11
      let currency =
1071
12
        Currencies::<T>::get(&terms.currency).ok_or(Error::<T>::CurrencyNotRegistered)
?1
;
1072
1073
11
      let borrower = Self::get_address(&borrower_address_id)
?0
;
1074
11
      ensure!(borrower.owner == borrower_account, 
Error::<T>::NotAddressOwner1
);
1075
1076
10
      let lender = Self::get_address(&lender_address_id)
?0
;
1077
10
      ensure!(lender.owner == lender_account, 
Error::<T>::NotAddressOwner1
);
1078
1079
9
      ensure!(lender.matches_chain_of(&borrower), 
Error::<T>::AddressBlockchainMismatch1
);
1080
1081
8
      ensure!(
1082
8
        lender.blockchain == currency.blockchain(),
1083
1
        Error::<T>::AddressBlockchainMismatch
1084
      );
1085
1086
7
      let ask_order_id = AskOrderId::new::<T>(expiration_block, &ask_guid);
1087
7
      ensure!(!AskOrders::<T>::contains_id(&ask_order_id), 
Error::<T>::DuplicateId1
);
1088
1089
6
      let bid_order_id = BidOrderId::new::<T>(expiration_block, &bid_guid);
1090
6
      ensure!(!BidOrders::<T>::contains_id(&bid_order_id), 
Error::<T>::DuplicateId1
);
1091
1092
5
      let offer_id = OfferId::new::<T>(expiration_block, &ask_order_id, &bid_order_id);
1093
5
      ensure!(!Offers::<T>::contains_id(&offer_id), 
Error::<T>::DuplicateOffer1
);
1094
1095
4
      let deal_order_id = DealOrderId::new::<T>(expiration_block, &offer_id);
1096
4
      ensure!(!DealOrders::<T>::contains_id(&deal_order_id), 
Error::<T>::DuplicateDealOrder1
);
1097
1098
3
      let current_block = Self::block_number();
1099
1100
3
      let ask_order = AskOrder {
1101
3
        lender_address_id: lender_address_id.clone(),
1102
3
        terms: terms.clone().try_into().map_err(Error::<T>::from)
?0
,
1103
3
        expiration_block,
1104
3
        block: current_block,
1105
3
        lender: lender_account.clone(),
1106
      };
1107
1108
3
      let bid_order = BidOrder {
1109
3
        borrower_address_id: borrower_address_id.clone(),
1110
3
        terms: terms.clone().try_into().map_err(Error::<T>::from)
?0
,
1111
3
        expiration_block,
1112
3
        block: current_block,
1113
3
        borrower: borrower_account.clone(),
1114
3
      };
1115
3
1116
3
      let offer = Offer {
1117
3
        ask_id: ask_order_id.clone(),
1118
3
        bid_id: bid_order_id.clone(),
1119
3
        block: current_block,
1120
3
        expiration_block,
1121
3
        lender: lender_account,
1122
3
      };
1123
3
1124
3
      let deal_order = DealOrder {
1125
3
        offer_id: offer_id.clone(),
1126
3
        lender_address_id,
1127
3
        borrower_address_id,
1128
3
        terms,
1129
3
        expiration_block,
1130
3
        timestamp: Self::timestamp(),
1131
3
        block: Some(Self::block_number()),
1132
3
        borrower: borrower_account,
1133
3
        funding_transfer_id: None,
1134
3
        lock: None,
1135
3
        repayment_transfer_id: None,
1136
3
      };
1137
3
1138
3
      AskOrders::<T>::insert_id(ask_order_id.clone(), ask_order.clone());
1139
3
      Self::deposit_event(Event::<T>::AskOrderAdded(ask_order_id, ask_order));
1140
3
1141
3
      BidOrders::<T>::insert_id(bid_order_id.clone(), bid_order.clone());
1142
3
      Self::deposit_event(Event::<T>::BidOrderAdded(bid_order_id, bid_order));
1143
3
1144
3
      Offers::<T>::insert_id(offer_id.clone(), offer.clone());
1145
3
      Self::deposit_event(Event::<T>::OfferAdded(offer_id, offer));
1146
3
1147
3
      DealOrders::<T>::insert_id(deal_order_id.clone(), deal_order.clone());
1148
3
      Self::deposit_event(Event::<T>::DealOrderAdded(deal_order_id, deal_order));
1149
3
      Ok(())
1150
14
    }
1151
1152
    #[pallet::weight(<T as Config>::WeightInfo::close_deal_order())]
1153
11
    pub fn close_deal_order(
1154
11
      origin: OriginFor<T>,
1155
11
      deal_order_id: DealOrderId<T::BlockNumber, T::Hash>,
1156
11
      transfer_id: TransferId<T::Hash>,
1157
11
    ) -> DispatchResult {
1158
11
      let 
who10
= ensure_signed(origin)
?1
;
1159
1160
10
      Self::try_mutate_deal_order_and_transfer(
1161
10
        &deal_order_id,
1162
10
        &transfer_id,
1163
10
        |deal_order| {
1164
11
          let 
borrower9
= try_get!(
1165
11
            Addresses<T>,
1166
11
            &deal_order.borrower_address_id,
1167
11
            NonExistentAddress
1168
11
          )
?1
;
1169
1170
9
          ensure!(borrower.owner == who, 
Error::<T>::NotBorrower1
);
1171
1172
8
          let now = Self::timestamp();
1173
8
          ensure!(now >= deal_order.timestamp, 
Error::<T>::MalformedDealOrder1
);
1174
1175
7
          ensure!(
1176
7
            deal_order.repayment_transfer_id.is_none(),
1177
1
            Error::<T>::DealOrderAlreadyClosed
1178
          );
1179
1180
6
          ensure!(deal_order.lock.is_some(), 
Error::<T>::DealOrderMustBeLocked1
);
1181
1182
5
          deal_order.repayment_transfer_id = Some(transfer_id.clone());
1183
5
1184
5
          Ok(Some(Event::<T>::DealOrderClosed(deal_order_id.clone())))
1185
10
        },
1186
10
        |transfer, _deal_order| {
1187
5
          ensure!(
1188
5
            transfer.deal_order_id == deal_order_id.clone(),
1189
1
            Error::<T>::TransferDealOrderMismatch
1190
          );
1191
1192
4
          ensure!(transfer.block <= Self::block_number(), 
Error::<T>::MalformedTransfer1
);
1193
3
          ensure!(transfer.account_id == who, 
Error::<T>::TransferAccountMismatch1
);
1194
2
          ensure!(!transfer.is_processed, 
Error::<T>::TransferAlreadyProcessed1
);
1195
1196
1
          transfer.is_processed = true;
1197
1
          Ok(Some(Event::<T>::TransferProcessed(transfer_id.clone())))
1198
10
        },
1199
10
      )
?9
;
1200
1201
1
      Ok(())
1202
11
    }
1203
1204
15
    #[transactional]
1205
    #[pallet::weight(<T as Config>::WeightInfo::request_collect_coins())]
1206
    pub fn request_collect_coins(
1207
      origin: OriginFor<T>,
1208
      evm_address: ExternalAddress,
1209
      tx_id: ExternalTxId,
1210
    ) -> DispatchResult {
1211
      let who = ensure_signed(origin)?;
1212
1213
      let contract = Self::collect_coins_contract();
1214
      let contract_chain = &contract.chain;
1215
1216
      let collect_coins_id = CollectedCoinsId::new::<T>(contract_chain, &tx_id);
1217
      ensure!(
1218
        !CollectedCoins::<T>::contains_key(&collect_coins_id),
1219
        Error::<T>::CollectCoinsAlreadyRegistered
1220
      );
1221
1222
      let deadline = Self::block_number().saturating_add(T::UnverifiedTaskTimeout::get());
1223
1224
      ensure!(
1225
        !PendingTasks::<T>::contains_key(deadline, &TaskId::from(collect_coins_id.clone())),
1226
        Error::<T>::CollectCoinsAlreadyRegistered
1227
      );
1228
1229
      let address_id = AddressId::new::<T>(contract_chain, &evm_address);
1230
      let address = Self::addresses(&address_id).ok_or(Error::<T>::NonExistentAddress)?;
1231
      ensure!(address.owner == who, Error::<T>::NotAddressOwner);
1232
1233
      let pending = types::UnverifiedCollectedCoins { to: evm_address, tx_id, contract };
1234
1235
      PendingTasks::<T>::insert(
1236
        deadline,
1237
        TaskId::from(collect_coins_id.clone()),
1238
        Task::from(pending.clone()),
1239
      );
1240
1241
      Self::deposit_event(Event::<T>::CollectCoinsRegistered(collect_coins_id, pending));
1242
1243
      Ok(())
1244
    }
1245
1246
5
    #[transactional]
1247
    #[pallet::weight(<T as Config>::WeightInfo::register_funding_transfer_legacy())]
1248
    pub fn register_funding_transfer_legacy(
1249
      origin: OriginFor<T>,
1250
      transfer_kind: LegacyTransferKind,
1251
      deal_order_id: DealOrderId<T::BlockNumber, T::Hash>,
1252
      blockchain_tx_id: ExternalTxId,
1253
    ) -> DispatchResult {
1254
      let who = ensure_signed(origin)?;
1255
1256
1
      let order = try_get_id!(DealOrders<T>, &deal_order_id, NonExistentDealOrder)?;
1257
1258
      ensure!(order.terms.currency.is_placeholder(), Error::<T>::DeprecatedExtrinsic);
1259
1260
      let (transfer_id, transfer) = Self::register_transfer_internal_legacy(
1261
        who,
1262
        order.lender_address_id,
1263
        order.borrower_address_id,
1264
        transfer_kind,
1265
        order.terms.amount,
1266
        deal_order_id,
1267
        blockchain_tx_id,
1268
      )?;
1269
      Self::deposit_event(Event::<T>::TransferRegistered(transfer_id, transfer));
1270
1271
      Ok(())
1272
    }
1273
1274
6
    #[transacti
onal]2
1275
    #[pallet::weight(<T as Config>::WeightInfo::register_repayment_transfer_legacy())]
1276
    pub fn register_repayment_transfer_legacy(
1277
      origin: OriginFor<T>,
1278
      transfer_kind: LegacyTransferKind,
1279
      repayment_amount: ExternalAmount,
1280
      deal_order_id: DealOrderId<T::BlockNumber, T::Hash>,
1281
      blockchain_tx_id: ExternalTxId,
1282
    ) -> DispatchResult {
1283
      let who = ensure_signed(origin)?;
1284
1285
1
      let order = try_get_id!(DealOrders<T>, &deal_order_id, NonExistentDealOrder)?;
1286
1287
      ensure!(order.terms.currency.is_placeholder(), Error::<T>::DeprecatedExtrinsic);
1288
1289
      let (transfer_id, transfer) = Self::register_transfer_internal_legacy(
1290
        who,
1291
        order.borrower_address_id,
1292
        order.lender_address_id,
1293
        transfer_kind,
1294
        repayment_amount,
1295
        deal_order_id,
1296
        blockchain_tx_id,
1297
      )?;
1298
      Self::deposit_event(Event::<T>::TransferRegistered(transfer_id, transfer));
1299
1300
      Ok(())
1301
    }
1302
1303
42
    #[transactional]
1304
    #[pallet::weight(<T as Config>::WeightInfo::register_funding_transfer())]
1305
    pub fn register_funding_transfer(
1306
      origin: OriginFor<T>,
1307
      transfer_kind: TransferKind,
1308
      deal_order_id: DealOrderId<T::BlockNumber, T::Hash>,
1309
      blockchain_tx_id: ExternalTxId,
1310
    ) -> DispatchResult {
1311
      let who = ensure_signed(origin)?;
1312
1313
1
      let order = try_get_id!(DealOrders<T>, &deal_order_id, NonExistentDealOrder)?;
1314
1315
      let (transfer_id, transfer) = Self::register_transfer_internal(
1316
        who,
1317
        order.lender_address_id,
1318
        order.borrower_address_id,
1319
        transfer_kind,
1320
        order.terms.amount,
1321
        deal_order_id,
1322
        blockchain_tx_id,
1323
        &order.terms.currency,
1324
      )?;
1325
      Self::deposit_event(Event::<T>::TransferRegistered(transfer_id, transfer));
1326
1327
      Ok(())
1328
    }
1329
1330
14
    #[transacti
onal]6
1331
    #[pallet::weight(<T as Config>::WeightInfo::register_repayment_transfer())]
1332
    pub fn register_repayment_transfer(
1333
      origin: OriginFor<T>,
1334
      transfer_kind: TransferKind,
1335
      repayment_amount: ExternalAmount,
1336
      deal_order_id: DealOrderId<T::BlockNumber, T::Hash>,
1337
      blockchain_tx_id: ExternalTxId,
1338
    ) -> DispatchResult {
1339
      let who = ensure_signed(origin)?;
1340
1341
1
      let order = try_get_id!(DealOrders<T>, &deal_order_id, NonExistentDealOrder)?;
1342
1343
      let (transfer_id, transfer) = Self::register_transfer_internal(
1344
        who,
1345
        order.borrower_address_id,
1346
        order.lender_address_id,
1347
        transfer_kind,
1348
        repayment_amount,
1349
        deal_order_id,
1350
        blockchain_tx_id,
1351
        &order.terms.currency,
1352
      )?;
1353
      Self::deposit_event(Event::<T>::TransferRegistered(transfer_id, transfer));
1354
1355
      Ok(())
1356
    }
1357
1358
    #[pallet::weight(<T as Config>::WeightInfo::exempt())]
1359
4
    pub fn exempt(
1360
4
      origin: OriginFor<T>,
1361
4
      deal_order_id: DealOrderId<T::BlockNumber, T::Hash>,
1362
4
    ) -> DispatchResult {
1363
4
      let 
who3
= ensure_signed(origin)
?1
;
1364
1365
3
      DealOrders::<T>::try_mutate(
1366
3
        &deal_order_id.expiration(),
1367
3
        &deal_order_id.hash(),
1368
3
        |value| -> DispatchResult {
1369
3
          let deal_order =
1370
3
            value.as_mut().ok_or(crate::Error::<T>::NonExistentDealOrder)
?0
;
1371
3
          ensure!(
1372
3
            deal_order.repayment_transfer_id.is_none(),
1373
1
            Error::<T>::DealOrderAlreadyClosed
1374
          );
1375
1376
2
          let lender = Self::get_address(&deal_order.lender_address_id)
?0
;
1377
2
          ensure!(who == lender.owner, 
Error::<T>::NotLender1
);
1378
1379
1
          let fake_transfer = Transfer {
1380
1
            deal_order_id: deal_order_id.clone(),
1381
1
            block: Self::block_number(),
1382
1
            account_id: who,
1383
1
            amount: ExternalAmount::zero(),
1384
1
            is_processed: true,
1385
1
            kind: TransferKind::Evm(EvmTransferKind::Ethless),
1386
1
            tx_id: ExternalTxId::try_from(b"0".to_vec()).expect(
1387
1
              "0 is a length of one which will always be < size bound of ExternalTxId",
1388
1
            ),
1389
1
            blockchain: lender.blockchain,
1390
1
            from: deal_order.lender_address_id.clone(),
1391
1
            to: deal_order.lender_address_id.clone(),
1392
1
            timestamp: Some(Self::timestamp()),
1393
1
          };
1394
1
          let fake_transfer_id =
1395
1
            TransferId::new::<T>(&fake_transfer.blockchain, &fake_transfer.tx_id);
1396
1
1397
1
          deal_order.repayment_transfer_id = Some(fake_transfer_id);
1398
1
1399
1
          Ok(())
1400
3
        },
1401
3
      )
?2
;
1402
1403
1
      Self::deposit_event(Event::<T>::LoanExempted(deal_order_id));
1404
1
      Ok(())
1405
4
    }
1406
1407
30
    #[trans
actional]14
1408
    #[pallet::weight(match &task_output {
1409
      crate::TaskOutput::CollectCoins(..) => <T as Config>::WeightInfo::persist_collect_coins(),
1410
      crate::TaskOutput::VerifyTransfer(..) => <T as Config>::WeightInfo::persist_transfer(),
1411
    })]
1412
    pub fn persist_task_output(
1413
      origin: OriginFor<T>,
1414
      deadline: T::BlockNumber,
1415
      task_output: TaskOutput<T::AccountId, T::Balance, T::BlockNumber, T::Hash, T::Moment>,
1416
    ) -> DispatchResultWithPostInfo {
1417
      let who = ensure_signed(origin)?;
1418
1419
      ensure!(Authorities::<T>::contains_key(&who), Error::<T>::InsufficientAuthority);
1420
1421
      let (task_id, event) = match task_output {
1422
        TaskOutput::VerifyTransfer(id, transfer) => {
1423
          ensure!(
1424
            !Transfers::<T>::contains_key(&id),
1425
            non_paying_error(Error::<T>::TransferAlreadyRegistered)
1426
          );
1427
1428
          let mut transfer = transfer;
1429
          transfer.block = frame_system::Pallet::<T>::block_number();
1430
1431
          Transfers::<T>::insert(&id, transfer);
1432
          (TaskId::from(id.clone()), Event::<T>::TransferVerified(id))
1433
        },
1434
        TaskOutput::CollectCoins(id, collected_coins) => {
1435
          ensure!(
1436
            !CollectedCoins::<T>::contains_key(&id),
1437
            non_paying_error(Error::<T>::CollectCoinsAlreadyRegistered)
1438
          );
1439
1440
          let address = Self::addresses(&collected_coins.to)
1441
            .ok_or(Error::<T>::NonExistentAddress)?;
1442
1443
          <pallet_balances::Pallet<T> as Mutate<T::AccountId>>::mint_into(
1444
            &address.owner,
1445
            collected_coins.amount,
1446
          )?;
1447
1448
          CollectedCoins::<T>::insert(&id, collected_coins.clone());
1449
          (
1450
            TaskId::from(id.clone()),
1451
            Event::<T>::CollectedCoinsMinted(id, collected_coins),
1452
          )
1453
        },
1454
      };
1455
1456
      PendingTasks::<T>::remove(&deadline, &task_id);
1457
1458
      Self::deposit_event(event);
1459
1460
      Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::No })
1461
    }
1462
1463
    #[pallet::weight(match &task_id {
1464
      crate::TaskId::VerifyTransfer(..) => <T as Config>::WeightInfo::fail_transfer(),
1465
      crate::TaskId::CollectCoins(..) => <T as Config>::WeightInfo::fail_collect_coins(),
1466
    })]
1467
8
    pub fn fail_task(
1468
8
      origin: OriginFor<T>,
1469
8
      deadline: T::BlockNumber,
1470
8
      task_id: TaskId<T::Hash>,
1471
8
      cause: VerificationFailureCause,
1472
8
    ) -> DispatchResultWithPostInfo {
1473
8
      let 
who6
= ensure_signed(origin)
?2
;
1474
1475
6
      ensure!(Authorities::<T>::contains_key(&who), 
Error::<T>::InsufficientAuthority2
);
1476
1477
4
      let 
event2
= match &task_id {
1478
2
        TaskId::VerifyTransfer(transfer_id) => {
1479
2
          ensure!(
1480
2
            !Transfers::<T>::contains_key(&transfer_id),
1481
1
            Error::<T>::TransferAlreadyRegistered
1482
          );
1483
1
          Event::<T>::TransferFailedVerification(transfer_id.clone(), cause)
1484
        },
1485
2
        TaskId::CollectCoins(collected_coins_id) => {
1486
2
          ensure!(
1487
2
            !CollectedCoins::<T>::contains_key(&collected_coins_id),
1488
1
            Error::<T>::CollectCoinsAlreadyRegistered
1489
          );
1490
1
          Event::<T>::CollectCoinsFailedVerification(collected_coins_id.clone(), cause)
1491
        },
1492
      };
1493
2
      PendingTasks::<T>::remove(&deadline, &task_id);
1494
2
      Self::deposit_event(event);
1495
2
1496
2
      Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::No })
1497
8
    }
1498
1499
    #[pallet::weight(<T as Config>::WeightInfo::add_authority())]
1500
    pub fn add_authority(
1501
      origin: OriginFor<T>,
1502
      who: T::AccountId,
1503
    ) -> DispatchResultWithPostInfo {
1504
8
      ensure_root(origin)
?1
;
1505
1506
7
      ensure!(!Authorities::<T>::contains_key(&who), 
Error::<T>::AlreadyAuthority1
);
1507
1508
6
      Authorities::<T>::insert(who, ());
1509
6
1510
6
      Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::No })
1511
8
    }
1512
1513
    #[pallet::weight(<T as Config>::WeightInfo::register_currency())]
1514
    pub fn register_currency(origin: OriginFor<T>, currency: Currency) -> DispatchResult {
1515
189
      ensure_root(origin)
?1
;
1516
1517
188
      let id = CurrencyId::new::<T>(&currency);
1518
188
1519
188
      ensure!(!Currencies::<T>::contains_key(&id), 
Error::<T>::CurrencyAlreadyRegistered1
);
1520
1521
187
      Currencies::<T>::insert(&id, &currency);
1522
187
      Self::deposit_event(Event::<T>::CurrencyRegistered(id, currency));
1523
187
1524
187
      Ok(())
1525
189
    }
1526
1527
3
    #[transactional]
1528
    #[pallet::weight(<T as Config>::WeightInfo::set_collect_coins_contract())]
1529
    pub fn set_collect_coins_contract(
1530
      origin: OriginFor<T>,
1531
      contract: GCreContract,
1532
    ) -> DispatchResult {
1533
      ensure_root(origin)?;
1534
      CollectCoinsContract::<T>::put(contract);
1535
      Ok(())
1536
    }
1537
1538
6
    #[trans
actional]2
1539
    #[pallet::weight(<T as Config>::WeightInfo::remove_authority())]
1540
    pub fn remove_authority(
1541
      origin: OriginFor<T>,
1542
      who: T::AccountId,
1543
    ) -> DispatchResultWithPostInfo {
1544
      ensure_root(origin)?;
1545
1546
      ensure!(Authorities::<T>::contains_key(&who), Error::<T>::NotAnAuthority);
1547
1548
      Authorities::<T>::remove(&who);
1549
1550
      Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::No })
1551
    }
1552
  }
1553
}