# Sommelier Finance Audit Notes
**Auditor:** Jake Bunce
**Client:** Sommelier Finance https://sommelier.finance/
https://github.com/VolumeFi/cellars/tree/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts
**Commit:** `b887510055d96f2dc32a56d0143fa8846d3a07c8`
## Whitepaper & specification about the project
Whitepaper: https://docs.zora.co/zoraos/dev/smart-contracts/whitepaper
Project is a trading platform:
Sommelier is a bet that Ethereum will be a dominant player in the global economy. Sommelier consists of the Cosmos Stargate SDK, its Tendermint-based consensus layer and a decentralized, bi-directional Ethereum bridge, managed by a global network of validators.
Liquidity Providers (LPs) will be able to use the Sommelier to author and execute complex, and automated financial transactions, such as portfolio rebalancing, limit orders, batched orders, as well as a host of other features that traders have come to expect from CeFi, but that are not currently available in DeFi.
## Review of the protocol/implementation
**[1] Unlocked Pragma**
**Files Affected:** `contracts/CellarPoolShare.sol`, `contracts/CellarPoolShareLimitETHUSDT.sol`, `contracts/CellarPoolShareLimitUSDCETH.sol`, `contracts/interfaces.sol`
**Severity: Low**
Default AL text.
**Recommendations:**
Default AL text.
**[2] Own implementation of reentrancy guard**
**Files Affected:** `contracts/CellarPoolShare.sol`
**Severity: Low**
Reentrancy protection has been defined via a custom implementation.
**Recommendations:**
Consider using a library that is well audited and enjoys wide deployment instead of rolling your own. Further reading: https://docs.openzeppelin.com/contracts/4.x/api/security#ReentrancyGuard
**[3] Greedy Contract**
**Files Affected:** `contracts/CellarPoolShare.sol`
**Severity: Medium**
The contract will accept Ether from users but there is no way for them to retrieve this from the contract.
**Recommendations:**
Prevent Ether from being deposited into the contract by adding a `revert()` into the fallback function.
**[4] Management Function Argument Validation**
**Files Afected:** `contracts/CellarPoolShare.sol`, `contracts/CellarPoolShareLimitETHUSDT.sol`,
**Severity: Medium**
[`setValidator()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShare.sol#L577),
[`transferOwnership()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShare.sol#L582), [`setManagementFee()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShare.sol#L587), [`setPerformanceFee()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShare.sol#L592),
do not check for the validity of the address argument is sound.
**Recommendations:**
Verify that the `newOwner` argument is not the zero address.
**[5] Inconsistent implementations for liquidity addition**
**Files Affected:** `contracts/CellarPoolShare.sol`, `contracts/CellarPoolShareLimitETHUSDT.sol`, `contracts/CellarPoolShareLimitUSDCETH.sol`
**Severity: High**
`addLiquidityForUniV3()` is inconsistency between `CellarPoolShare.sol` and `CellarPoolShareLimitETHUSDT.sol` & `CellarPoolShareLimitUSDCETH` with the total supply and pricing.
**Recommendations:**
Move to a single function to remove code duplication and clarify why there are limits for some assets but not others.
**[6] Balances can be over/underflowed**
**Files Affected:** `contracts/CellarPoolShare.sol`, `contracts/CellarPoolShareLimitETHUSDT.sol`, `contracts/CellarPoolShareLimitUSDCETH.sol`.
**Severity: High**
Integer values can be over/underflowed. This means values could be manipulated to incorrect values by an attacker.
**Recommendations:**
Use the OpenZeppelin library [`SafeMath`](https://docs.openzeppelin.com/contracts/2.x/api/math) with < `0.8.0` of Solidity, or move to >= `0.8.0` where such protection is built into the compiler.
**[7] Incomplete Function**
**Files Affected:** `contracts/CellarPoolShare.sol`
**Severity: Medium**
[`_beforeTokenTransfer()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShare.sol#L300) appears to be an incomplete function.
**Recommendations:**
Implement the function or clarify the intention behind this function
**[8] Unclear Operation/Lack of Documentation**
**Files Affected:** `contracts/CellarPoolShare.sol`, `contracts/CellarPoolShareLimitETHUSDT.sol`, `contracts/CellarPoolShareLimitUSDCETH.sol`.
**Severity: Undefined**
[`invest()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShare.sol#L279) and weight operations [`_getWeightInfo()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShare.sol#L708) & [`_modifyWeightInfo()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShare.sol#L840) are complicated and there's no supporting documentation. Please provide documentation to describe the intended outcome of these functions. Additionally there is duplication of these functions across the Solidity files and there should only be one.
**[9] Clarify Flash loan Protection**
**Files Affected:** `contracts/interfaces.sol`
**Severity: Undefined**
Contract [`BlockLock`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/interfaces.sol#L1184) is used for protection against flash loans. Please provide documentation on the intended behaviour of this protection.
**[10] Multiple instances of reentrancy**
**Files Affected:** `contracts/CellarPoolShare.sol`, `contracts/CellarPoolShareLimitETHUSDT.sol`, `contracts/CellarPoolShareLimitUSDCETH.sol`
**Severity: High**
[`invest()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShare.sol#L279), [`invest()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShareLimitETHUSDT.sol#L277), [`invest()`](https://github.com/VolumeFi/cellars/blob/b887510055d96f2dc32a56d0143fa8846d3a07c8/contracts/CellarPoolShareLimitUSDCETH.sol#L277) are all susceptible to reentrancy.
**Recommendations:**
Implement reentrancy protection for this function. Suggested to use an open source library that is well audited and enjoys a deployment from a number of projects, such as https://docs.openzeppelin.com/contracts/4.x/api/security#ReentrancyGuard.
## Tests
```
================================= test session starts ==================================
platform linux -- Python 3.6.9, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /app
plugins: eth-brownie-1.16.4, web3-5.23.1, xdist-1.34.0, forked-1.3.0, hypothesis-6.21.6
collected 16 items
Attached to local RPC client listening at '127.0.0.1:8545'...
tests/test_00_add_liquidity.py .. [ 12%]
tests/test_01_transfer.py ... [ 31%]
tests/test_02_remove_liquidity.py ... [ 50%]
tests/test_03_reinvest.py ... [ 68%]
tests/test_04_rebalance.py ... [ 87%]
tests/test_05_weight.py . [ 93%]
tests/test_06_add_liquidity_limit.py . [100%]
=================================== warnings summary ===================================
../home/ethsec/.local/pipx/venvs/eth-brownie/lib/python3.6/site-packages/brownie/network/main.py:46
/home/ethsec/.local/pipx/venvs/eth-brownie/lib/python3.6/site-packages/brownie/network/main.py:46: BrownieEnvironmentWarning: Development network has a block height of 13416770
BrownieEnvironmentWarning,
-- Docs: https://docs.pytest.org/en/stable/warnings.html
====================== 16 passed, 1 warning in 359.90s (0:05:59) =======================
```
## Best Practices
**[1] Lint Errors**
```
CellarPoolShare.sol
19:5 warning Explicitly mark visibility of state state-visibility
19:5 warning 'NONFUNGIBLEPOSITIONMANAGER' should start with _ private-vars-leading-underscore
22:5 warning Explicitly mark visibility of state state-visibility
22:5 warning 'UNISWAPV3FACTORY' should start with _ private-vars-leading-underscore
25:5 warning Explicitly mark visibility of state state-visibility
25:5 warning 'SWAPROUTER' should start with _ private-vars-leading-underscore
28:5 warning Explicitly mark visibility of state state-visibility
28:5 warning 'WETH' should start with _ private-vars-leading-underscore
30:5 warning Explicitly mark visibility of state state-visibility
30:5 warning 'FEEDOMINATOR' should start with _ private-vars-leading-underscore
32:5 warning Explicitly mark visibility of state state-visibility
32:5 warning 'YEAR' should start with _ private-vars-leading-underscore
50:5 warning Explicitly mark visibility of state state-visibility
50:5 warning 'lastManageTimestamp' should start with _ private-vars-leading-underscore
93:5 warning Function order is incorrect, modifier definition can not go after constructor (line 60) ordering
135:5 warning Function body contains 105 lines but allowed no more than 50 lines function-max-lines
135:5 warning Function has cyclomatic complexity 14 but allowed no more than 7 code-complexity
142:9 warning Provide an error message for require reason-string
142:17 warning Avoid to make time-based decisions in your business logic not-rely-on-time
246:9 warning Provide an error message for require reason-string
246:17 warning Avoid to make time-based decisions in your business logic not-rely-on-time
279:5 warning Function body contains 110 lines but allowed no more than 50 lines function-max-lines
279:5 warning 'invest' should start with _ private-vars-leading-underscore
338:15 warning Code contains empty blocks no-empty-blocks
338:24 warning Code contains empty blocks no-empty-blocks
370:15 warning Code contains empty blocks no-empty-blocks
370:24 warning Code contains empty blocks no-empty-blocks
399:5 warning 'getManagementFee' should start with _ private-vars-leading-underscore
415:5 warning Function body contains 77 lines but allowed no more than 50 lines function-max-lines
415:5 warning Function has cyclomatic complexity 8 but allowed no more than 7 code-complexity
422:28 warning Avoid to make time-based decisions in your business logic not-rely-on-time
474:31 warning Avoid to make time-based decisions in your business logic not-rely-on-time
495:5 warning Function body contains 79 lines but allowed no more than 50 lines function-max-lines
495:5 warning Function has cyclomatic complexity 10 but allowed no more than 7 code-complexity
515:10 warning Variable "outAmount0" is unused no-unused-vars
515:30 warning Variable "outAmount1" is unused no-unused-vars
515:50 warning Variable "liquiditySum" is unused no-unused-vars
517:31 warning Avoid to make time-based decisions in your business logic not-rely-on-time
708:5 warning Function body contains 114 lines but allowed no more than 50 lines function-max-lines
840:5 warning Function body contains 66 lines but allowed no more than 50 lines function-max-lines
917:5 warning Function body contains 133 lines but allowed no more than 50 lines function-max-lines
1029:29 warning Code contains empty blocks no-empty-blocks
1042:29 warning Code contains empty blocks no-empty-blocks
1063:5 warning Function body contains 71 lines but allowed no more than 50 lines function-max-lines
1073:28 warning Avoid to make time-based decisions in your business logic not-rely-on-time
1141:24 warning Code contains empty blocks no-empty-blocks
1143:5 warning All public or external methods in a contract must override a definition from an interface comprehensive-interface
1143:32 warning Code contains empty blocks no-empty-blocks
CellarPoolShareLimitETHUSDT.sol
12:5 warning Explicitly mark visibility of state state-visibility
12:5 warning 'NONFUNGIBLEPOSITIONMANAGER' should start with _ private-vars-leading-underscore
15:5 warning Explicitly mark visibility of state state-visibility
15:5 warning 'UNISWAPV3FACTORY' should start with _ private-vars-leading-underscore
18:5 warning Explicitly mark visibility of state state-visibility
18:5 warning 'SWAPROUTER' should start with _ private-vars-leading-underscore
21:5 warning Explicitly mark visibility of state state-visibility
21:5 warning 'WETH' should start with _ private-vars-leading-underscore
23:5 warning Explicitly mark visibility of state state-visibility
23:5 warning 'FEEDOMINATOR' should start with _ private-vars-leading-underscore
25:5 warning Explicitly mark visibility of state state-visibility
25:5 warning 'YEAR' should start with _ private-vars-leading-underscore
42:5 warning Explicitly mark visibility of state state-visibility
42:5 warning 'lastManageTimestamp' should start with _ private-vars-leading-underscore
45:5 warning Constant name must be in capitalized SNAKE_CASE const-name-snakecase
80:5 warning Function order is incorrect, modifier definition can not go after constructor (line 47) ordering
122:5 warning 'totalPrice' should start with _ private-vars-leading-underscore
128:5 warning Function body contains 116 lines but allowed no more than 50 lines function-max-lines
128:5 warning Function has cyclomatic complexity 15 but allowed no more than 7 code-complexity
135:9 warning Provide an error message for require reason-string
135:17 warning Avoid to make time-based decisions in your business logic not-rely-on-time
250:9 warning Provide an error message for require reason-string
250:17 warning Avoid to make time-based decisions in your business logic not-rely-on-time
277:5 warning Function body contains 102 lines but allowed no more than 50 lines function-max-lines
277:5 warning 'invest' should start with _ private-vars-leading-underscore
331:15 warning Code contains empty blocks no-empty-blocks
331:24 warning Code contains empty blocks no-empty-blocks
360:15 warning Code contains empty blocks no-empty-blocks
360:24 warning Code contains empty blocks no-empty-blocks
382:5 warning 'getManagementFee' should start with _ private-vars-leading-underscore
398:5 warning Function body contains 77 lines but allowed no more than 50 lines function-max-lines
398:5 warning Function has cyclomatic complexity 8 but allowed no more than 7 code-complexity
405:28 warning Avoid to make time-based decisions in your business logic not-rely-on-time
457:31 warning Avoid to make time-based decisions in your business logic not-rely-on-time
478:5 warning Function body contains 79 lines but allowed no more than 50 lines function-max-lines
478:5 warning Function has cyclomatic complexity 10 but allowed no more than 7 code-complexity
498:10 warning Variable "outAmount0" is unused no-unused-vars
498:30 warning Variable "outAmount1" is unused no-unused-vars
498:50 warning Variable "liquiditySum" is unused no-unused-vars
500:31 warning Avoid to make time-based decisions in your business logic not-rely-on-time
680:5 warning Function body contains 107 lines but allowed no more than 50 lines function-max-lines
790:5 warning Function body contains 66 lines but allowed no more than 50 lines function-max-lines
859:5 warning Function body contains 133 lines but allowed no more than 50 lines function-max-lines
971:29 warning Code contains empty blocks no-empty-blocks
984:29 warning Code contains empty blocks no-empty-blocks
995:5 warning Function body contains 71 lines but allowed no more than 50 lines function-max-lines
1005:28 warning Avoid to make time-based decisions in your business logic not-rely-on-time
1073:24 warning Code contains empty blocks no-empty-blocks
1075:5 warning All public or external methods in a contract must override a definition from an interface comprehensive-interface
1075:32 warning Code contains empty blocks no-empty-blocks
CellarPoolShareLimitUSDCETH.sol
12:5 warning Explicitly mark visibility of state state-visibility
12:5 warning 'NONFUNGIBLEPOSITIONMANAGER' should start with _ private-vars-leading-underscore
15:5 warning Explicitly mark visibility of state state-visibility
15:5 warning 'UNISWAPV3FACTORY' should start with _ private-vars-leading-underscore
18:5 warning Explicitly mark visibility of state state-visibility
18:5 warning 'SWAPROUTER' should start with _ private-vars-leading-underscore
21:5 warning Explicitly mark visibility of state state-visibility
21:5 warning 'WETH' should start with _ private-vars-leading-underscore
23:5 warning Explicitly mark visibility of state state-visibility
23:5 warning 'FEEDOMINATOR' should start with _ private-vars-leading-underscore
25:5 warning Explicitly mark visibility of state state-visibility
25:5 warning 'YEAR' should start with _ private-vars-leading-underscore
42:5 warning Explicitly mark visibility of state state-visibility
42:5 warning 'lastManageTimestamp' should start with _ private-vars-leading-underscore
45:5 warning Constant name must be in capitalized SNAKE_CASE const-name-snakecase
80:5 warning Function order is incorrect, modifier definition can not go after constructor (line 47) ordering
122:5 warning 'totalPrice' should start with _ private-vars-leading-underscore
128:5 warning Function body contains 116 lines but allowed no more than 50 lines function-max-lines
128:5 warning Function has cyclomatic complexity 15 but allowed no more than 7 code-complexity
135:9 warning Provide an error message for require reason-string
135:17 warning Avoid to make time-based decisions in your business logic not-rely-on-time
250:9 warning Provide an error message for require reason-string
250:17 warning Avoid to make time-based decisions in your business logic not-rely-on-time
277:5 warning Function body contains 102 lines but allowed no more than 50 lines function-max-lines
277:5 warning 'invest' should start with _ private-vars-leading-underscore
331:15 warning Code contains empty blocks no-empty-blocks
331:24 warning Code contains empty blocks no-empty-blocks
360:15 warning Code contains empty blocks no-empty-blocks
360:24 warning Code contains empty blocks no-empty-blocks
382:5 warning 'getManagementFee' should start with _ private-vars-leading-underscore
398:5 warning Function body contains 77 lines but allowed no more than 50 lines function-max-lines
398:5 warning Function has cyclomatic complexity 8 but allowed no more than 7 code-complexity
405:28 warning Avoid to make time-based decisions in your business logic not-rely-on-time
457:31 warning Avoid to make time-based decisions in your business logic not-rely-on-time
478:5 warning Function body contains 79 lines but allowed no more than 50 lines function-max-lines
478:5 warning Function has cyclomatic complexity 10 but allowed no more than 7 code-complexity
498:10 warning Variable "outAmount0" is unused no-unused-vars
498:30 warning Variable "outAmount1" is unused no-unused-vars
498:50 warning Variable "liquiditySum" is unused no-unused-vars
500:31 warning Avoid to make time-based decisions in your business logic not-rely-on-time
680:5 warning Function body contains 107 lines but allowed no more than 50 lines function-max-lines
790:5 warning Function body contains 66 lines but allowed no more than 50 lines function-max-lines
859:5 warning Function body contains 133 lines but allowed no more than 50 lines function-max-lines
971:29 warning Code contains empty blocks no-empty-blocks
984:29 warning Code contains empty blocks no-empty-blocks
995:5 warning Function body contains 71 lines but allowed no more than 50 lines function-max-lines
1005:28 warning Avoid to make time-based decisions in your business logic not-rely-on-time
1073:24 warning Code contains empty blocks no-empty-blocks
1075:5 warning All public or external methods in a contract must override a definition from an interface comprehensive-interface
1075:32 warning Code contains empty blocks no-empty-blocks
interfaces.sol
568:5 warning 'RESOLUTION' should start with _ private-vars-leading-underscore
569:5 warning 'Q96' should start with _ private-vars-leading-underscore
575:2 error Line length must be no more than 120 but current length is 129 max-line-length
576:2 error Line length must be no more than 120 but current length is 123 max-line-length
578:2 error Line length must be no more than 120 but current length is 127 max-line-length
584:5 warning Function body contains 94 lines but allowed no more than 50 lines function-max-lines
604:13 warning Provide an error message for require reason-string
613:9 warning Provide an error message for require reason-string
689:5 warning 'MIN_TICK' should start with _ private-vars-leading-underscore
692:5 warning 'MAX_TICK' should start with _ private-vars-leading-underscore
697:2 error Line length must be no more than 120 but current length is 125 max-line-length
699:5 warning Function body contains 59 lines but allowed no more than 50 lines function-max-lines
699:5 warning Function has cyclomatic complexity 21 but allowed no more than 7 code-complexity
770:5 warning 'toUint128' should start with _ private-vars-leading-underscore
771:9 warning Provide an error message for require reason-string
780:5 warning Function order is incorrect, internal function can not go after private function (line 770) func-order
780:5 warning Function order is incorrect, internal pure function can not go after private pure function (line 770) ordering
926:1 warning Function order is incorrect, interface can not go after library definition (line 766) ordering
1186:5 warning 'BLOCK_LOCK_COUNT' should start with _ private-vars-leading-underscore
✖ 167 problems (4 errors, 163 warnings)
```
**[2] Filename capitalisation**
`interfaces.sol` -> `Interfaces.sol` for consistency
**[3] Lack of docstrings**
Functions should be well documented with docstrings defining intended operation.
**[4] Unclear Error Codes**
Many functions have `require()` clauses to evaluate conditions prior to execution. The error returned to the user is in the format `RX`, where X is an integer. It would be better for users to have a meaningful error message returned instead of a number.