/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 | #[frame_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 | #[transactional]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 | #[transactional]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 | #[transactional]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>(¤cy); |
1518 | 188 | |
1519 | 188 | ensure!(!Currencies::<T>::contains_key(&id), Error::<T>::CurrencyAlreadyRegistered1 ); |
1520 | | |
1521 | 187 | Currencies::<T>::insert(&id, ¤cy); |
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 | #[transactional]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 | | } |