---
# System prepended metadata

title: Nomination pools - inspect potential inaccuracy of points_to_balance runtime API
tags: [' polkadot', ' frame', evergreen-note, ' staking']

---


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)
	}


```