Smart Contract Audit Ethereum · UUPS Proxy Authorized CertiK Partner

Аудит смарт-контракта: устранение критических уязвимостей перед публичным запуском

Security token на базе Ethereum с UUPS proxy-паттерном и TimelockController. Полный рефакторинг архитектуры, закрытие всех Critical/High findings до публикации отчёта.

22 863 b
итоговый размер контракта после рефакторинга
+1 713 b
запас до лимита EVM (24 576 байт)
5
findings закрыто: ZHA-07, 16, 38, 40, 42
0 / 0
Critical / High при публикации отчёта
Контекст
Проект выпускал security token на базе Ethereum с UUPS proxy-паттерном и TimelockController. До публичного листинга требовался внешний аудит безопасности.
Проблема
Контракт превышал лимит байткода EVM — деплой невозможен. В логике контроля доступа и восстановления подписей выявлены уязвимости:
Critical ×1 High ×2 Medium ×2
Рефакторинг архитектуры
Вынос логики в LibRecovery.sol
Сократил контракт на ~1 800 байт, убрав встроенную логику восстановления подписи
bytecode limit
> 24 576 bytes
До — встроенная логика
// ZSTToken.sol — внутри основного контракта contract ZSTToken is ERC3643, UUPSUpgradeable { // ❌ вся логика прямо в контракте function _recoverSigner( bytes32 hash, bytes memory sig ) internal pure returns (address) { bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(sig, 0x20)) s := mload(add(sig, 0x40)) v := byte(0, mload(add(sig, 0x60))) } return ecrecover(hash, v, r, s); } // +~600 байт к bytecode }
После — LibRecovery.sol
// LibRecovery.sol — отдельная библиотека library LibRecovery { function recover( bytes32 hash, bytes memory sig ) internal pure returns (address) { bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(sig, 0x20)) s := mload(add(sig, 0x40)) v := byte(0, mload(add(sig, 0x60))) } return ecrecover(hash, v, r, s); } } // ZSTToken.sol — использует библиотеку import "./LibRecovery.sol"; contract ZSTToken is ERC3643, UUPSUpgradeable { using LibRecovery for bytes32; } // ✓ 22 863 байт
До
ZSTToken.sol
❌ > 24 576 байт — деплой невозможен
После
ZSTToken.sol
✓ 22 863 байт · запас 1 713 b
Вынесено
LibRecovery.sol
library · deploy-time linking
library-контракт линкуется при деплое и не учитывается в лимите основного контракта
Finding ZHA-07 · Critical
Обход проверки владельца токена
Отсутствие валидации позволяло вызвать transfer от имени любого адреса
severity
Critical
Уязвимый код
// ❌ ZHA-07: нет проверки msg.sender function transferByAgent( address from, address to, uint256 amount ) external { // ❌ любой может вызвать _transfer(from, to, amount); }
После исправления
// ✓ ZHA-07: роль + проверка identity function transferByAgent( address from, address to, uint256 amount ) external { require( hasRole(AGENT_ROLE, msg.sender), "Not an agent" ); require(identityRegistry .isVerified(to), "KYC fail"); _transfer(from, to, amount); }
Все закрытые findings: ZHA-07 ZHA-16 ZHA-38 ZHA-40 ZHA-42 ✓ Audit passed
Audit passed
Результат
Контракт успешно прошёл внешний аудит авторизованной компании. Все Critical и High findings закрыты до публикации отчёта. Проект вышел в публичный листинг без инцидентов безопасности.
Solidity 0.8
UUPS Proxy · OpenZeppelin
TimelockController
Gnosis Safe · Multisig
LibRecovery.sol
ERC-3643 · security token
dimtiks.ru
Blockchain Development & Smart Contract Auditing · TOKEN LAB

Есть задача? Давайте разберём её

Расскажите что происходит — мы скажем можем ли помочь и как это выглядит по деньгам и срокам. Без обязательств.