[L01] require() should be used instead of assert()
Findings:
2022-09-canto/src/Swap/BaseV1-periphery.sol::85 => assert(msg.sender == address(wcanto)); // only accept ETH via fallback from the WETH contract
2022-09-canto/src/Swap/BaseV1-periphery.sol::236 => assert(amountAOptimal <= amountADesired);
2022-09-canto/src/Swap/BaseV1-periphery.sol::282 => assert(wcanto.transfer(pair, amountCANTO));
2022-09-canto/src/Swap/BaseV1-periphery.sol::428 => assert(wcanto.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]));
[L02] Unbounded loops with external calls
Impact
The interface and the function should require a start index and a
lenght, so that the index composition can be fetched in batches without
running out of gas. If there are thousands of index components (e.g.
like the Wilshire 5000 index), the function may revert
Findings:
2022-09-canto/src/Swap/BaseV1-core.sol::197 => function prices(address tokenIn, uint amountIn, uint points) external view returns (uint[] memory) {
2022-09-canto/src/Swap/BaseV1-core.sol::201 => function sample(address tokenIn, uint amountIn, uint points, uint window) public view returns (uint[] memory) {
2022-09-canto/src/Swap/BaseV1-core.sol::237 => function sampleReserves(uint points, uint window) public view returns (uint[] memory, uint[] memory) {
2022-09-canto/src/Swap/BaseV1-core.sol::271 => function sampleSupply(uint points, uint window) public view returns (uint[] memory) {
2022-09-canto/src/Swap/BaseV1-periphery.sol::31 => function sampleReserves(uint points, uint window) external view returns(uint[] memory, uint[] memory);
2022-09-canto/src/Swap/BaseV1-periphery.sol::32 => function sampleSupply(uint points, uint window) external view returns(uint[] memory);
2022-09-canto/src/Swap/BaseV1-periphery.sol::33 => function sample(address tokenIn, uint amountIn, uint points, uint window) external view returns(uint[] memory);
2022-09-canto/src/Swap/BaseV1-periphery.sol::141 => function getAmountsOut(uint amountIn, route[] memory routes) public view returns (uint[] memory amounts) {
2022-09-canto/src/Swap/BaseV1-periphery.sol::370 => function _swap(uint[] memory amounts, route[] memory routes, address _to) internal virtual {
2022-09-canto/src/Swap/BaseV1-periphery.sol::418 => function swapExactCANTOForTokens(uint amountOutMin, route[] calldata routes, address to, uint deadline)
2022-09-canto/src/Swap/BaseV1-periphery.sol::432 => function swapExactTokensForCANTO(uint amountIn, uint amountOutMin, route[] calldata routes, address to, uint deadline)
[L03] Missing checks for address(0x0) when assigning values to address state variables
[L05] abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()
Impact
Use abi.encode() instead which will pad items to 32 bytes, which will prevent hash collisions (e.g. abi.encodePacked(0x123,0x456) => 0x123456 => abi.encodePacked(0x1,0x23456), but abi.encode(0x123,0x456) => 0x0...1230...456). “Unless there is a compelling reason, abi.encode should be preferred”. If there is only one argument to abi.encodePacked() it can often be cast to bytes() or bytes32() instead.
Findings:
2022-09-canto/src/Swap/BaseV1-core.sol::603 => bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable)); // notice salt includes stable as well, 3 parameters
2022-09-canto/src/Swap/BaseV1-periphery.sol::103 => pair = address(uint160(uint256(keccak256(abi.encodePacked(
2022-09-canto/src/Swap/BaseV1-periphery.sol::106 => keccak256(abi.encodePacked(token0, token1, stable)),
2022-09-canto/src/Swap/BaseV1-periphery.sol::597 => return (keccak256(abi.encodePacked(str1)) == keccak256(abi.encodePacked(str2)));
[L06] _safeMint() should be used rather than _mint() wherever possible
Findings:
2022-09-canto/src/Swap/BaseV1-core.sol::305 => _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
2022-09-canto/src/Swap/BaseV1-core.sol::311 => _mint(to, liquidity);
Non-Critical Issues
[N01] Adding a return statement when the function defines a named return variable, is redundant
2022-09-canto/src/Swap/BaseV1-core.sol::1 => // SPDX-License-Identifier: MIT
2022-09-canto/src/Swap/BaseV1-periphery.sol::1 => // SPDX-License-Identifier: MIT
[N08] public functions not called by the contract should be declared external instead
Impact
Contracts are allowed to override their parents’ functions and change the visibility from external to public.
Findings:
2022-09-canto/src/Swap/BaseV1-core.sol::118 => function lastObservation() public view returns (Observation memory) {
2022-09-canto/src/Swap/BaseV1-core.sol::130 => function getReserves() public view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast) {
2022-09-canto/src/Swap/BaseV1-core.sol::158 => function currentCumulativePrices() public view returns (uint reserve0Cumulative, uint reserve1Cumulative, uint blockTimestamp) {
2022-09-canto/src/Swap/BaseV1-core.sol::201 => function sample(address tokenIn, uint amountIn, uint points, uint window) public view returns (uint[] memory) {
2022-09-canto/src/Swap/BaseV1-core.sol::237 => function sampleReserves(uint points, uint window) public view returns (uint[] memory, uint[] memory) {
2022-09-canto/src/Swap/BaseV1-core.sol::271 => function sampleSupply(uint points, uint window) public view returns (uint[] memory) {
2022-09-canto/src/Swap/BaseV1-core.sol::439 => function _k(uint x, uint y) public view returns (uint) {
2022-09-canto/src/Swap/BaseV1-periphery.sol::94 => function sortTokens(address tokenA, address tokenB) public pure returns (address token0, address token1) {
2022-09-canto/src/Swap/BaseV1-periphery.sol::101 => function pairFor(address tokenA, address tokenB, bool stable) public view returns (address pair) {
2022-09-canto/src/Swap/BaseV1-periphery.sol::119 => function getReserves(address tokenA, address tokenB, bool stable) public view returns (uint reserveA, uint reserveB) {
2022-09-canto/src/Swap/BaseV1-periphery.sol::141 => function getAmountsOut(uint amountIn, route[] memory routes) public view returns (uint[] memory amounts) {
2022-09-canto/src/Swap/BaseV1-periphery.sol::153 => function isPair(address pair) public view returns (bool) {
[N09] Numeric values having to do with time should use time units for readability
Impact
There are units for seconds, minutes, hours, days, and weeks
Findings:
2022-09-canto/src/Swap/BaseV1-core.sol::36 => // Structure to capture time period obervations every 30 minutes, used for local oracles
2022-09-canto/src/Swap/BaseV1-core.sol::44 => // Capture oracle reading every 30 minutes
2022-09-canto/src/Swap/BaseV1-core.sol::147 => timeElapsed = blockTimestamp - _point.timestamp; // compare the last observation with current timestamp, if greater than 30 minutes, record a new event
[N10] Constant redefined elsewhere
Impact
Consider defining in only one contract so that values cannot become out of sync when only one location is updated
Findings:
2022-09-canto/src/Swap/BaseV1-core.sol::481 => bytes32 digest = keccak256(
2022-09-canto/src/Swap/BaseV1-core.sol::603 => bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable)); // notice salt includes stable as well, 3 parameters
[N11] Interfaces should be moved to separate files
Impact
Most of the other interfaces in this project are in their own file in
the interfaces directory. The interfaces below do not follow this
pattern
[L01] require() should be used instead of assert()
Findings:
[L02] Unbounded loops with external calls
Impact
The interface and the function should require a start index and a lenght, so that the index composition can be fetched in batches without running out of gas. If there are thousands of index components (e.g. like the Wilshire 5000 index), the function may revert
Findings:
[L03] Missing checks for
address(0x0)
when assigning values toaddress
state variablesFindings:
[L04] Unused
receive()
function will lock Ether in contractImpact
If the intention is for the Ether to be used, the function should call another function, otherwise it should revert
Findings:
[L05]
abi.encodePacked()
should not be used with dynamic types when passing the result to a hash function such askeccak256()
Impact
Use abi.encode() instead which will pad items to 32 bytes, which will prevent hash collisions (e.g. abi.encodePacked(0x123,0x456) => 0x123456 => abi.encodePacked(0x1,0x23456), but abi.encode(0x123,0x456) => 0x0...1230...456). “Unless there is a compelling reason, abi.encode should be preferred”. If there is only one argument to abi.encodePacked() it can often be cast to bytes() or bytes32() instead.
Findings:
[L06]
_safeMint()
should be used rather than_mint()
wherever possibleFindings:
Non-Critical Issues
[N01] Adding a return statement when the function defines a named return variable, is redundant
Findings:
[N02]
require()
/revert()
statements should have descriptive reason stringsFindings:
[N03]
constants
should be defined rather than using magic numbersFindings:
[N04] Use a more recent version of solidity
Impact
Use a solidity version of at least 0.8.12 to get string.concat() to be used instead of abi.encodePacked(,)
Findings:
[N05] Variable names that consist of all capital letters should be reserved for
const
/immutable
variablesImpact
If the variable needs to be different based on which class it comes from, a view/pure function should be used instead (e.g. like this).
Findings:
[N06] Event is missing
indexed
fieldsImpact
Each
event
should use threeindexed
fields if there are three or more fieldsFindings:
[N07] Unused file
Impact
Findings:
[N08]
public
functions not called by the contract should be declaredexternal
insteadImpact
Contracts are allowed to override their parents’ functions and change the visibility from external to public.
Findings:
[N09] Numeric values having to do with time should use time units for readability
Impact
There are units for seconds, minutes, hours, days, and weeks
Findings:
[N10] Constant redefined elsewhere
Impact
Consider defining in only one contract so that values cannot become out of sync when only one location is updated
Findings:
[N11] Interfaces should be moved to separate files
Impact
Most of the other interfaces in this project are in their own file in the interfaces directory. The interfaces below do not follow this pattern
Findings: