This document is a security audit report performed by danbogd, where LCX has been reviewed.
Сommit hash .
In total, 4 issues were reported including:
- 1 medium severity issues
- 1 low severity issues
- 2 owner privileges (ability of owner to manipulate contract, may be risky for investors).
- 0 notes.
No critical security issues were found.
Incoming addresses should be checked for an empty value(0x0 address).
Lines 241, 561.
function setTokenAddress(IERC20 token) public onlyOwner returns(bool){
LCXToken = token;
return true;
}
function setTokenVestingAddress(TokenVesting tokenVestingAddress) public onlyOwner returns(bool){
vestingContractAddress = tokenVestingAddress;
return true;
}
Contract owner allow himself to:
- upgrade contract and implement any logic in the new contract. And even if the new contract will be audited, at any time possible to change the address of the new contract again to not audited and insecure.
Lines 241, 561.
function setTokenAddress(IERC20 token) public onlyOwner returns(bool){
LCXToken = token;
return true;
}
function setTokenVestingAddress(TokenVesting tokenVestingAddress) public onlyOwner returns(bool){
vestingContractAddress = tokenVestingAddress;
return true;
}
- revoke the vesting.
function revoke(address account) public onlyOwner {
VestedToken storage vested = vestedUser[account];
require(!vested.revoked);
uint256 balance = vested.totalToken;
uint256 unreleased = _releasableAmount(account);
uint256 refund = balance.sub(unreleased);
vested.revoked = true;
vested.totalToken = unreleased;
LCXToken.safeTransfer(owner(), refund);
emit VestingRevoked(account);
}
There are no checks that the beneficiary address can be a contract address.
Example:
The user was able to divest himself of his interest even though the tokens never moved. He didn’t sell the timelocked tokens itself. He sold the future ownership of the tokens. The user is asked by the deployer of the LCX vesting contract for the address where he’d like to receive his tokens after the releaseTime expires in 3 years. User deploys the “Bypasser” contract and gives its address to the deployer of the LCX vesting contract. The Bypasser contract didn’t magically make the timelocked tokens transferable — it made the future ownership of the timelocked tokens transferable.
More details here.
Line: 262.
function _setVesting(address account, uint256 amount, uint256 cliff, uint256 duration, uint256 startAt) internal {
require(account!=address(0));
require(cliff<=duration);
VestedToken storage vested = vestedUser[account];
vested.cliff = cliff;
vested.start = startAt;
vested.duration = duration;
vested.totalToken = amount;
vested.releasedToken = 0;
vested.revoked = false;
}
The review did not show any critical issues, some of medium and low severity issues were found.