Начиная с Solidity 0.8 арифметика откатывает транзакцию при переполнении вверх и вниз. Отключить это можно только явно, блоком unchecked { ... }. В Rust наоборот. Отладочная сборка паникует, а с --release по умолчанию значение оборачивается в дополнительном коде. Проверки в релизе будут, только если в профиле Cargo стоит overflow-checks = true. В C знаковое переполнение - вообще неопределённое поведение, и проверку после операции компилятор может просто удалить.
Для кода, который переводит балансы, правильным я считаю умолчание Solidity. Обёрнутый результат сохранится как неверный баланс. Откат возвращает всю транзакцию. Отказ лучше, чем неверное число, которое останется навсегда.
Но дыра у обоих одна. В Solidity явное преобразование типа обрезает значение даже в режиме проверок, так что uint128(x) не откатится. as в Rust тоже молча обрезает. Проверенное сложение, за которым идёт сужающее приведение, на деле ничем не проверено.
Отсюда правило. В арифметике сумм никаких unchecked. В релизном профиле Rust проверки включены. Приведения сумм только через try_from. Кто-нибудь из вас держал overflow-checks включённым в релизных сборках? Интересно, заметна ли цена.