owned this note
owned this note
Published
Linked with GitHub
---
title: 'Аудит проекта DCHF'
---
Аудит проекта DCHF
===
## Оглавление
[TOC]
## Ссылка на исследуемые контракты
Смарт контракты были предоставлены в zip-архиве: dchf-audit.zip. SHA256 для этого файла равно 422634256D6B50B41A1F97E56B7174F27E44DD9B0D3D207CB1BFB16D82D004B0.
## Ре-аудит исследуемых контрактов
В промежутке времени с 26 ноября 2022 по 03 декабря 2022 был проведен повторный аудит исследуемых контрактов.
Был предоставлен доступ к приватному репозиторию по адресу:
https://bitbucket.org/grizzlyfi/dchf-contracts/src/master/. Коммит для реаудита имеет номер:
901c1b05372fbc17bc3474152e9a3916a119d96a
## Классификация найденных уязвимостей
• CRITICAL: Ошибки, приводящие к краже активов, блокировке доступа к контрактам, на балансе которых есть активы. А также недополученная прибыль или любая другая потеря активов.
• MIDDLE: Ошибки, которые могут привести к сбою работы контракта. Дальнейшее восстановление возможно только путем ручного изменения состояния контракта или его замены.
• WARNINGS: Ошибки, которые могут нарушить предполагаемую логику контракта или подвергнуть его DoS-атакам и т.д.
• COMMENTS: Другие проблемы и рекомендации, сообщенные для команды.
---
## Найденные проблемы
### CRITICAL
#### 1. Возможно проведение Reentrancy-атак
##### Description
Проведение Reentrancy-атаки может привести к потере ETH, который был на балансе контракта. Об этом написано, например, здесь: [What is a Reentrancy Attack?](https://www.certik.com/resources/blog/3K7ZUAKpOr1GW75J2i0VHh-what-is-a-reentracy-attack).
В исследуемых контрактах есть несколько участков кода содержащих такую проблему.
- В контракте "StabilityPool" на строках 182 и 224 функции "provideToSP()" и "withdrawFromSP()" может вызвать кто угодно. Внутри этих функций вызывается "_sendAssetGainToDepositor()". При вызове функции "_sendAssetGainToDepositor()" на строке 703 происходит отправка эфира. Если вызывающая сторона будет смарт контрактом с fallback() функцией обратно вызывающей "provideToSP()" и "withdrawFromSP()", то возможно проведение Reentrancy-атаки и выкачка эфира с баланса контракта.
- Такая же уязвимость наблюдается для контракта "ETHTransferScript" на строке 6.
##### Recommendation
Необходимо подключить библиотеку "ReentrancyGuard" также, как это, например, сделано в контракте "ActivePool" на строке 104.
##### Fixing the issue
Данная пролема была исправлена в коммите 9d886e1f180b7ac51ab0be77c924458afa174798
#### 2. Неверное значение эфира
##### Description
В контракте "BorrowerOperationsScript" на строке 23 для вычисления "msg.value" вызывается функция "getValueOrArg()". Эта функция возвращает количество эфира для payable функции "openTrove()" в контракте "BorrowerOperations". Однако, если значение переменной "_asset" окажется адресом токена все равно в "msg.value" будет записано значение "_assetAmountSent". Значение "_assetAmountSent" также используется для определения количества токенов, если передаются токены. Таким образом при передаче токенов будет передано "_assetAmountSent" токенов и такое же количество эфира. Но фактически будут учтены только токены, а эфир просто будет передан на баланс контракта "BorrowerOperations". Аналогичная ситуация для функции "addColl()" на строке 39 и функции "adjustTrove()" на строке 89.
##### Recommendation
Необходимо исправить логику проверки в функции "getValueOrArg()". Например, сделать по аналогии с проверкой в контракте "BorrowerWrappersScript" на строке 178.
##### Fixing the issue
Этот контракт и функция были удалены
### MIDDLE
#### 1. Нарушение стандарта ERC-20
##### Description
В соответствии со стандартом [ERC-20](https://eips.ethereum.org/EIPS/eip-20) функции при работе с активами возвращают булево значение. Необходимо обрабатывать возвращаемое значение.
- В контракте "CommunityIssuance" на строке 225 не обрабатывается ответ при вызове функции "transfer()".
- В контракте "MONStaking" на строке 118, 161, 182, не обрабатывается ответ при вызове функции "transfer()", на строке 138 при вызове функции "transferFrom()".
- В контракте "TokenScript" на строке 19 не обрабатывается ответ при вызове функции "transfer()" и на строке 35 при вызове функции "transferFrom()".
##### Recommendation
Необходимо добавить обработку получаемого значения или использовать библиотеку [SafeERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.7/contracts/token/ERC20/utils/SafeERC20.sol).
##### Fixing the issue
Данная пролема была исправлена в коммите f6af9db8addb65a8dc181b6241970242d8cc21f5
#### 2. Нехватка газа для выполнения всех итераций цикла (DoS)
##### Description
Для каждой итерации цикла требуется газ. Может наступить момент, когда газа потребуется больше, чем выделено для записи одного блока. В этом случае все итерации цикла завершатся неудачно.
- В контракте "TroveManager" на строке 1134 переменной "_maxIterations" присваивается максимальное значение "type(uint256).max". Далее идет выполение цикла "while". При определенных условиях вызов функции "redeemCollateral()" будет завершаться ревертом из-за нехватки газа.
- В контракте "MONStaking" выполняется цикл "for" с количеством итераций цикла "ASSET_TYPE.length" на строках 153-169. Количество элементов массива "ASSET_TYPE" на строке 215 можно увеливать на неограниченное число. Может возникнуть ситуация, что перестанет работать функция "unstake()" и токены останутся заблокированными на балансе контракта.
##### Recommendation
Необходимо добавить ограничение на максимальное количество итераций цикла.
##### Fixing the issue
Данная пролема была принята к сведению.
#### 3. Возможна блокировка токенов на балансе контракта
##### Description
В контракте "MONStaking" при каждой процедуре "stake()" на строке 118 и "unstake()" на строке 161 на адрес "msg.sender" высылается часть токенов "dchfToken". Для корректной работы этих процедур необходимо, чтобы на балансе контракта было достаточное количество токенов "dchfToken". Эти токены переводит администрация проекта. Но в случае, если по каким-либо причинам потребуется забрать часть токенов с баланса контракта, то для этого нет разработанного функционала. Таким образом токены останутся заблокированными на балансе контракта.
##### Recommendation
Необходимо разработать функционал для экстренного перевода токенов "dchfToken" с баланса контракта "MONStaking".
##### Fixing the issue
Данная пролема была принята к сведению.
#### 4. Неверный прайс токена
##### Description
В контракте "PriceFeed" на строках 245, 249, 293, 306 делается преобразование переменной типа "int256" в переменную типа "uint256". Если значение переменной будет отрицательное, то после преобразования произойдет арифметическое переполнение. Необходимо перед преобразованием числа делать проверку его значения и брать значение по модулю.
Аналогично для контракта "PriceFeedV2" на строках 239, 243, 287, 300.
##### Recommendation
Необходимо исправить эти ошибки.
##### Fixing the issue
Данная пролема была принята к сведению.
### WARNINGS
#### 1. Неверное использование функционала
##### Description
В контракте "AdminContract" на строках 42-49 делаются бессмысленные действия, которые не дают никакого результата.
##### Recommendation
Необходимо сделать контракт "CheckContract" предком и на строках 42-49 делать вызов функции "checkContract()". Код можно переделать также, как, например, сделано в контракте "CommunityIssuance".
##### Fixing the issue
Данная пролема была принята к сведению.
#### 2. Возможна запись неверного значения в event
##### Description
В контракте "StabilityPool" на строкe 778 вызывается функция "sendMON" с параметром "depositorMONGain". Далее в событии "MONPaidToDepositor" записывается значение параметра "depositorMONGain". Но фактическое значение этого параметра может оказаться меньше и даже равно 0. Это видно в контракте "CommunityIssuance" на строке 219.
##### Recommendation
Рекомендуется сделать, чтобы функция "sendMON()" в контракте "CommunityIssuance" возвращала фактическое значение. Далее в контракте "StabilityPool" это значение будет обрабатываться.
##### Fixing the issue
Данная пролема была принята к сведению.
#### 3. Пустой контракт
##### Description
Контракт "GasPool" пустой. Однако в контракте "BorrowerOperations" на строках 133, 260, 580 идет работа с адресом такого контракта. И в контракте "TroveManager" на строках 72, 879, 1003, 1093 идет работа с адресом такого контракта.
##### Recommendation
Рекомендуется добавить логику или удалить этот контракт.
##### Fixing the issue
Данная пролема была принята к сведению.
#### 4. Нет проверок
##### Description
Перед тем, как использовать ecrecover() необходимо сделать дополнительные проверки параметров digest, v, r, s. Все это делаеттся в библиотеке ECDSA от [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.7/contracts/utils/cryptography/ECDSA.sol).
В контракте "ERC20Permit" на строке 99 используется "ecrecover()" без проверок.
##### Recommendation
Рекомендуется подключить библиотеку для проведения проверок.
##### Fixing the issue
Данная пролема была принята к сведению.
### COMMENTS
#### 1. Ненужный импорт
##### Description
В контракте "BorrowerWrappersScript" на строке 14 есть неиспользуемый импорт.
##### Recommendation
Рекомендуется удалить ненужные импорты.
##### Fixing the issue
Данная пролема была принята к сведению.
#### 2. Неоптимизированные данные
##### Description
При сохранении структурированных данных можно делать оптимизацию. Например, если в структуре 4 поля с типом данных uint256, то при сохранении в storage будет занято 4 слота памяти. Если значения этих данных не очень большие, их можно переделать в тип uint64. В этом случае будет занят только 1 слот памяти. Это позволяет экономить газ.
- В контракте "LockedMON" на строках 16-22 описана структура "Rule" с данными типа uint256. Можно у части данных переопределить тип данных.
- В контракте "SortedTroves" на строках 69-75 описана структура "Data".
- В контракте "StabilityPool" на строках 53-59 описана структура "Snapshots".
##### Recommendation
Рекомендуется переопределить часть типов данных у структур.
##### Fixing the issue
Данная пролема была принята к сведению.
==================================================
## Новые найденные проблемы
#### 1. Возможна кража токенов у пользователей
##### Description
В контракте "CommunityIssuance" на строке 146 осуществляется вызов функции "safeTransferFrom()" для адреса кошелька, переданного в параметре "_spender". Функцию "_addFundToStabilityPoolFrom" можно вызвать на строке 128. Это может сделать владелец контракта или администратор контракта. Таким образом у любого пользователя, давшего approve() на адрес этого контракта, владелец контракта или администратор контракта могут без ведома пользователя забрать токены "monToken" с его кошелька. Затем владелец вызвав функцию "removeFundFromStabilityPool()", может перевести эти токены на свой кошелек.
##### Recommendation
Для пополнения баланса контракта использовать только служебные кошельки. Т.е. в качестве "_spender" использовать только служебные кошельки.
#### 2. Опасное тождество
##### Description
В контракте "CommunityIssuance" на строках 117 и 170 осуществляется вызов функции "disableStabilityPool()". Но это возможно только при выполненении строгого равенства значений переменных "totalMONIssued[_pool]" и "MONSupplyCaps[_pool]".
Если значение будет отличаться на ничтожно малую величину, такую, как 1/10**-18 степени равенство уже не выполнится. Например, такое отличие может произойти при делении целых чисел. Это приведет к тому, что вызов функции "disableStabilityPool()" не произойдет, хотя значения переменных будут почти тождественны
##### Recommendation
В таких случаях нельзя использовать строгое равенство значений переменных.
#### 3. Нет проверки адреса
##### Description
В контракте "DfrancParameters" на строке 114 осуществляется установка маппинг-переменной "hasCollateralConfigured" в истинное значение. Но проверки ключа маппинга, переменной "_asset" на то, что она не 0 не делается. Установка нулевого адреса в "true" может привести к непредсказуемым последствиям для работы контракта.
##### Recommendation
Необходимо добавить проверку для переменной "_asset".
#### 4. Нет необходимого функционала
##### Description
В контракте "DfrancParameters" на строке 114 осуществляется установка маппинг-переменной "hasCollateralConfigured" в истинное значение. Но коллатералы могут резко начать терять свою стоимость, например, как с токенами LUNA или FTT. В этом случае можно было бы заблокировать работы с таким токеном. Но сейчас такого функционала нет.
##### Recommendation
Необходимо добавить функционал для блокировки токенов.
#### 5. Возможна блокировка работы
##### Description
В контракте "SortedTroves" на строке 245 производится уменьшение значения переменной "data[_asset].size".
Но нет проверки, что это значение больше 0. Отсутствие такой проверки может привести к блокировке работы функции "remove()" и, как следствие, блокировке работы функционала для ликвидации "Trove" в контракте "TroveManager".
##### Recommendation
Исправить ошибку.
#### 6. Лишние переменные
##### Description
В контракте "DCHFToken" на строке 44 определена переменная "borrowerOps". В функциях "addBorrowerOps()" и "removeBorrowerOps" значение переменной меняется. Но в этом контракте и в других, связанных с эттим, больше нигде не используется эта переменная. Вместо этой переменной для проверок используется другая переменная "validBorrowerOps". Аналогично для переменной "troveManagers" на строке 43.
##### Recommendation
Можно уменьшить стоимость транзакций, если не использовать лишние переменные. Для фронта массив адресов для "borrowerOps" и "troveManagers" можно получить из событий.
## Appendix
:::info
Здесь представлена неполная схема взаимодействия всех смарт контрактов
:::
![](https://i.imgur.com/Np6Lty2.jpg)