# Final Security Audit of `DexioLock` contract. ![](https://i.imgur.com/DreWe7g.png) ## Conclusion This audit was made by [Web3Go](https://web3go.tech) Auditor: Vladimir Smelov <vsmelov.job@gmail.com>. Date: 2022-08-09 In the final contract were **NOT FOUND**: - Backdoors for investor funds withdrawal by anyone. - Bugs allow stealing money from the contract. - Any severe security problems. - Any serious problems with gas consumption. The client was acknowledged about all notes below. ## Scope ### Pre-audit - DexioLock.sol (md5 hash - `f948096a0ae089f57977fdc1fe47fdcc`) ### Final audit - DexioLock.sol https://gist.github.com/vsmelov/7d3a26e56534049990d37d620a59b0f7 ## Methodology 1. Blind audit. Understand the structure of the code without reading any docs. 2. Ask questions to developers. 3. Draw the scheme of cross-contracts interactions. 4. Write user stories and usage cases. 5. Run static analyzers. 6. Find problems with: - backdoors; - bugs; - math; - potential leaking of funds; - potential locking of the contract; - validate arguments and events; - others. ## Result ### Critical There are no critical in the final version of the contract. ___ ### Major There are no major in the final version of the contract. ___ ### Warning ___ #### WARNING-1. Transfer method inconsistency. At: - contracts/DexioLock.sol:545 In unlock you use ```solidity=545 IERC20(userLock.token).safeTransfer(msg.sender, unlockAmount); //zz why not safeTransferFromEnsureExactAmount ``` But at - contracts/DexioLock.sol:481 You use ```solidity=481 safeTransferFromEnsureExactAmount(token, msg.sender, address(this), amount); ``` why don't you use the same functions for lock and unlock? ##### Recommendation. Consider the usage of the same functions to lock/unlock tokens or adding comments or refactor. ##### Status. ACKNOWLEDGED - **not a security issue**. ##### Client's commentary. > It's to save gas; if someone was able to put tokens via safeTransferFromEnsureExactAmount then it means that token is NORMAL (actual transfer amount equals to passed amount). So you can let the user withdraw it back in a usual way. ___ ### Comment. ___ #### COMMENT-1. Redundant struct. At - contracts/DexioLock.sol:422 ```solidity=422 struct CumulativeLockInfo { address token; uint256 amount; } ``` The token is a key to the mapping The factory is never set. The amount is never used for any operational purposes (only in view method). ##### Recommendation. Consider removing this struct. Or, at least, create a direct ``` mapping(address => uint256) public cumulativeLockInfo; ``` Or use just ``` IERC20(token).balanceOf(address(this)); ``` to check how much tokens are locked in the contract. ##### Status. PARTIALLY FIXED - `factory was removed from the struct`. ___ #### COMMENT-2. Use mappings rather than arrays. At - contracts/DexioLock.sol:430 ```solidity=430 Lock[] private _locks; ``` Mappings are cheaper. Iteration over array is never used inside transactional (not-view) methods. Also, you will be able remove the item and receive gas compensation. ##### Recommendation. Consider using a mapping. ##### Status. ACKNOWLEDGED ___ #### COMMENT-3. Bad code style. At - contracts/DexioLock.sol:431 ```solidity=431 mapping(address => EnumerableSet.UintSet) private _userNormalLockIds; ``` - contracts/DexioLock.sol:590 ```solidity=590 CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[ userLock.token ]; ``` The identation is contr-intuitive. ##### Recommendation. Follow the identation code-style from https://docs.soliditylang.org/en/v0.8.16/style-guide.html ##### Status. ACKNOWLEDGED ___ #### COMMENT-4. Confusing the "NormalToken" suffix. At - contracts/DexioLock.sol You use the suffix "Normal" often, but it's unclear what you mean by "normal." ##### Recommendation. Consider adding a definition of the "normal token" as a comment. ##### Status. ACKNOWLEDGED ___ #### COMMENT-5. Redundant struct. At - contracts/DexioLock.sol:436 ```solidity=436 mapping(address => EnumerableSet.UintSet) private _tokenToLockIds; ``` It is not needed. The frontend may fetch events. Just a wasting of gas. ##### Recommendation. Consider removing or adding comments on why it is needed. ##### Status. ACKNOWLEDGED ___ #### COMMENT-6. Unused event. At - contracts/DexioLock.sol:437 ```solidity=437 event Log(uint gas); ``` The event is never used. ##### Recommendation. Remove. ##### Status. FIXED - The event was removed. ___ #### COMMENT-7. Redundant check. At - contracts/DexioLock.sol:461 ```solidity=460 modifier validLock(uint256 lockId) { require(lockId < _locks.length, "Invalid lock id"); _; } ``` Accessing an array past its end causes a failing assertion. Methods .push() and .push(value) can be used to append a new element at the end of the array, where .push() appends a zero-initialized element and returns a reference to it. Check this - https://docs.soliditylang.org/en/v0.6.12/types.html ##### Recommendation. Remove. ##### Status. ACKNOWLEDGED ___ #### COMMENT-8. Use indentation. At - contracts/DexioLock.sol:509-516 ```solidity=509 Lock memory newLock = Lock({ id: id, token: token, owner: owner, amount: amount, lockDate: block.timestamp, unlockDate: unlockDate }); ``` The identations is wrong. Follow the solidity style-guide. ##### Recommendation. Refactor ##### Status. ACKNOWLEDGED ___ #### COMMENT-9. Rephrase the error message. At - contracts/DexioLock.sol:523 ##### Recommendation. Refactor ##### Status. ACKNOWLEDGED ___ #### COMMENT-10. Additional timestamp sanity check. At - contracts/DexioLock.sol:476 ```solidity=475 require( unlockDate > block.timestamp, "Unlock date should be after current time" ); ``` Sometimes frontend may mess up with seconds and microseconds (x1000 bigger), so it's wise to check, `require(unlockDate - block.timestamp < 1000 days)` ##### Recommendation. Consider adding additional check on `unlockDate`. ##### Status. ACKNOWLEDGED ___ #### COMMENT-11. Function duplication. At - contracts/DexioLock.sol:664 - contracts/DexioLock.sol:633 ```solidity=662 function totalTokenLockedCount() external view returns (uint256) { return allNormalTokenLockedCount(); } ``` Two functions do the same work. ##### Recommendation. Consider removing one of two identical functions. ##### Status. ACKNOWLEDGED #### COMMENT-12. Impossible condition. At: - contracts/DexioLock.sol:529 ```solidity=528 uint256 unlockAmount = userLock.amount; if (IERC20(userLock.token).balanceOf(address(this)) < unlockAmount) { unlockAmount = IERC20(userLock.token).balanceOf(address(this)); //cc external call twice //zz how is it possible } ``` It's not clear in which use case it's possible. ##### Recommendation. Consider adding comments or example of the usage scenario when it is possible. ##### Status. ACKNOWLEDGED