`Wei` and `Gwei` lose their type through arithmetic — `+=` / `-=` rejected by mypy
- Dominant language
- Python
- Stars
- 5.5k
- Forks
- 1.7k
- Avg merge
- 3d 10h
- Merged PRs (30d)
- 2
Description
### What feature should we add?
## Problem
`Wei` and `Gwei` are currently defined as `NewType` aliases over `int`:
```python
Wei = NewType("Wei", int)
Gwei = NewType("Gwei", int)
```
`NewType` creates a distinct type for the type checker, but all arithmetic operations return the *base* type (`int`), not the `NewType`. This means any arithmetic on `Wei` or `Gwei` values immediately "escapes" the type and mypy flags the result as `int`, not `Wei`/`Gwei`.
In practice, augmented assignment (`+=`, `-=`) is rejected outright:
```python
x = Wei(5)
x += Wei(1) # error: Incompatible types in assignment (expression has type "int", variable has type "Wei")
```
Even plain addition requires an explicit cast every time:
```python
total: Wei = Wei(0)
for tx in block["transactions"]:
total = Wei(total + tx["value"]) # cast needed on every accumulation
```
This affects real-world usage patterns — accumulating gas costs, summing balances, computing fee estimates — and makes well-typed Ethereum code significantly more verbose.
## Proposed fix
Replace the `NewType` aliases with `int` subclasses that override arithmetic operators to preserve the return type:
```python
class Wei(int):
def __add__(self, other: "Wei") -> "Wei": ...
def __sub__(self, other: "Wei") -> "Wei": ...
def __mul__(self, other: int) -> "Wei": ...
# etc.
class Gwei(int):
...
```
Because the classes subclass `int`, they are fully backward-compatible: anywhere `int` is accepted, `Wei`/`Gwei` still work. The `isinstance` check also becomes meaningful, which opens the door for formatters to return typed values instead of plain `int`.
As a natural follow-up, the result formatters in `method_formatters.py` can wrap monetary fields (`gasPrice`, `value`, `maxFeePerGas`, `effectiveGasPrice`, `baseFeePerGas`, `eth_getBalance`, etc.) with `Wei(...)` and withdrawal `amount` with `Gwei(...)`, so that values coming out of RPC calls already carry the correct type at the boundary.
## Backward compatibility
- `Wei` and `Gwei` are `int` subclasses, so all existing code that treats them as `int` continues to work without modification.
- `NewType` values cannot be used with `isinstance`; the new classes can — this is strictly additive.
- The formatter changes are internal and do not affect the public API.
## Prior art
The `eth_typing` library uses a similar pattern for `BlockNumber` (also a `NewType`), and the same limitation applies there. This proposal is scoped to `Wei` and `Gwei` since those are most commonly involved in arithmetic.
Contributor guide
Assessment
This issue has not been assessed yet.