In Westend, there is a pool #15 (`5EYCAe5ijiYfAXEth5DDXhQX65taFsxqC43mgtEXxXgLVkXC`) that has been [slashed once ~6 months ago](https://westend.subscan.io/account/5EYCAe5ijiYfAXEth5DDXhQX65taFsxqC43mgtEXxXgLVkXC?tab=slash). At the time of writing this doc, the pool had `188,887,543,554,311` points, `169` members and it was open. The account [`5ExpZ6p6anF2RzXUgiu6qJSBuxv3bDBJ7sWnqprPeBu6khYU`](https://westend.subscan.io/account/5ExpZ6p6anF2RzXUgiu6qJSBuxv3bDBJ7sWnqprPeBu6khYU) joined pool #15 [~3 months ago](https://westend.subscan.io/extrinsic/0xd8fb78b513d2d75d88a38d37352ab20f99a49cbd2692b60875250fdf18e59d74) -- after the pool slash -- with 4WND and hasn't added more bound to the pool. The current inner pool of the account in the pool 15 is the following: ```json nominationPools.poolMembers: Option<PalletNominationPoolsPoolMember> { poolId: 15 points: 430,107,526,881 lastRecordedRewardCounter: 114,278,579,641,276,793 unbondingEras: {} } ``` Since there hasn't been any slashes since the account joined the pool, we'd expect that the conversion of the account's points in the pool to balance to be 4WND (initial balance at joining time). However, this is the result of the when calling the `points_to_balance` runtime API: ### Tests - [x] reproduce innacuracy with tests ```rust= #[test] fn points_to_balance_raw() { let u256 = <Runtime as Config>::BalanceToU256::convert; let balance = <Runtime as Config>::U256ToBalance::convert; let pool_balance: u128 = 175_665_415_505_556; let pool_points: u128 = 188_887_543_554_311; let member_points: u128 = 430_107_526_881; let final_balance = balance( u256(pool_balance) .saturating_mul(u256(member_points)) .div(pool_points), ); println!("1. {:?}", final_balance); // 1. 399999999999 assert_eq!(400_000_000_000, final_balance); // fails } ``` - [ ] try using arithmetics with rounding `div_ceil` works in using unstable feature `int_roundings` in `nightly` (can't be used in the runtime). Using fixed point arithmetics and perbill does not seem to help. ```rust= #![feature(int_roundings)] fn main() { let pool_balance: u128 = 175_665_415_505_556; let pool_points: u128 = 188_887_543_554_311; let member_points: u128 = 430_107_526_881; println!("1. {:?}", (pool_balance * member_points) / pool_points); // 1. 399999999999 println!("2. {:?}", (pool_balance * member_points).div_ceil(pool_points)); // 2. 400000000000 } ``` --- ### Notes ``` nominationPoolsApi.pointsToBalance(15, 0.430107526881) // 399.9999 mWND ``` ```rust= /// Calculate the equivalent point of `new_funds` in a pool with `current_balance` and /// `current_points`. fn balance_to_point( current_balance: BalanceOf<T>, current_points: BalanceOf<T>, new_funds: BalanceOf<T>, ) -> BalanceOf<T> { let u256 = T::BalanceToU256::convert; let balance = T::U256ToBalance::convert; match (current_balance.is_zero(), current_points.is_zero()) { (_, true) => new_funds.saturating_mul(POINTS_TO_BALANCE_INIT_RATIO.into()), (true, false) => { // The pool was totally slashed. // This is the equivalent of `(current_points / 1) * new_funds`. new_funds.saturating_mul(current_points) }, (false, false) => { // Equivalent to (current_points / current_balance) * new_funds balance( u256(current_points) .saturating_mul(u256(new_funds)) // We check for zero above .div(u256(current_balance)), ) }, } } /// Calculate the equivalent balance of `points` in a pool with `current_balance` and /// `current_points`. fn point_to_balance( current_balance: BalanceOf<T>, current_points: BalanceOf<T>, points: BalanceOf<T>, ) -> BalanceOf<T> { let u256 = T::BalanceToU256::convert; let balance = T::U256ToBalance::convert; if current_balance.is_zero() || current_points.is_zero() || points.is_zero() { // There is nothing to unbond return Zero::zero() } // Equivalent of (current_balance / current_points) * points balance(u256(current_balance).saturating_mul(u256(points))) // We check for zero above .div(current_points) } ```