-
Notifications
You must be signed in to change notification settings - Fork 91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Loans: Add multiple triggers for write-off #1314
Conversation
Initial commit from Jeroen at: bf77eb6 |
18c6122
to
d250729
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some explanations of core parts:
Changes are ready for review. Migration is still pending |
8cc72bf
to
6a42e03
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new policy implementation looks great to me!
@wischli I think the migration is ok (tested in EDIT: based on #1198 (comment) I think I need to add it too to |
1b82c6b
to
ad045b9
Compare
This is ready for a final review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only have a few nitpicks. The migration read-write-count is off by one but the rest looks splendid to me, great work again!
pallets/loans-ref/src/lib.rs
Outdated
@@ -473,7 +480,7 @@ pub mod pallet { | |||
/// - Write off by admin with percentage 0.5 and penalty 0.2 | |||
/// - Time passes and the policy can be applied. | |||
/// - Write of with a policy that says: percentage 0.3, penaly 0.4 | |||
/// - The loan is written off with the maximum between the policy and the current state: | |||
/// - The loan is written off with the maximum between the policy and the current rule: | |||
/// percentage 0.5, penaly 0.4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit
/// percentage 0.5, penaly 0.4 | |
/// percentage 0.5, penalty 0.4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realizing that only means you did a deep review, thanks!!
pallets/loans-ref/src/lib.rs
Outdated
/// - overdue_days: 15, percentage 20% | ||
/// | ||
/// If the loan is not overdue, it will not return any rule. | ||
/// If the loan overdue by 4 days, it will not return any rule. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit
/// If the loan overdue by 4 days, it will not return any rule. | |
/// If the loan is overdue by 4 days, it will not return any rule. |
pallets/loans-ref/src/lib.rs
Outdated
@@ -679,16 +688,38 @@ pub mod pallet { | |||
}) | |||
} | |||
|
|||
fn find_write_off_state( | |||
/// From all overdue write off rules, it returns the one has highest percentage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit
/// From all overdue write off rules, it returns the one has highest percentage | |
/// From all overdue write off rules, it returns the one with the highest percentage |
.filter_map(|rule| { | ||
rule.triggers | ||
.iter() | ||
.map(|trigger| loan.check_write_off_trigger(&trigger.0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Maybe it would be better to destructure into the 0th tuple element directly to make it more explicit?
.map(|trigger| loan.check_write_off_trigger(&trigger.0)) | |
.map(|(trigger, _)| loan.check_write_off_trigger(&trigger)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has only one element, the map
part should be something like .map(|UniqueWriteOffTrigger(trigger)|)
, which maybe is more confusing (line breaks and adds a new indentation). It if was a tuple, I'd agree with you.
|
||
log::info!("Successful migration: v0 -> v1. Items: {count}"); | ||
|
||
T::DbWeight::get().reads_writes(count, count) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: It's actually count + 1
since we read and write the StorageVersion
:
T::DbWeight::get().reads_writes(count, count) | |
T::DbWeight::get().reads_writes(count.saturating_add(1), count.saturating_add(1)) |
You could also initialize count = 1
and log
log::info!("Successful migration: v0 -> v1. Items: {count.saturating_sub(1)}");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the same at first, but then I checked some substrate pallets, and there, Substrate returns Weight::zero()
after reading the StorageVersion
, which maybe means the version is something stored in the memory, not in the database 🤔.
I've tried to check if I'm right, but it's done procedurally...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyways adding 1 does not hurt!
old_policy | ||
.into_iter() | ||
.zip(new_police) | ||
.all(|(old_vector, new_vector)| { | ||
let mut policy = old_vector.iter().zip(new_vector.iter()); | ||
policy.all(|(old, new)| { | ||
*new == WriteOffRule::new( | ||
[WriteOffTrigger::PrincipalOverdueDays(old.overdue_days)], | ||
old.percentage, | ||
old.penalty, | ||
) | ||
}) | ||
}) | ||
.then_some(()) | ||
.ok_or("Error: policies differ") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very elegantly written assertion 😍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! ❤️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for baking in my comments, looks great!
TypeInfo, | ||
RuntimeDebug, | ||
MaxEncodedLen, | ||
EnumCount, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used to bound the triggers
vec (as the triggers need to be unique, thus the bound should be on the number of variants in this enum)
pub mod migrations { | ||
pub mod nuke; | ||
pub mod v1; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not keep them around forever or hide them behind a feature flag to not bloat the wasm. But rather minor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once the migration is not used, the unused code should be removed from the wasm. Until I know, Substrate passes wasm-opt
that removes unused symbols/code and things like those.
PrincipalOverdueDays(u32), | ||
|
||
/// Seconds since the oracle valuation was last updated | ||
OracleValuationOutdated(Moment), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do we think we need this for? in which scenario will this be a trigger?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an oracle, has not updated a price for a long time, the chain logic somehow differs from the real value of that price, and affects to the portfolio valuation, so we need to write off that loan in a percentage if it exceeds the maximum updated period for its price.
Description
Fixes #1305
Tasks
Changes
WriteOffState::overdue_days
toWriteOffState::triggers
of typeWriteOffTrigger
enum.WriteOffState::{penalty, percentage} removed and used instead of the
WriteOffStatus` that already contains that information.WriteOffState
toWriteOffRule
.percentage
andpenalty
instead ofoverdue_days