-
Notifications
You must be signed in to change notification settings - Fork 47
[COMP-1165] fixed precision decimal implementation (wip) #11
Conversation
COMP-1165 Compound Interest [Compound Chain]
We need to calculate continuously compounding interest for CASH on the Compound Chain. This calculation is easy mathematically
Feel free to use libraries and take wisdom from other projects for this work. Additionally, pay attention to the decisions made by COMP-1143 which is continuous interest on the Ethereum chain. |
Summarizing a call that we just had
not pursuing for now
those things may change over time as the implementation details become clearer |
Great start, though I sort of think we should try this math out in real situations (implementing some core fns using it) to make sure its ergonomic. Alternatively we merge early and iterate on it later |
/// 12345.6789. The decimals are stored separately. | ||
#[derive(Clone, PartialEq, Debug)] | ||
pub struct Amount { | ||
mantissa: MantissaType, |
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.
don't we want a decimals
field here too? like 1 eth = Amount{ mantissa: 1e18, decimals: 18 } ?
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.
Shouldn't Amount
be just type Amount = BigUint
actually?
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 have type Amount = u32
as a stub for the moment, I figure we probably want this thing to map pretty closely to the Decimal type in Elixir tbh, seemed to me like the discussion was pretty much there, but maybe I misunderstand
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 we landed on Amounts are infinite precision unsigned integers? I don't think we really want to store decimals on chain for every single amount and its kind of nice to deal with decimals explicitly when we need them for display (or importing prices)
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.
At the end of the day, we probably should start writing functions before musing on this too much further, since it should clear up our path
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.
Not really sure how this is a blocker if we just use type Amount = BigUint
or even uint128
for writing functions now
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.
don't we want a decimals field here too? like 1 eth = Amount{ mantissa: 1e18, decimals: 18 } ?
I had this in a previous commit and took it out because there really isn't any need to carry around the decimals everywhere. It does provide some nice runtime guarantees but they aren't worth the storage cost as per standup discussion a while back
Shouldn't Amount be just type Amount = BigUint actually?
The biggest difference between this type wrapper style and the type alias style is that the type wrapper style allows us to implement traits defined outside the current crate. For more discussion see https://doc.rust-lang.org/stable/error-index.html#E0117
This is necessary to, for example, implement serialization and deserialization for parity's binary encoding (name escapes me now) traits.
11779a6
to
ad74ba7
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
rhs.decimals | ||
); | ||
} | ||
|
||
// note - this cannot fail with BigUint but that will change based on underlying storage |
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 will change based on storage? 🤔
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 add function may fail due to overflow if the storage type for the mantissa is overflowable
in order for us to be "able to switch out what the mantissa is stored as easily" we need to be able to fail during the add function here
As discussed bigint + u8 for decimal representation also avoiding the use of generics for the most part. For now the only imaginable way to have errors in my mind is decimal mismatch. Lots to fill in here and some open questions Do we want this in a separate crate? I imagine it is possible the eth-rpc crate may ultimately depend on this functionality. I just figured out that u128 is natively available in rust on all targets as LLVM has support for it rust simply exposes that. See this pr from late 2016. This has been stablized in 1.26. The representable range here even with a token that is using 18 decimals of precision is 19 9s. If you have 10^19 of something I think it is alright to say that you need to do two transactions. I believe that u128 represents the best combination of speed flexibility and consistency. It is very unlikely to me that any token balance will natively overflow this representation.
3da74db
to
2be9974
Compare
This comment has been minimized.
This comment has been minimized.
I guess we should just start writing those and then decide
On Thu, Dec 3, 2020 at 8:01 PM Jared Flatow ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In pallets/cash/src/amount.rs
<#11 (comment)>
:
> +use num_bigint::BigUint;
+
+/// The type of the decimal field.
+pub type DecimalType = u8;
+
+/// The type of the mantissa field
+pub type MantissaType = BigUint;
+
+/// Represents a decimal number in base 10 with fixed precision. The number of decimals depends
+/// on the amount being represented and is not stored alongside the amount.
+///
+/// For example, if the mantissa is 123456789 and decimals is 4 the number that is represented is
+/// 12345.6789. The decimals are stored separately.
+#[derive(Clone, PartialEq, Debug)]
+pub struct Amount {
+ mantissa: MantissaType,
Not really sure how this is a blocker if we just use type Amount = BigUint
or even uint128 for writing functions now
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#11 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACCFMAHFOPNCWMAZYPHVP3STBNJXANCNFSM4T5BAPGA>
.
--
geoffrey hayes
geoff@compound.finance
415.735.8262
|
I'm going to close this since it has been integrated into #26 |
As discussed bigint + u8 for decimal representation also avoiding the
use of generics for the most part. For now the only imaginable way
to have errors in my mind is decimal mismatch.
Lots to fill in here and some open questions
Do we want this in a separate crate? I imagine it is possible the
eth-rpc crate may ultimately depend on this functionality.
I just figured out that u128 is natively available in rust on all
targets as LLVM has support for it rust simply exposes that. See
this pr from late
2016. This has been stablized in 1.26.
The representable range here even with a token that is using 18
decimals of precision is 19 9s. If you have 10^19 of something
I think it is alright to say that you need to do two transactions.
I believe that u128 represents the best combination of speed
flexibility and consistency. It is very unlikely to me that
any token balance will natively overflow this representation.