# Security Audit of `Narfex-fiat-factory` contracts. ## Conclusion This audit was made by Auditor: Vladimir Smelov <vladimirfol@gmail.com>. Date: 2023-05-04 TODO ## Scope TODO ## Methodology 1. Blind audit. Understand the structure of the code without reading any docs. 2. Ask questions to developers. 3. Run static analyzers. 4. Find problems with: - backdoors; - bugs; - math; - potential leaking of funds; - potential locking of the contract; - validate arguments and events; - others. ## Result #### CRITICAL-1. At - NarfexLawyers.sol:123 ```solidity=123 if (getIsLawyer(_account)) return false; ``` should be "if (!getIsLawyer(_account)) return false;" ##### Status. NEW ______ #### CRITICAL-2. At - NarfexP2pRouter.sol:50,63-64 ```solidity=50 function swapToETH(address to, address token, uint amount) external { ``` ```solidity=63 data.outAmount = 0; data.outAmountMin = 0; ``` minAmountOut is absolutely requiring otherwise sandwich attack. ##### Status. NEW ______ #### CRITICAL-3. At - NarfexExchangerPool.sol:150 ```solidity=150 0, ``` sandwich attack ##### Status. NEW ______ #### CRITICAL-4. At - NarfexExchangerRouter3.sol:68,257-268,272-280,354-357 ```solidity=68 uint transferFee; ``` ```solidity=257 if (data.isExactOut) { /// Increase output amount by outgoing token fee for calculations data.outAmount = B.transferFee > 0 ? data.amount * (PERCENT_PRECISION + B.transferFee) / PERCENT_PRECISION : data.amount; } else { transferInAmount = data.amount; /// Decrease input amount for calculations data.inAmount = A.transferFee > 0 ? data.amount * (PERCENT_PRECISION - A.transferFee) / PERCENT_PRECISION : data.amount; } ``` ```solidity=272 if (data.isExactOut) { /// Increase input amount by inbound token fee transferInAmount = A.transferFee > 0 ? data.inAmount * (PERCENT_PRECISION + A.transferFee) / PERCENT_PRECISION : data.inAmount; require(data.inAmount <= data.inAmountMax, "Input amount is higher than maximum"); } else { require(data.outAmount >= data.outAmountMin, "Output amount is lower than minimum"); } ``` ```solidity=354 if (C.transferFee > 0) { /// Increasing the output amount offsets the loss from the fee data.outAmount = data.outAmount * (PERCENT_PRECISION + C.transferFee) / PERCENT_PRECISION; } ``` for deflationary reflective tokens (which are quite popular), it is not possible to set a clear transferFee, as it varies for each transfer and is generally unpredictable. Has this been examined in the context of transfer-fee tokens? <br /> Firstly, it is essential to consider that the PERCENT_PRECISION might differ among various tokens, for instance, a conceivable token could impose a fee as minute as 0.000000000000000000000001%. <br /> Secondly, a significant issue arises, as the equation x * (1+f) * (1-f) does not equal x; if a token deducts a fee of f% from the increased value of x*(1+f), the final amount sent to the pool would be insufficient for the swap; to address this, the following equation must be solved: y * (DENOMINATOR-f) / DENOMINATOR = x; y = x * DENOMINATOR / (DENOMINATOR-f); it is crucial to note that this is not equivalent to x * (DENOMINATOR+f) / DENOMINATOR, although the values are closely related. ##### Status. NEW ______ #### CRITICAL-5. At - NarfexExchangerRouter3.sol:131,148-149 ```solidity=131 uint uintValue = oracle.getPrice(_token) * uint(_amount) / USDC_PRECISION; ``` ```solidity=148 uint priceA = A.addr == address(USDC) ? USDC_PRECISION : A.price; uint priceB = B.addr == address(USDC) ? USDC_PRECISION : B.price; ``` 1 IDR = 0.000068 USDC = 6.8 * 1e-5 <br /> If their currency devalues by another 100 times, the oracle would return zero. <br /> It currently returns a price=68, although the exchange rate is 1.00 Indonesian Rupiah = 0.000067823873 US Dollars. <br /> We are already losing 1% in precision, which, in my opinion, is CRITICAL. <br /> The proposal is actually quite straightforward: set the price in the oracle not for 1 IDR but for 10e18 IDR, and then getAmountInUSDC = amount * getPrice / 10e18. ##### Status. NEW ______ #### CRITICAL-6. At - NarfexExchangerRouter3.sol:472-514 ```solidity=472 function _swap( SwapData memory data ) internal nonReentrant { require(data.path.length > 1, "Path length must be at least 2 addresses"); uint lastIndex = data.path.length - 1; Token memory A; /// First token Token memory B; /// Last token { /// Get the oracle data for the first and last tokens address[] memory sideTokens = new address[](2); sideTokens[0] = data.path[0]; sideTokens[1] = data.path[lastIndex]; INarfexOracle.TokenData[] memory tokensData = oracle.getTokensData(sideTokens, true); A = _assignTokenData(sideTokens[0], tokensData[0]); B = _assignTokenData(sideTokens[1], tokensData[1]); } require(A.addr != B.addr, "Can't swap the same tokens"); if (A.isFiat && B.isFiat) { /// If swap between fiats ExchangeData memory exchange = _getExchangeValues(A, B, data.amount, data.isExactOut); _swapFiats(data.from, data.to, A, B, exchange); return; } if (!A.isFiat && !B.isFiat) { /// Swap on DEX only _swapOnlyDEX(data, A, B); return; } if ((A.isFiat && B.addr == address(USDC)) || (B.isFiat && A.addr == address(USDC))) { /// If swap between fiat and USDC in the pool ExchangeData memory exchange = _getExchangeValues(A, B, data.amount, data.isExactOut); _swapFiatAndUSDC(data, A, B, exchange, false); return; } /// Swap with DEX and Fiats data.path = _getDEXSubPath(data.path, A.isFiat); _swapFiatWithDEX(data, A.isFiat ? A : B, A.isFiat ? B : A, A.isFiat); } ``` I do think it's mandatory to put require for outAmountMin here. Be very strict. Do not believe that you expected pre-calculations will be correct. Do IERC20.balanceOf(to) call before and after the swap and ensure the difference is >= outAmountMin ##### Status. NEW ______ #### CRITICAL-7. At - NarfexExchangerRouter3.sol:516-522 ```solidity=516 /// @notice Set a new pool address /// @param _newPoolAddress Another pool address /// @param _decimals Pool token decimals function setPool(address _newPoolAddress, uint8 _decimals) public onlyOwner { pool = INarfexExchangerPool(_newPoolAddress); USDC_PRECISION = 10**_decimals; } ``` it's not "Pool token decimals" but "USDC decimals". I believe it should be set in the constructor and not changed later. Or if you change USDC decimals then also change USDC address. ##### Status. NEW ______ #### MAJOR-1. At - NarfexLawyers.sol:145-152 ```solidity=145 /// @notice Randomly returns the currently available lawyer /// @return Active lawyer address function getLawyer() public view returns(address) { (address[] memory active, uint length) = getActiveLawyers(); if (length == 0) return address(0); uint index = uint(keccak256(abi.encodePacked(block.timestamp, block.basefee))) % length; return active[index]; } ``` this is not random! It may easily be manipulated via FlashBot attack ##### Status. NEW ______ #### MAJOR-2. At - NarfexP2pRouter.sol:53-55 ```solidity=53 path[0] = token; path[1] = address(USDC); path[2] = address(WETH); ``` it could be VERY inefficient, some tokens have big pool for token-WETH and small for token-USDC, such swap will lead to huge slippage. Consider removal of this function completely because you already have swap where path is explicitly passed. ##### Status. NEW ______ #### MAJOR-3. At - NarfexP2pRouter.sol:79 ```solidity=79 USDC.transferFrom(msg.sender, address(pool), _usdcAmount); ``` safeTransferFrom ##### Status. NEW ______ #### MAJOR-4. At - NarfexP2pRouter.sol:83,94 ```solidity=83 pool.increaseLimit(msg.sender, _fiatAddress, fiatAmount); ``` ```solidity=94 pool.decreaseLimit(msg.sender, _fiatAddress, _fiatAmount); ``` ??? i'm not sure. What if depositer will transfer out his tokens, limits will not be affected right? Unclear place. ##### Status. NEW ______ #### MAJOR-5. At - NarfexP2pRouter.sol:99 ```solidity=99 usdcAmount = usdcAmount * PERCENT_PRECISION / VALIDATOR_POOL_PERCENT; ``` ?????? explain this formula in comments because it's not obvious why do you return bigger usdc amount. In fact it leads to mis-pricing of fiats. time=1 1usd=100rub, deposit 1usd get 80rub, time=2 1usd=20rub, withdraw 80rub, where to get 4usd? ##### Status. NEW ______ #### MAJOR-6. At - NarfexP2pRouter.sol:102 ```solidity=102 USDC.transferFrom(address(pool), msg.sender, usdcAmount); ``` safeTransferFrom ##### Status. NEW ______ #### MAJOR-7. At - NarfexKYC.sol:10 ```solidity=10 mapping(address=>string) private _clients; ``` explain why do you use string here? why not IPFS link? Is it "Encrypted JSON encoded account personnel data"? What does it mean encrypted? Who can decrypt it? What if decryption key will leak? ##### Status. NEW ______ #### MAJOR-8. At - NarfexP2pBuyFactory.sol:86 ```solidity=86 function create(address _fiatAddress, uint16 _commission) public { ``` what if validator disagree with Oracle price? ##### Status. NEW ______ #### MAJOR-9. At - NarfexExchangerPool.sol:77 ```solidity=77 token.approve(router, 0); /// Remove allowance ``` safeApprove?? IERC20 interface attack?? ##### Status. NEW ______ #### MAJOR-10. At - NarfexExchangerPool.sol:105 ```solidity=105 token.transfer(_msgSender(), _amount); ``` safeTransfer ##### Status. NEW ______ #### MAJOR-11. At - NarfexExchangerPool.sol:114 ```solidity=114 IERC20(_address).transfer(_msgSender(), _amount); ``` safeTransfer ##### Status. NEW ______ #### MAJOR-12. At - NarfexExchangerPool.sol:154 ```solidity=154 emit WithdrawFee(feeAmount); ``` wrong event argument, place this one line above ##### Status. NEW ______ #### MAJOR-13. At - NarfexFiat.sol:19 ```solidity=19 contract NarfexFiat is IERC20 { ``` what is the guarantee that 1 NarfexFiat token actually matches 1 real fiat? Consider adding explanations in the contract. ##### Status. NEW ______ #### MAJOR-14. At - NarfexExchangerRouter3.sol:GLOBAL there is no unit tests! ##### Status. NEW ______ #### MAJOR-15. At - NarfexExchangerRouter3.sol:GLOBAL Consider refactoring. In general, I advocate for writing as little code as possible; many areas can be replaced with OpenZeppelin implementations of AccessControl and EnumerableSet. ##### Status. NEW ______ #### WARNING-1. At - NarfexLawyers.sol:12 ```solidity=12 bool[7][24] schedule; ``` should it be more flexible (e.g. 10:15-17:45)? what timezone is it? ##### Status. NEW ______ #### WARNING-2. At - NarfexLawyers.sol:55-59 ```solidity=55 for (uint8 w; w < 7; w++) { for (uint8 h; h < 24; h++) { schedule[w][h] = true; } } ``` all hours are true by default, is it correct? ##### Status. NEW ______ #### WARNING-3. At - NarfexLawyers.sol:76 ```solidity=76 for (uint i; i < list.length - 1; i++) { ``` very gas expensive O(n), use EnumerableMap with O(1) ##### Status. NEW ______ #### WARNING-4. At - NarfexFiatFactory.sol:60 ```solidity=60 fiats[tokenSymbol] = fiat; ``` require(fiats[tokenSymbol]==address(0), "already exists") ??? ##### Status. NEW ______ #### WARNING-5. At - INarfexOracle.sol:30 ```solidity=30 function getPrice(address _address) external view returns (uint); ``` explain return how many decimals how is it calculated ##### Status. NEW ______ #### WARNING-6. At - PancakeLibrary.sol:52-74 ```solidity=52 function pairFor(address tokenA, address tokenB) internal view returns (address pair) { (address token0, address token1) = sortTokens(tokenA, tokenB); /// ETH data bytes memory factory = hex'5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f'; bytes memory initCodeHash = hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'; if (block.chainid == 56) { /// BSC factory = hex'cA143Ce32Fe78f1f7019d7d551a6402fC5350c73'; initCodeHash = hex'00fb7f630766e6a796048ea87d01acd3068e8ff67d078148a3fa3f4a84f69bd5'; } if (block.chainid == 97) { /// BSC testnet factory = hex'B7926C0430Afb07AA7DEfDE6DA862aE0Bde767bc'; initCodeHash = hex'ecba335299a6693cb2ebc4782e74669b84290b6378ea3a3873c7231a8d7d1074'; } if (block.chainid == 137 || block.chainid == 42161 || block.chainid == 80001) { /// Polygon or Arbitrum factory = hex'c35DADB65012eC5796536bD9864eD8773aBc74C4'; initCodeHash = hex'e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303'; } if (block.chainid == 324) { /// zkSync Era factory = hex'40be1cBa6C5B47cDF9da7f963B6F761F4C60627D'; initCodeHash = hex'95d5c05820d58f1c8cc736b47fe10a29ddcd2cf73a0d842e8537b9fe510fc618'; } ``` check ##### Status. NEW ______ #### WARNING-7. At - NarfexP2pRouter.sol:33 ```solidity=33 uint amountLimit, ``` unclear complex semantic, use separate functions for exactIn and exactOut ##### Status. NEW ______ #### WARNING-8. At - NarfexP2pRouter.sol:34,50 ```solidity=34 uint deadline) external payable ensure(deadline) ``` ```solidity=50 function swapToETH(address to, address token, uint amount) external { ``` consider to put nonReentrant to external functions ##### Status. NEW ______ #### WARNING-9. At - NarfexFiat.sol:36-59 ```solidity=36 /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor( string memory tokenName, string memory tokenSymbol, address factoryAddress ) { _name = tokenName; _symbol = tokenSymbol; _factory = INarfexFiatFactory(factoryAddress); uint8 currentDecimals = 6; if (block.chainid == 56 || block.chainid == 97) { currentDecimals = 18; } _decimals = currentDecimals; } ``` wrong comment about decimals ##### Status. NEW ______ #### WARNING-10. At - NarfexFiat.sol:54 ```solidity=54 uint8 currentDecimals = 6; ``` it looks unsafe, some unclear magic numbers. Consider the usage of _factory.defaultDecimals or USDC.decimals() or somethings like this. ##### Status. NEW ______ #### WARNING-11. At - NarfexFiat.sol:61 ```solidity=61 modifier onlyOwner { ``` rename to onlyFullAccess ##### Status. NEW ______ #### WARNING-12. At - NarfexFiat.sol:GLOBAL ask some native speaker to check all comments :-) ##### Status. NEW ______ #### WARNING-13. At - NarfexFiat.sol:260 ```solidity=260 _balances[sender] = _balances[sender] - amount; ``` silent failure if sender does not have enough balance, consider adding a require with error message before ##### Status. NEW ______ #### WARNING-14. At - NarfexFiat.sol:296 ```solidity=296 _balances[account] = _balances[account] - amount; ``` silent failure if account does not have enough balance, add require with error message before ##### Status. NEW ______ #### WARNING-15. At - NarfexExchangerRouter3.sol:37 ```solidity=37 contract NarfexExchangerRouter3 is Ownable, ReentrancyGuard { ``` You may consider employing the approach used for via.protocol, which operates with dozens of bridges and DEXs, similarly to 1inch and Eywa. <br /> There is a router that accepts the method execute(Operation[] operations). <br /> struct Operation { Adapter; TokenIn; AmountIn; TokenOut; minAmountOut; }. <br /> Your router remains minimally simple. <br /> Subsequently, the logic for working with each adapter is delegated to separate smart contracts: AdapterNarfexFiatToUsdc, AdapterPancakeswapV2, AdapterPancakeswapV3, and Adapter1Inch. <br /> This results in a minimalistic router that merely executes a for loop through an array of operations, while the logic for interacting with each DeFi product (including Narfex fiat to USDC) is contained within separate adapters, where the code is concise, simply restructuring arguments for a specific DEX to ensure a unified interface for all. ##### Status. NEW ______ #### WARNING-16. At - NarfexExchangerRouter3.sol:95 ```solidity=95 if (block.chainid == 56 || block.chainid == 97) { ``` use USDC.decimals() ##### Status. NEW ______ #### WARNING-17. At - NarfexExchangerRouter3.sol:128 ```solidity=128 /// @return USDC amount ``` it's wrong in fact ##### Status. NEW ______ #### WARNING-18. At - NarfexExchangerRouter3.sol:205 ```solidity=205 INarfexFiat(B.addr).mintTo(data.to, exchange.outAmount); ``` usdcAmount is not assigned ##### Status. NEW ______ #### WARNING-19. At - NarfexExchangerRouter3.sol:206 ```solidity=206 } else { ``` require B.addr == address(USDC) ##### Status. NEW ______ #### WARNING-20. At - NarfexExchangerRouter3.sol:236-244,250-255 ```solidity=236 function _processSwapData(SwapData memory data) internal view { if (data.isExactOut) { data.amounts = PancakeLibrary.getAmountsIn(data.outAmount, data.path); data.inAmount = data.amounts[0]; } else { data.amounts = PancakeLibrary.getAmountsOut(data.inAmount, data.path); data.outAmount = data.amounts[data.amounts.length - 1]; } } ``` ```solidity=250 function _swapOnlyDEX( SwapData memory data, Token memory A, Token memory B ) private { ``` Why are we interacting directly with PancakeSwap instead of using their Router2? It seems that if the swap chain includes multiple tokens with feeOnTransfer, there might be an issue; I would recommend testing this on a testnet; at the very least, it seems that we should use the actual received target token amount taken from the return value of IPancakePair(PancakeLibrary.pairFor(input, output)).swap. ##### Status. NEW ______ #### WARNING-21. At - NarfexExchangerRouter3.sol:236 ```solidity=236 function _processSwapData(SwapData memory data) internal view { ``` We could refactor and logically separate everything to reduce the overall code complexity; essentially, we should only have a module for swapping fiat <-> USDC; for everything else, we can utilize the already-written router from PancakeSwap, which would likely reduce the volume of your router by a factor of 3. ##### Status. NEW ______ #### WARNING-22. At - NarfexExchangerRouter3.sol:236 ```solidity=236 function _processSwapData(SwapData memory data) internal view { ``` Will we support PancakeSwap v3 pairs? Currently, some new tokens are being deployed exclusively on v3! ##### Status. NEW ______ #### WARNING-23. At - NarfexExchangerRouter3.sol:286 ```solidity=286 assert(WETH.transfer(firstPair, transferInAmount)); ``` strictly following IERC20, you HAVE TO use safeTransfer ##### Status. NEW ______ #### WARNING-24. At - NarfexExchangerRouter3.sol:289,299 ```solidity=289 data.from.transfer(msg.value - transferInAmount); ``` ```solidity=299 data.to.transfer(data.outAmount); ``` never use transfer - https://consensys-net.webpkgcache.com/doc/-/s/consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/ ##### Status. NEW ______ #### WARNING-25. At - NarfexExchangerRouter3.sol:492-497,503-509 ```solidity=492 if (A.isFiat && B.isFiat) { /// If swap between fiats ExchangeData memory exchange = _getExchangeValues(A, B, data.amount, data.isExactOut); _swapFiats(data.from, data.to, A, B, exchange); return; } ``` ```solidity=503 if ((A.isFiat && B.addr == address(USDC)) || (B.isFiat && A.addr == address(USDC))) { /// If swap between fiat and USDC in the pool ExchangeData memory exchange = _getExchangeValues(A, B, data.amount, data.isExactOut); _swapFiatAndUSDC(data, A, B, exchange, false); return; } ``` put require path.length==2 ##### Status. NEW ______ #### WARNING-26. At - NarfexExchangerRouter3.sol:541 ```solidity=541 uint amountLimit, ``` unclear complex semantic, use separate functions for exactIn and exactOut ##### Status. NEW ______ #### WARNING-27. At - NarfexExchangerRouter3.sol:537-543 ```solidity=537 function swap( address[] memory path, bool isExactOut, uint amount, uint amountLimit, uint deadline) public payable ensure(deadline) { ``` add returns actual tokens amounts. This could be useful for the contract usage. ##### Status. NEW ______ #### LOW-1. At - NarfexLawyers.sol:12 ```solidity=12 bool[7][24] schedule; ``` I'm not sure is it as efficient as bit-shifts ##### Status. NEW ______ #### LOW-2. At - NarfexLawyers.sol:15 ```solidity=15 address public writer; ``` you can use OpenZeppelin's AccessControl ##### Status. NEW ______ #### LOW-3. At - NarfexLawyers.sol:16-18 ```solidity=16 mapping (address=>Lawyer) private _lawyers; mapping (address=>bool) private _isLawyer; address[] public list; ``` use EnumerableMap ##### Status. NEW ______ #### LOW-4. At - NarfexLawyers.sol:19 ```solidity=19 uint constant DAY = 86400; ``` use "1 days" included Solidity constant ##### Status. NEW ______ #### LOW-5. At - NarfexLawyers.sol:102 ```solidity=102 _lawyers[msg.sender].schedule = _schedule; ``` emit event ##### Status. NEW ______ #### LOW-6. At - NarfexLawyers.sol:108 ```solidity=108 _lawyers[msg.sender].isActive = _newState; ``` emit event ##### Status. NEW ______ #### LOW-7. At - NarfexLawyers.sol:125-126 ```solidity=125 uint8 weekDay = uint8((block.timestamp / DAY + 4) % 7); uint8 hour = uint8((block.timestamp / 60 / 60) % 24); ``` conversion to uint8 is redundant ##### Status. NEW ______ #### LOW-8. At - NarfexLawyers.sol:131 ```solidity=131 /// @return Array of addresses ``` note that last elements of array can be address(0), explain return ##### Status. NEW ______ #### LOW-9. At - NarfexFiatFactory.sol:17 ```solidity=17 /// Mapping TokenSymbol=>Address ``` you could use Openzeppelin EnumerableMap here ##### Status. NEW ______ #### LOW-10. At - NarfexFiatFactory.sol:20 ```solidity=20 NarfexFiat[] public fiatsList; ``` maybe the owner should be able to remove fiats from this list ##### Status. NEW ______ #### LOW-11. At - NarfexFiatFactory.sol:23 ```solidity=23 event CreateFiat(string tokenName, string tokenSymbol, NarfexFiat tokenAddress); ``` indexed ##### Status. NEW ______ #### LOW-12. At - NarfexFiatFactory.sol:47 ```solidity=47 function isHaveFullAccess(address account) internal view returns (bool) { ``` rename to hasFullAccess ##### Status. NEW ______ #### LOW-13. At - INarfexOracle.sol:9 ```solidity=9 bool isCustomReward; // Use defalt referral percent on false ``` typo in word "default" ##### Status. NEW ______ #### LOW-14. At - INarfexOracle.sol:10 ```solidity=10 uint price; // USD price only for fiats ``` decimals unclear add comment or calculations example ##### Status. NEW ______ #### LOW-15. At - INarfexOracle.sol:11 ```solidity=11 uint reward; // Referral percent only for fiats ``` decimals unclear add comment or calculations example ##### Status. NEW ______ #### LOW-16. At - INarfexOracle.sol:12 ```solidity=12 int commission; // Commission percent. Can be lower than zero ``` decimals unclear add comment or calculations example ##### Status. NEW ______ #### LOW-17. At - INarfexOracle.sol:13 ```solidity=13 uint transferFee; // Token transfer fee with 1000 decimals precision (20 for NRFX is 2%) ``` maybe 10_000? ##### Status. NEW ______ #### LOW-18. At - NarfexP2pRouter.sol:8 ```solidity=8 uint constant VALIDATOR_POOL_PERCENT = 8000; ``` explain how it works ##### Status. NEW ______ #### LOW-19. At - NarfexP2pRouter.sol:27 ```solidity=27 /// @dev Fiat to crypto must be exchanged via USDT ``` USDC ##### Status. NEW ______ #### LOW-20. At - NarfexP2pRouter.sol:69 ```solidity=69 function deposit(uint _usdcAmount, address _fiatAddress) public nonReentrant returns(uint) { ``` what does it do? add natspec comments ##### Status. NEW ______ #### LOW-21. At - NarfexP2pRouter.sol:88 ```solidity=88 function withdraw(uint _fiatAmount, address _fiatAddress) public nonReentrant returns(uint) { ``` what does it do? add natspec comments ##### Status. NEW ______ #### LOW-22. At - NarfexKYC.sol:11 ```solidity=11 mapping(address=>bool) private _verificators; ``` bad english, rename to verifiers ##### Status. NEW ______ #### LOW-23. At - NarfexKYC.sol:13 ```solidity=13 address public writer; ``` you can use OpenZeppelin's AccessControl ##### Status. NEW ______ #### LOW-24. At - NarfexKYC.sol:56 ```solidity=56 emit Verify(_account); ``` add _data to event ##### Status. NEW ______ #### LOW-25. At - NarfexKYC.sol:59 ```solidity=59 /// @notice Clead account personnel data ``` typo "Clean" ##### Status. NEW ______ #### LOW-26. At - NarfexKYC.sol:79 ```solidity=79 /// @notice Mark account as verificator ``` rename to verifier everywhere ##### Status. NEW ______ #### LOW-27. At - NarfexKYC.sol:104 ```solidity=104 emit Unblacklisted(_account); ``` bad english, rename to "RemovedFromBlacklist" ##### Status. NEW ______ #### LOW-28. At - NarfexKYC.sol:111 ```solidity=111 return _blacklisted[_account]; ``` should it be EnumerableSet to let fetch all blacklisted accounts? ##### Status. NEW ______ #### LOW-29. At - NarfexKYC.sol:116 ```solidity=116 /// @return Is can trade ``` bad english ##### Status. NEW ______ #### LOW-30. At - NarfexP2pBuyOffer.sol:15 ```solidity=15 uint32 createDate; ``` rename to timestamp ##### Status. NEW ______ #### LOW-31. At - NarfexP2pBuyOffer.sol:304 ```solidity=304 require(_amount <= getAvailableBalance(), "Not enouth free balance"); ``` typo enough ##### Status. NEW ______ #### LOW-32. At - NarfexP2pBuyOffer.sol:328 ```solidity=328 /// @notice Creade a new trade ``` typo create ##### Status. NEW ______ #### LOW-33. At - NarfexP2pBuyOffer.sol:330 ```solidity=330 /// @param bankAccountId Choosed bank account index ``` typo chosen ##### Status. NEW ______ #### LOW-34. At - NarfexP2pBuyOffer.sol:369 ```solidity=369 /// @param bankAccountId Choosed bank account index ``` typo chosen ##### Status. NEW ______ #### LOW-35. At - NarfexP2pBuyFactory.sol:29 ```solidity=29 contract NarfexP2pBuyFactory is Ownable { ``` Buy and Sell factories are similar, consider refactoring ##### Status. NEW ______ #### LOW-36. At - NarfexP2pBuyFactory.sol:31 ```solidity=31 struct Offer { /// Offer getter structure ``` remove "getter" ##### Status. NEW ______ #### LOW-37. At - NarfexP2pBuyFactory.sol:37-41 ```solidity=37 uint commission; uint totalCommission; uint minTrade; uint maxTrade; uint tradesQuote; ``` unclear semantics consider documenting ##### Status. NEW ______ #### LOW-38. At - NarfexP2pBuyFactory.sol:49 ```solidity=49 uint public tradesLimit = 2; /// Trades count per one offer in one time ``` how is it used? ##### Status. NEW ______ #### LOW-39. At - TestnetUSDC.sol:23-24 ```solidity=23 _name = 'Circle USD'; _symbol = 'USDC'; ``` add TEST ##### Status. NEW ______ #### LOW-40. At - NarfexExchangerPool.sol:21 ```solidity=21 contract NarfexExchangerPool is Ownable { ``` inherit from INarfexExchangerPool ##### Status. NEW ______ #### LOW-41. At - NarfexExchangerPool.sol:28 ```solidity=28 mapping(address => mapping(address => uint)) public validatorLimit; ``` explain mapping keys ##### Status. NEW ______ #### LOW-42. At - NarfexExchangerPool.sol:29 ```solidity=29 uint private feeAmount; ``` explain what this is in the comment ##### Status. NEW ______ #### LOW-43. At - NarfexExchangerPool.sol:34 ```solidity=34 event Withdraw(address recipient, uint amount); ``` indexed ##### Status. NEW ______ #### LOW-44. At - NarfexExchangerPool.sol:36 ```solidity=36 event IncreaseValidatorLimit(address indexed validator, address fiat, uint amount); ``` indexed ##### Status. NEW ______ #### LOW-45. At - NarfexExchangerPool.sol:37 ```solidity=37 event DecreaseValidatorLimit(address indexed validator, address fiat, uint amount); ``` indexed ##### Status. NEW ______ #### LOW-46. At - NarfexExchangerPool.sol:119 ```solidity=119 emit IncreaseValidatorLimit(_validator, _fiatAddress, _amount); ``` emit final amount ##### Status. NEW ______ #### LOW-47. At - NarfexExchangerPool.sol:125 ```solidity=125 emit DecreaseValidatorLimit(_validator, _fiatAddress, _amount); ``` emit final amount ##### Status. NEW ______ #### LOW-48. At - NarfexExchangerPool.sol:137 ```solidity=137 feeAmount += _amount; ``` emit event ##### Status. NEW ______ #### LOW-49. At - NarfexExchangerPool.sol:143 ```solidity=143 path[0] = address(token); ``` I would recommend also to add separate function to withdraw token without swap ##### Status. NEW ______ #### LOW-50. At - NarfexExchangerPool.sol:151 ```solidity=151 block.timestamp + 20 minutes ``` not need to add anything ##### Status. NEW ______ #### LOW-51. At - NarfexFiat.sol:7 ```solidity=7 interface INarfexFiatFactory { ``` split to another file ##### Status. NEW ______ #### LOW-52. At - NarfexFiat.sol:19 ```solidity=19 contract NarfexFiat is IERC20 { ``` inherit from INarfexFiat ##### Status. NEW ______ #### LOW-53. At - NarfexFiat.sol:30-32 ```solidity=30 string private _name; string private _symbol; uint8 private _decimals; ``` Consider the usage of the "immutable" declaration. ##### Status. NEW ______ #### LOW-54. At - NarfexFiat.sol:34 ```solidity=34 uint256 constant MAX_INT = type(uint).max; ``` explicit "private" declaration. ##### Status. NEW ______ #### LOW-55. At - NarfexFiat.sol:67 ```solidity=67 * @dev Returns the bep token owner. ``` remove "bep" ##### Status. NEW ______ #### LOW-56. At - NarfexFiat.sol:80 ```solidity=80 /// @notice Returns true if the specified address have full access to user funds management ``` "has" not "have" ##### Status. NEW ______ #### LOW-57. At - NarfexFiat.sol:83 ```solidity=83 function isHaveFullAccess(address account) internal view returns (bool) { ``` rename to hasFullAccess ##### Status. NEW ______ #### LOW-58. At - NarfexFiat.sol:88,95,97,102,109,116,123 ```solidity=88 * @dev Returns the token name. ``` ```solidity=95 * @dev Returns the token decimals. ``` ```solidity=97 function decimals() public view returns (uint8) { ``` ```solidity=102 * @dev Returns the token symbol. ``` ```solidity=109 * @dev See {BEP20-totalSupply}. ``` ```solidity=116 * @dev See {BEP20-balanceOf}. ``` ```solidity=123 * @dev See {BEP20-transfer}. ``` everywhere use "@inheritdoc IERC20" instead ##### Status. NEW ______ #### LOW-59. At - NarfexFiat.sol:164 ```solidity=164 * required by the EIP. See the note at the beginning of {BEP20}; ``` remove all this BEP20 comments, just use @inheritdoc everywhere ##### Status. NEW ______ #### LOW-60. At - NarfexFiat.sol:202 ```solidity=202 return true; ``` remove the return value, there is no standard for this method. It's not required. ##### Status. NEW ______ #### LOW-61. At - NarfexFiat.sol:219,200 ```solidity=219 function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { ``` ```solidity=200 function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { ``` add NatSpec notice and param ##### Status. NEW ______ #### LOW-62. At - NarfexFiat.sol:225 ```solidity=225 return true; ``` remove the return value, there is no standard for this method. It's not required. ##### Status. NEW ______ #### LOW-63. At - NarfexFiat.sol:228 ```solidity=228 /// @notice Mint amount of tokens to specified reciever address ``` typo "receiver" ##### Status. NEW ______ #### LOW-64. At - NarfexFiat.sol:232 ```solidity=232 /// @return Always true ``` remove the return value, there is no standard for this method. It's not required. ##### Status. NEW ______ #### LOW-65. At - NarfexFiat.sol:257-258,275,294,319-320 ```solidity=257 require(sender != address(0), "BEP20: transfer from the zero address"); require(recipient != address(0), "BEP20: transfer to the zero address"); ``` ```solidity=275 require(account != address(0), "BEP20: mint to the zero address"); ``` ```solidity=294 require(account != address(0), "BEP20: burn from the zero address"); ``` ```solidity=319 require(owner != address(0), "BEP20: approve from the zero address"); require(spender != address(0), "BEP20: approve to the zero address"); ``` to be strict it's not always BEP20, consider renaming with "IERC20:" or "NarfexFiat:" ##### Status. NEW ______ #### LOW-66. At - NarfexExchangerRouter3.sol:10-29 ```solidity=10 interface INarfexFiat is IERC20 { function burnFrom(address _address, uint _amount) external; function mintTo(address _address, uint _amount) external; } interface INarfexExchangerPool { function getFeeAmount() external view returns (uint); function getBalance() external view returns (uint); function approveRouter() external; function increaseLimit(address _validator, address _fiatAddress, uint _amount) external; function decreaseLimit(address _validator, address _fiatAddress, uint _amount) external; function increaseFeeAmount(uint _amount) external; function clearFeeAmount() external; } interface IWETH { function deposit() external payable; function transfer(address to, uint value) external returns (bool); function withdraw(uint) external; } ``` split to other files and reuse ##### Status. NEW ______ #### LOW-67. At - NarfexExchangerRouter3.sol:37 ```solidity=37 contract NarfexExchangerRouter3 is Ownable, ReentrancyGuard { ``` rename to NarfexExchangerRouter ##### Status. NEW ______ #### LOW-68. At - NarfexExchangerRouter3.sol:40 ```solidity=40 /// Structures for solving the problem of limiting the number of variables ``` The presence of triple slashes (///) instead of double slashes (//) outside of NatSpec comments is unconventional; it is recommended to follow generally accepted practice; in essence, the utilization of triple slashes (///) can be considered erroneous, as the Solidity compiler (solc) interprets them as NatSpec comments with the Doxygen @notice tag; it is peculiar that the compiler processes them without any warnings. ##### Status. NEW ______ #### LOW-69. At - NarfexExchangerRouter3.sol:43 ```solidity=43 uint rate; ``` add comment or example of calculations, what is the denominator of the rate? consider documenting USDC_PRECISION ##### Status. NEW ______ #### LOW-70. At - NarfexExchangerRouter3.sol:53-56 ```solidity=53 uint inAmount; uint inAmountMax; uint outAmount; uint outAmountMin; ``` what is the difference with inAmount, consider documenting or rename to actualInAmount, actualOutAmount ##### Status. NEW ______ #### LOW-71. At - NarfexExchangerRouter3.sol:65,67-68 ```solidity=65 int commission; ``` ```solidity=67 uint reward; uint transferFee; ``` denominated by PRECISION or PERCENT_PRECISION? ##### Status. NEW ______ #### LOW-72. At - NarfexExchangerRouter3.sol:77 ```solidity=77 uint internal USDC_PRECISION = 10**6; ``` set in constructor ##### Status. NEW ______ #### LOW-73. At - NarfexExchangerRouter3.sol:79 ```solidity=79 uint constant MAX_INT = 2**256 - 1; ``` use type(uint).max ##### Status. NEW ______ #### LOW-74. At - NarfexExchangerRouter3.sol:102 ```solidity=102 modifier ensure(uint deadline) { ``` rename with ensureDeadline ##### Status. NEW ______ #### LOW-75. At - NarfexExchangerRouter3.sol:107 ```solidity=107 event SwapFiat(address indexed _account, address _fromToken, address _toToken, ExchangeData _exchange); ``` indexed _fromToken _toToken ##### Status. NEW ______ #### LOW-76. At - NarfexExchangerRouter3.sol:108 ```solidity=108 event SwapDEX(address indexed _account, address _fromToken, address _toToken, uint inAmount, uint outAmount); ``` indexed _fromToken _toToken ##### Status. NEW ______ #### LOW-77. At - NarfexExchangerRouter3.sol:112 ```solidity=112 assert(msg.sender == address(WETH)); ``` use require with error message ##### Status. NEW ______ #### LOW-78. At - NarfexExchangerRouter3.sol:246 ```solidity=246 /// @notice Exchange only between crypto Сoins through liquidity pairs ``` wrong term. replace Coins with Tokens, https://zerion.io/blog/crypto-coins-vs-tokens-the-difference-explained/ ##### Status. NEW ______ #### LOW-79. At - NarfexExchangerRouter3.sol:284 ```solidity=284 require(msg.value >= transferInAmount, "BNB is not sended"); ``` typo in "sent" ##### Status. NEW ______ #### LOW-80. At - NarfexExchangerRouter3.sol:332 ```solidity=332 function _swapFiatWithDEX( ``` very long function consider refactoring ##### Status. NEW ______ #### LOW-81. At - NarfexExchangerRouter3.sol:474 ```solidity=474 ) internal nonReentrant ``` I would prefer to put nonReentrant on external/public functions instead ##### Status. NEW ______ #### LOW-82. At - NarfexExchangerRouter3.sol:519,526 ```solidity=519 function setPool(address _newPoolAddress, uint8 _decimals) public onlyOwner { ``` ```solidity=526 function setOracle(address _newOracleAddress) public onlyOwner { ``` emit event ##### Status. NEW ______ #### LOW-83. At - NarfexExchangerRouter3.sol:551 ```solidity=551 data.inAmountMax = isExactOut ? amountLimit : MAX_INT; ``` looks scary, consider adding a comment ##### Status. NEW