Note that we only audited the code available to us on this URL at the time of the audit. If the URL is not from any block explorer (main net), it may be subject to change. Always check the contract address on this audit report and compare it to the token you are doing research for.
No fix needed, will not bring problems.
Presence of unused variables
Unused variables are allowed in Solidity and they do not pose a direct security issue. It is best practice though to avoid them as they can: cause an increase in computations (and unnecessary gas consumption), indicate bugs or malformed data structures and they are generally a sign of poor code quality or cause code noise and decrease readability of the code
1
Recommendation
Remove all unused variables from the code base.
Update notes
No fix needed, will not bring problems.
Shadowing State Variables
Solidity allows for ambiguous naming of state variables when inheritance is used. Contract A
with a variable x
could inherit contract B
that also has a state variable x
defined. This would result in two separate versions of x
, one of them being accessed from contract A
and the other one from contract B
. In more complex contract systems this condition could go unnoticed and subsequently lead to security issues.
Shadowing state variables can also occur within a single contract when there are multiple definitions on the contract and function level.
1
Example
pragma solidity 0.4.24;
contract ShadowingInFunctions {
uint n = 2;
uint x = 3;
function test1() constant returns (uint n) {
return n; // Will return 0
}
function test2() constant returns (uint n) {
n = 1;
return n; // Will return 1
}
function test3() constant returns (uint x) {
uint n = 4;
return n+x; // Will return 4
}
}
Recommendation
Review storage variable layouts for your contract systems carefully and remove any ambiguities. Always check for compiler warnings as they can flag the issue within a single contract.
Update notes
Could be fixed, will not bring problems.
No way to withdraw contract balance
Tokens and ether can be collected by smart contracts from external addresses. Some exchange and liquidity-add operations could result in the accumulation of leftover ether and tokens.
Recommendation
To remove tokens and ether from the contract, add a withdraw function.
Update notes
Could be fixed, will not bring problems.
Reliance on third-parties
Interaction between smart contracts with third-party protocols like Uniswap and Pancakeswap. The audit’s scope presupposes that third party entities will perform as intended and treats them as if they were black boxes. In the real world, third parties can be hacked and used against you. Additionally, improvements made by third parties may have negative effects, such as higher transaction costs or the deprecation of older routers.
Recommendation
Regularly check third-party dependencies, and when required, reduce severe effects.
Update notes
Could be fixed, will not bring problems.
Initial supply
When the contract is deployed, the contract deployer receives all of the initially created assets. Since the deployer and/or contract owner can distribute tokens without consulting the community, this could be a problem.
Recommendation
Private keys belonging to the employer and/or contract owner should be stored properly. The initial asset allocation procedure should involve consultation with the community.
Update notes
Could be fixed, will not bring problems.
Contract does not use a ReEntrancyGuard
One of the major dangers of calling external contracts is that they can take over the control flow. In the reentrancy attack (a.k.a. recursive call attack), a malicious contract calls back into the calling contract before the first invocation of the function is finished. This may cause the different invocations of the function to interact in undesirable ways.
1
Exploit scenario
function withdrawBalance(){
// send userBalance[msg.sender] Ether to msg.sender
// if mgs.sender is a contract, it will call its fallback function
if( ! (msg.sender.call.value(userBalance[msg.sender])() ) ){
throw;
}
userBalance[msg.sender] = 0;
}
Recommendation
The best practices to avoid Reentrancy weaknesses are: Make sure all internal state changes are performed before the call is executed. This is known as the Checks-Effects-Interactions pattern, or use a reentrancy lock (ie. OpenZeppelin’s ReentrancyGuard.
Update notes
Could be fixed, will not bring problems.
Outdated Compiler Version
Using an outdated compiler version can be problematic especially if there are publicly disclosed bugs and issues that affect the current compiler version.
Recommendation
It is recommended to use a recent version of the Solidity compiler.
Update notes
Could be fixed, will not bring problems.
Unchecked Call Return Value
The return value of a message call is not checked. Execution will resume even if the called contract throws an exception. If the call fails accidentally or an attacker forces the call to fail, this may cause unexpected behaviour in the subsequent program logic.
1
Example
pragma solidity 0.4.25;
contract ReturnValue {
function callchecked(address callee) public {
require(callee.call());
}
function callnotchecked(address callee) public {
callee.call();
}
}
Recommendation
If you choose to use low-level call methods, make sure to handle the possibility that the call will fail by checking the return value.
Update notes
Could be fixed, will not bring problems.
Floating Pragma
Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively.
1
Example
pragma solidity ^0.4.0;
contract PragmaNotLocked {
uint public x = 1;
}
Recommendation
Lock the pragma version and also consider known bugs (https://github.com/ethereum/solidity/releases) for the compiler version that is chosen.
Pragma statements can be allowed to float when a contract is intended for consumption by other developers, as in the case with contracts in a library or EthPM package. Otherwise, the developer would need to manually update the pragma in order to compile locally.
Update notes
Could be fixed, will not bring problems.
Function Default Visibility
Functions that do not have a function visibility type specified are public
by default. This can lead to a vulnerability if a developer forgot to set the visibility and a malicious user is able to make unauthorized or unintended state changes.
1
Example
pragma solidity ^0.4.24;
contract HashForEther {
function withdrawWinnings() {
// Winner if the last 8 hex characters of the address are 0.
require(uint32(msg.sender) == 0);
_sendWinnings();
}
function _sendWinnings() {
msg.sender.transfer(this.balance);
}
}
Recommendation
Functions can be specified as being external
, public
, internal
or private
. It is recommended to make a conscious decision on which visibility type is appropriate for a function. This can dramatically reduce the attack surface of a contract system.
Update notes
Could be fixed, will not bring problems.
Tautology or contradiction
Expressions that are tautologies or contradictions.
1
Example
contract A {
function f(uint x) public {
if (x >= 0) { // bad -- always true
}
}
function g(uint8 y) public returns (bool) {
return (y < 512); // bad!
}
}
x
is a uint256
, so x >= 0
will be always true. y
is a uint8
, so y <512
will be always true.
Recommendation
Fix the incorrect comparison by changing the value type or the comparison.
Update notes
Could be fixed, will not bring problems.
Write after write
Variables that are written but never read and written again.
1
Example
contract Buggy{
function my_func() external initializer{
// ...
a = b;
a = c;
// ..
}
}
`a` is first asigned to `b`, and then to `c`. As a result the first write does nothing.
Recommendation
Fix or remove the writes.
Update notes
Could be fixed, will not bring problems.
Divide before multiply
Solidity integer division might truncate. As a result, performing multiplication before division can sometimes avoid loss of precision.
1
Example
contract A {
function f(uint n) public {
coins = (oldSupply / n) * interest;
}
}
If n
is greater than oldSupply
, coins
will be zero. For example, with oldSupply = 5; n = 10, interest = 2
, coins will be zero.
If (oldSupply * interest / n)
was used, coins
would have been 1
.
In general, it’s usually a good idea to re-arrange arithmetic to perform multiplication before division, unless the limit of a smaller type makes this dangerous.
Recommendation
Consider ordering multiplication before division.
Update notes
Could be fixed, will not bring problems.
Calls inside a loop
Calls inside a loop might lead to a denial-of-service attack.
1
Example
contract CallsInLoop{
address[] destinations;
constructor(address[] newDestinations) public{
destinations = newDestinations;
}
function bad() external{
for (uint i=0; i < destinations.length; i++){
destinations[i].transfer(i);
}
}
}
If one of the destinations has a fallback function that reverts, bad
will always revert.
Recommendation
Favor pull over push strategy for external calls.
Update notes
Could be fixed, will not bring problems.
Missing events arithmetic
Missing events for critical arithmetic parameters.
1
Example
contract C {
modifier onlyOwner {
if (msg.sender != owner) throw;
_;
}
function setBuyPrice(uint256 newBuyPrice) onlyOwner public {
buyPrice = newBuyPrice;
}
function buy() external {
... // buyPrice is used to determine the number of tokens purchased
}
}
updateOwner()
has no event, so it is difficult to track off-chain changes in the buy price.
Recommendation
Emit an event for critical parameter changes.
Update notes
Could be fixed, will not bring problems.
Boolean equality
Detected the comparison to boolean constants.
1
Example
contract A {
function f(bool x) public {
// ...
if (x == true) { // bad!
// ...
}
// ...
}
}
Boolean constants can be used directly and do not need to be compare to true
or false
.
Recommendation
Remove the equality to the boolean constant.
Update notes
Could be fixed, will not bring problems.
Conformance to Solidity naming conventions
Solidity defines a naming convention that should be followed.
1
Could be fixed, will not bring problems.
Costly operations inside a loop
Costly operations inside a loop might waste gas, so optimizations are justified.
1
Recommendation
Use a local variable to hold the loop computation result.
Update notes
Could be fixed, will not bring problems.
Too many digits
Literals with many digits are difficult to read and review.
1
Example
contract MyContract{
uint 1_ether = 10000000000000000000;
}
While 1_ether
looks like 1 ether
, it is 10 ether
. As a result, it’s likely to be used incorrectly.
Recommendation
Use: Ether suffix, Time suffix, or The scientific notation
Update notes
Could be fixed, will not bring problems.
Missing zero address validation
1
Example
contract C {
modifier onlyAdmin {
if (msg.sender != owner) throw;
_;
}
function updateOwner(address newOwner) onlyAdmin external {
owner = newOwner;
}
}
Bob calls updateOwner
without specifying the newOwner
, so Bob loses ownership of the contract.
Recommendation
Check that the address is not zero.
Update notes
Could be fixed, will not bring problems.
Code With No Effects
In Solidity, it’s possible to write code that does not produce the intended effects. Currently, the solidity compiler will not return a warning for effect-free code. This can lead to the introduction of “dead” code that does not properly performing an intended action.
For example, it’s easy to miss the trailing parentheses in msg.sender.call.value(address(this).balance)("");
, which could lead to a function proceeding without transferring funds to msg.sender
. Although, this should be avoided by checking the return value of the call.
1
Error Example
pragma solidity ^0.5.0;
contract DepositBox {
mapping(address => uint) balance;
// Accept deposit
function deposit(uint amount) public payable {
require(msg.value == amount, 'incorrect amount');
// Should update user balance
balance[msg.sender] == amount;
}
}
Instead of balance[msg.sender] == amount
, the contract should do balance[msg.sender] = amount
. Even though the compiler won’t detect this as an error, this code won’t do anything.
Recommendation
It’s important to carefully ensure that your contract works as intended. Write unit tests to verify correct behaviour of the code.
Update notes
No notes added
Could be fixed, will not bring problems.
Typographical Error
A typographical error can occur for example when the intent of a defined operation is to sum a number to a variable (+=) but it has accidentally been defined in a wrong way (=+), introducing a typo which happens to be a valid operator. Instead of calculating the sum it initializes the variable again.
1
Error Example
pragma solidity ^0.4.25;
contract TypoOneCommand {
uint numberOne = 1;
function alwaysOne() public {
numberOne =+ 1;
}
}
numberOne
, will always be 1 because the operator should be += instead of =+, which is a valid operator, but will just initialize it again to 1.
Recommendation
The weakness can be avoided by performing pre-condition checks on any math operation or using a vetted library for arithmetic calculations such as SafeMath developed by OpenZeppelin.
Update notes
Could be fixed, will not bring problems.
Authorization through tx.origin
tx.origin
is a global variable in Solidity which returns the address of the account that sent the transaction. Using the variable for authorization could make a contract vulnerable if an authorized account calls into a malicious contract. A call could be made to the vulnerable contract that passes the authorization check since tx.origin
returns the original sender of the transaction which in this case is the authorized account.
1
Error Example
pragma solidity 0.4.24;
contract MyContract {
address owner;
function MyContract() public {
owner = msg.sender;
}
function sendTo(address receiver, uint amount) public {
require(tx.origin == owner);
receiver.transfer(amount);
}
}
Recommendation
tx.origin
should not be used for authorization. Use msg.sender
instead.
Update notes
Could be fixed, will not bring problems.
Delegatecall to Untrusted Callee (Proxy)
There exists a special variant of a message call, named delegatecall
which is identical to a message call apart from the fact that the code at the target address is executed in the context of the calling contract and msg.sender
and msg.value
do not change their values. This allows a smart contract to dynamically load code from a different address at runtime. Storage, current address and balance still refer to the calling contract.
Calling into untrusted contracts is very dangerous, as the code at the target address can change any storage values of the caller and has full control over the caller’s balance.
1
Error Example
pragma solidity ^0.4.24;
contract Proxy {
address owner;
constructor() public {
owner = msg.sender;
}
function forward(address callee, bytes _data) public {
require(callee.delegatecall(_data));
}
}
Recommendation
Use delegatecall
with caution and make sure to never call into untrusted contracts. If the target address is derived from user input ensure to check it against a whitelist of trusted contracts.
Update notes
Could be fixed, will not bring problems.
Use of Deprecated Solidity Functions
Several functions and operators in Solidity are deprecated. Using them leads to reduced code quality. With new major versions of the Solidity compiler, deprecated functions and operators may result in side effects and compile errors.
1
Recommendation
Solidity provides alternatives to the deprecated constructions. Most of them are aliases, thus replacing old constructions will not break current behavior. For example, sha3
can be replaced with keccak256
.
Deprecated | Alternative |
---|---|
suicide(address) | selfdestruct(address) |
block.blockhash(uint) | blockhash(uint) |
sha3(...) | keccak256(...) |
callcode(...) | delegatecall(...) |
throw | revert() |
msg.gas | gasleft |
constant | view |
var | corresponding type name |
Update notes
Could be fixed, will not bring problems.
Integer Overflow and Underflow
An overflow/underflow happens when an arithmetic operation reaches the maximum or minimum size of a type. For instance if a number is stored in the uint8 type, it means that the number is stored in a 8 bits unsigned number ranging from 0 to 2^8-1. In computer programming, an integer overflow occurs when an arithmetic operation attempts to create a numeric value that is outside of the range that can be represented with a given number of bits – either larger than the maximum or lower than the minimum representable value.
1
Error Example
//Single transaction overflow
pragma solidity ^0.4.11;
contract IntegerOverflowMappingSym1 {
mapping(uint256 => uint256) map;
function init(uint256 k, uint256 v) public {
map[k] -= v;
}
}
Recommendation
It is recommended to use vetted safe math libraries for arithmetic operations consistently throughout the smart contract system.
Update notes
Could be fixed, will not bring problems.
Dangerous strict equalities
Use of strict equalities that can be easily manipulated by an attacker.
1
Exploit Example
contract Crowdsale{
function fund_reached() public returns(bool){
return this.balance == 100 ether;
}
Crowdsale
relies on fund_reached
to know when to stop the sale of tokens. Crowdsale
reaches 100 Ether. Bob sends 0.1 Ether. As a result, fund_reached
is always false and the crowdsale
never ends.
Recommendation
Don’t use strict equality to determine if an account has enough Ether or tokens.
Update notes
This audit report has been prepared by Coinsult’s experts at the request of the client. In this audit, the results of the static analysis and the manual code review will be presented. The purpose of the audit is to see if the functions work as intended, and to identify potential security issues within the smart contract.
The information in this report should be used to understand the risks associated with the smart contract. This report can be used as a guide for the development team on how the contract could possibly be improved by remediating the issues that were identified.
Coinsult is not responsible if a project turns out to be a scam, rug-pull or honeypot. We only provide a detailed analysis for your own research.
Coinsult is not responsible for any financial losses. Nothing in this contract audit is financial advice, please do your own research.
The information provided in this audit is for informational purposes only and should not be considered investment advice. Coinsult does not endorse, recommend, support or suggest to invest in any project.
Coinsult can not be held responsible for when a project turns out to be a rug-pull, honeypot or scam.
Share this audit report