vyper
vyper copied to clipboard
VIP: allow generic decimal types
Simple Summary
Allow generic decimal types with parametrizable precision and bits settings.
Motivation
Fixed point math is generally useful for smart contracts as one of the most common use cases for smart contracts is to do accurate accounting. This is manifest in the fact that numerous ERCs and applications depend on fixed point math (ex. ERC20, various AMMs, ERC4626). This VIP proposes general decimal types which are
- easy to declare in the usual case ("I want decimals 18 x 256")
- tunable if the user wants to use a specific number of bits ("I want decimals 10 x 112")
- compatible with ERC20 and other token standards (exposed ABI types are standard integer types)
Specification
Introduce new PEP484 style Decimals and UDecimals types which each accept two parameters (the latter of which is optional).
Examples:
UDecimals[18](i.e. unsigned decimals type with 18 decimals of precision and occupying 256 bits of space)UDecimals[18, 128](i.e. unsigned decimals with 18 decimals of precision and occupying 128 bits of space)Decimals[10, 168](equivalent to current decimals)
The existing decimals type will be considered a deprecated alias of Decimals[10, 168].
In general, the ABI type of Decimals[N, B] will be int_<B>. The ABI type of UDecimals[N, B] will be uint_<B>.
- TBD: safemath rules. Consider the rules with and without the availability of a MULDIV (EIP-5000) instruction.
Backwards Compatibility
- Deprecates
decimaltype - Changes the ABI type of
decimal.
Dependencies
Requires some refactoring of the internal type system (probably will be done as work for #2431).
References
https://github.com/vyperlang/vyper/issues/3039 Could be useful for use-site tuning of ERC20 or other token libraries.
Copyright
Copyright and related rights waived via CC0
I don't think that you should have to specify the amount of bits the type takes up. We can calculate that.
It would be really nice if we have the ability to create custom types (or type aliases) such that we could allow typing to be used by end users more directly, e.g. ray := decimal(places=27) for Maker codebase.
Note: 38 decimal places is the most possible that can fit in 128 bits (corresponding to fixed255x38)
List of decimals:
fixed132x1fixed135x2fixed138x3fixed142x4fixed145x5fixed148x6fixed152x7fixed155x8fixed158x9fixed162x10fixed165x11fixed168x12fixed172x13fixed175x14fixed178x15fixed182x16fixed185x17fixed188x18fixed192x19fixed195x20fixed198x21fixed202x22fixed205x23fixed208x24fixed212x25fixed215x26fixed218x27fixed222x28fixed225x29fixed228x30fixed231x31(NOTE: doesn't fit pattern)fixed235x32fixed238x33fixed241x34(NOTE: doesn't fit pattern)fixed245x35fixed248x36fixed251x37(NOTE: doesn't fit pattern)fixed255x38
Meeting notes: Pamp it
Was thinking about this some more today. It could be something like the following:
- Internally, Vyper users only see the
Decimaltype, which is a fixed point type that is allowed to be any width decimal (usingfixed<M>x<N>notation) as long as the calculations align at "endpoints" (internal type conversions or ABI boundaries) - For interface definitions,
Decimaltakes an argument (e.g.Decimal(fixed168x10)) which defines the "required" shape of theDecimaltype at that interface and pins that boundary to that particular ABI type - When producing ABIs, Vyper statically determines the ABI type of
Decimaland usesfixed<M>x<N>notation to denote that interface type. This can be overriden using the same trick for interface definitions e.g.def myCall(val: Decimal(fixed168x10))to pin to a particular type (useful for ERCs) - All else is "undefined behavior", allowing the compiler to optimize towards the highest precision and lowest loss calculations based on all the mathematical interactions that are possible within an entire contract, given the constraints at the boundary points. This optimization should be statically determinable, so that it will always produce the same outcome given the same program (although different programs and constraint system can produce different results)
This design would be strictly better than doing uint256 math all over, with implicit assumptions about precision loss, and occasionally watching contract call failures from using SafeMath without due consideration to these scenarios. The compiler can also produce suggestions for the min/max size particular values can be at conversion boundary points in order to ensure correct operation of the algorithm.
meeting notes: add an example, bring up for discussion again