0% found this document useful (0 votes)
28 views22 pages

BitChain Audit Report 2023-06-13

The document provides a security audit summary for the BitChain project, detailing its vulnerabilities, including 1 high-severity, 3 medium-severity, and 2 low-severity issues, totaling 13 identified concerns. Notable findings include risks related to front-running in token swaps, potential failures in token transfers, and centralization risks due to a privileged owner role. Recommendations for addressing these issues are also outlined, emphasizing the need for improved security measures and code consistency.

Uploaded by

Adam Burks
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
28 views22 pages

BitChain Audit Report 2023-06-13

The document provides a security audit summary for the BitChain project, detailing its vulnerabilities, including 1 high-severity, 3 medium-severity, and 2 low-severity issues, totaling 13 identified concerns. Notable findings include risks related to front-running in token swaps, potential failures in token transfers, and centralization risks due to a privileged owner role. Recommendations for addressing these issues are also outlined, emphasizing the need for improved security measures and code consistency.

Uploaded by

Adam Burks
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 22

Overview

Project Summary
● Name: BitChain
● Address: 0xc730e6eD9586351B802498d27a18F41E78f759Fb
● Platform: BNB Smart Chain
● Language: Solidity
● Audit Range: See Appendix - 1

Project Dashboard
Application Summary
Name BitChain

Version v1

Type Solidity

Dates June 13 2023

Logs June 13 2023

Vulnerability Summary
Total High-Severity issues 1

Total Medium-Severity issues 3

Total Low-Severity issues 2

Total informational issues 7

Total 13

Contact
E-mail: [email protected]

1
Risk Level Description
The issue puts a large number of users’ sensitive
information at risk, or is reasonably likely to lead to
High Risk
catastrophic impact for clients’ reputations or serious
financial implications for clients and users.

The issue puts a subset of users’ sensitive


information at risk, would be detrimental to the client’s
Medium Risk
reputation if exploited, or is reasonably likely to lead
to a moderate financial impact.

The risk is relatively small and could not be exploited


on a recurring basis, or is a risk that the client has
Low Risk
indicated is low impact in view of the client’s business
circumstances.

The issue does not pose an immediate risk, but is


Informational relevant to security best practices or defense in
depth.

2
Content

Introduction 4
1.1 About SALUS 4
1.2 Audit Breakdown 4
1.3 Disclaimer 4
Findings 5
2.1 Summary of Findings 5
2.2 Notable Findings 6
1. Swapping from Uniswap V2 can be front-runned due to lack of slippage protection 6
2. Token transfer may fail when totalTax is 0 and isSwapBackEnabled is true 7
3. Lack of expiration timestamp check 9
4. Centralization risk 10
5. Inconsistencies in setting of swapTokensAtAmount 11
6. Unchecked return value from sendBNB 12
2.3 Informational Findings 13
7. Different condition checks before calling swapBack() in different functions 13
8. Redundant code 14
9. Variable denominator can be marked as constant instead of immutable 15
10. Missing isContract check for pair parameter 16
11. Inconsistency tax between code and documentation 17
12. Missing two-step transfer ownership pattern 19
13. Use call instead of transfer for native tokens transfer 20
Appendix 21
Appendix 1 - Files in Scope 21

3
Introduction
1.1 About SALUS
At Salus Security, we are in the business of trust.

We are dedicated to tackling the toughest security challenges facing the industry today. By
building foundational trust in technology and infrastructure through security, we help clients
to lead their respective industries and unlock their full Web3 potential.

Our team of security experts employ industry-leading proof-of-concept (PoC) methodology


for demonstrating smart contract vulnerabilities, coupled with advanced red teaming
capabilities and a stereoscopic vulnerability detection service, to deliver comprehensive
security assessments that allow clients to stay ahead of the curve.

In addition to smart contract audits and red teaming, our Rapid Detection Service for smart
contracts aims to make security accessible to all. This high calibre, yet cost-efficient, security
tool has been designed to support a wide range of business needs including investment due
diligence, security and code quality assessments, and code optimisation.

We are reachable on Telegram (https://t.me/salusec), Twitter (https://twitter.com/salus_sec),


or Email ([email protected]).

1.2 Audit Breakdown


The objective was to evaluate the repository for security-related issues, code quality, and
adherence to specifications and best practices. Possible issues we looked for included (but
are not limited to):
● Risky external calls
● Integer overflow/underflow
● Transaction-ordering dependence
● Timestamp dependence
● Access control
● Call stack limits and mishandled exceptions
● Number rounding errors
● Centralization of power
● Logical oversights and denial of service
● Business logic specification
● Code clones, functionality duplication

1.3 Disclaimer
Note that this security audit is not designed to replace functional tests required before any
software release and does not give any warranties on finding all possible security issues with
the given smart contract(s) or blockchain software, i.e., the evaluation result does not
guarantee the nonexistence of any further findings of security issues.

4
Findings
2.1 Summary of Findings

ID Title Severity Category Status

1 Swapping from Uniswap V2 can be front-runned High Business Logic Pending


due to lack of slippage protection
2 Token transfer may fail when totalTax is 0 and Medium Business Logic Pending
isSwapBackEnabled is true
3 Lack of expiration timestamp check Medium Business Logic Pending

4 Centralization risk Medium Centralization Pending

5 Inconsistencies in setting of Low Business Logic Pending


swapTokensAtAmount
6 Unchecked return value from sendBNB Low Data Validation Pending

7 Different condition checks before calling Informational Data Validation Pending


swapBack() in different functions
8 Redundant code Informational Redundancy Pending

9 Variable denominator can be marked as constant Informational Gas Pending


instead of immutable Optimization
10 Missing isContract check for pair parameter Informational Data Validation Pending

11 Inconsistency tax between code and Informational Configuration Pending


documentation
12 Missing two-step transfer ownership pattern Informational Business Logic Pending

13 Use call instead of transfer for native tokens Informational Business Logic Pending
transfer

5
2.2 Notable Findings
Significant flaws that impact system confidentiality, integrity, or availability are listed below.

1. Swapping from Uniswap V2 can be front-runned due to lack of


slippage protection

Severity: High Category: Business Logic

Target:
- BitChain.sol

Description
Swapping BitChain(BIT) tokens in swapBack function through Uniswap V2 router can be
front-runned resulting in loss of received native tokens for the protocol. The vulnerability
arises because the argument amountOutMin in the
swapExactTokensForETHSupportingFeeOnTransferTokens function is set to 0.

BitChain.sol:L1214-L1220
uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
swapBackAmount,
0,
path,
address(this),
block.timestamp
)
This means that any amount is acceptable. This can result in an attacker sandwiching the
transaction to extract profit which will be a loss to the protocol.

Recommendation
Consider setting some value for amountOutMin. This can be calculated from oracles.
Please refrain from using spot price for calculating the amountOutMin because spot prices
can also be manipulated by the attacker.

6
2. Token transfer may fail when totalTax is 0 and
isSwapBackEnabled is true

Severity: Medium Category: Business Logic

Target:
- BitChain.sol

Description
The swapBack function is called during token transfers when contractTokenBalance
reaches the threshold and isSwapBackEnabled is true.

BitChain.sol:L1153-L1164
uint256 contractTokenBalance = balanceOf(address(this));

bool canSwap = contractTokenBalance >= swapTokensAtAmount;

if (
canSwap &&
!swapping &&
!_isAutomatedMarketMakerPair[from] &&
isSwapBackEnabled
) {
swapBack();
}
In the swapBack function, the native currency obtained from the
swapExactTokensForETHSupportingFeeOnTransferTokens function will be distributed
according to the respective proportions of marketingTokenAmount and teamTokenAmount.
The marketingTokenAmount and teamTokenAmount are increased only when transfers
occur between addresses that are not excluded from fees.

BitChain.sol:L1202-L1240
function swapBack() internal inSwap {
...

uint256 contractTokenBalance = balanceOf(address(this));

uint256 totalTax = marketingTokenAmount + teamTokenAmount;

uint256 swapBackAmount = contractTokenBalance;

try
uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
swapBackAmount,
0,
path,
address(this),
block.timestamp
)
{} catch {
return;
}

uint256 newBalance = address(this).balance;

7
uint256 marketingBNB = (newBalance * marketingTokenAmount) / totalTax;
uint256 teamBNB = newBalance - marketingBNB;

...
}
As the BitChain contract itself is exempt from fees, malicious users can transfer tokens
directly to the contract and let its balance reach the threshold to trigger the swapBack
function. Since the marketingTokenAmount and teamTokenAmount are not increased, the
totalTax will be 0, causing the token transfer to fail.

Recommendation
Consider adding a check for totalTax. Additionally, as the native currency obtained from the
swap needs to be distributed, consider defining a default allocation ratio for marketing and
team when totalTax is 0.

8
3. Lack of expiration timestamp check

Severity: Medium Category: Business Logic

Target:
- BitChain.sol

Description
The process of exchanging ERC20 tokens lacks a transaction expiration check, which may
lead to tokens being sold at a price lower than the market price at the moment of a swap.

BitChain.sol:L1214-L1220
uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
swapBackAmount,
0,
path,
address(this),
block.timestamp
)
Setting the deadline parameter to block.timestamp basically disables the transaction
expiration check because the deadline will be set to whatever timestamp the block including
the transaction is minted at. The value of the deadline parameter has to be determined
outside of the transaction.

Recommendation
Consider a reasonable value to the deadline argument. For example, Uniswap sets it to 30
minutes on the Ethereum mainnet and 5 minutes on L2 networks. Also, consider letting the
owner and/or the DAO change the value when on-chain conditions change and may require
a different value.

Also due to the fact that swapBack() is implemented in _transfer(), it will be triggered almost
any time and it is not a user deal to input the deadline. Consider removing swapBack() from
_transfer() and keeping manualSwapBack() or implementing another architect decision.

9
4. Centralization risk

Severity: Medium Category: Centralization

Target:
- BitChain.sol

Description
There is a privileged owner role in the BitChain contract.

The owner of the BitChain contract can change the tax or wallet configuration and claim
stuck tokens. Should the owner's private key be compromised, an attacker could set
marketingWallet and teamWallet to an address he controls and set all tax configurations to
the maximum. If the privileged accounts are plain EOA accounts, this can be worrisome
and pose a risk to the other users.

Recommendation
Consider transferring the privileged role to multi-sig account.

10
5. Inconsistencies in setting of swapTokensAtAmount

Severity: Low Category: Business Logic

Target:
- BitChain.sol

Description
In the following require statement, totalSupply() / 100_000 will be 0.001% of the total
supply. However, the error message states 0.00001%, which is inconsistent.

BitChain.sol:L1083-L1086
require(
amount >= totalSupply() / 100_000,
"Amount must be equal or greater than 0.00001% of Total Supply"
);

Recommendation
Consider fixing the mismatch between the error message and implementation.

11
6. Unchecked return value from sendBNB

Severity: Low Category: Data Validation

Target:
- BitChain.sol

Description
The return value of sendBNB is not checked. Therefore, if sendBNB fails silently, it may
need an additional transaction to claim stuck tokens for the owner when the issue is
noticed.

BitChain.sol:L1230-L1236
if (marketingBNB > 0) {
sendBNB(marketingWallet, marketingBNB);
}

if (teamBNB > 0) {
sendBNB(teamWallet, teamBNB);
}

Recommendation
Consider checking the return value of sendBNB and emitting an event if it fails. This will
help the team detect failed sendBNB transactions.

12
2.3 Informational Findings

7. Different condition checks before calling swapBack() in


different functions

Severity: Informational Category: Data Validation

Target:
- BitChain.sol

Description
In _transfer function, there are some condition checks before calling the swapBack
function.

BitChain.sol:L1157-L1164
if (
canSwap &&
!swapping &&
!_isAutomatedMarketMakerPair[from] &&
isSwapBackEnabled
) {
swapBack();
}
While in the manualSwapBack function, only the balance of the contract is checked. So if
the owner set isSwapBackEnabled to false, it is still possible to call swapBack() through
manualSwapBack().

BitChain.sol:L1256-L1262
function manualSwapBack() external {
uint256 contractTokenBalance = balanceOf(address(this));

require(contractTokenBalance > 0, "Cant Swap Back 0 Token!");

swapBack();
}

Recommendation
Consider adding appropriate checks to the manualSwapBack function to improve code
consistency

13
8. Redundant code

Severity: Informational Category: Redundancy

Target:
- BitChain.sol

Description
BitChain.sol:L1207-L1211
uint256 contractTokenBalance = balanceOf(address(this));
...
uint256 swapBackAmount = contractTokenBalance;
There is a redundant local variable contractTokenBalance in the swapBack function
because it is not used after being assigned to swapBackAmount.

Recommendation
Consider using uint256 swapBackAmount = balanceOf(address(this));.

14
9. Variable denominator can be marked as constant instead of
immutable

Severity: Informational Category: Gas Optimization

Target:
- BitChain.sol

Description
BitChain.sol:L874
uint256 public immutable denominator;
The variable denominator is marked as immutable instead of constant. It is set to 10000 in
the constructor. Since it's assigned a literal number, it can be changed to constant and be
set at compile time.

Recommendation
Consider changing the denominator from immutable to constant and removing the
assignment in the constructor, i.e. uint256 public constant denominator = 10_000.

15
10. Missing isContract check for pair parameter

Severity: Informational Category: Data Validation

Target:
- BitChain.sol

Description
When setting the configuration of an address type, such as marketingWallet and
teamWallet, an isContract check is included. However, the isContract check is missing for
the pair parameter.

BitChain.sol:L1100-L1115
function setAutomatedMarketMakerPair(
address pair,
bool status
) external onlyOwner {
require(
pair != uniswapV2Pair,
"Current Pair address cannot be modified"
);
require(
_isAutomatedMarketMakerPair[pair] != status,
"Pair address is already the value of 'status'"
);
_isAutomatedMarketMakerPair[pair] = status;

emit UpdateAutomatedMarketMakerPair(pair, status);


}

Recommendation
Consider adding a isContract check for the pair parameter to improve code consistency.

16
11. Inconsistency tax between code and documentation

Severity: Informational Category: Configuration

Target:
- BitChain.sol

Description
The tokenomics document on the website states that the buy and sell tax should be 6%, but
the code only defines it as 5% in the constructor.

BitChain.sol:L915-L921
marketingTaxBuy = 400;
marketingTaxSell = 400;
marketingTaxTransfer = 400;

teamTaxBuy = 100;
teamTaxSell = 100;
teamTaxTransfer = 100;
For each transfer, when takeFee is true, a fee will be deducted according to marketingTax
and teamTax, which is 5% for current configuration.

BitChain.sol:L1140-L1200
function _transfer(
address from,
address to,
uint256 amount
) internal override {
...

if (takeFee) {
uint256 tempMarketingAmount;
uint256 tempTeamAmount;

if (_isAutomatedMarketMakerPair[from]) {
tempMarketingAmount = (amount * marketingTaxBuy) / denominator;
tempTeamAmount = (amount * teamTaxBuy) / denominator;
} else if (_isAutomatedMarketMakerPair[to]) {
tempMarketingAmount = (amount * marketingTaxSell) / denominator;
tempTeamAmount = (amount * teamTaxSell) / denominator;
} else {
...
}
marketingTokenAmount += tempMarketingAmount;
teamTokenAmount += tempTeamAmount;

uint256 fees = tempMarketingAmount + tempTeamAmount;

if (fees > 0) {
amount -= fees;
super._transfer(from, address(this), fees);
}
}

...
}

17
Recommendation
Consider fixing the mismatch between the configuration and documentation.

18
12. Missing two-step transfer ownership pattern

Severity: Informational Category: Business logic

Target:
- BitChain.sol

Description
The BitChain contract inherits from the Ownable contract. This contract does not implement
a two-step process for transferring ownership. Thus, ownership of the contract can easily be
lost when making a mistake in transferring ownership.

Recommendation
Consider using the Ownable2Step contract from OpenZeppelin instead.

19
13. Use call instead of transfer for native tokens transfer

Severity: Informational Category: Business logic

Target:
- BitChain.sol

Description
The transfer function is not recommended for sending native tokens due to its 2300 gas unit
limit which may not work with smart contract wallets or multi-sig. Instead, call can be used to
circumvent the gas limit.

BitChain.sol:L971-L974
if (token == address(0x0)) {
payable(msg.sender).transfer(address(this).balance);
return;
}

Recommendation
Consider using call instead of transfer for sending native token.

Example:

payable(msg.sender).call{value: address(this).balance}("");

20
Appendix
Appendix 1 - Files in Scope
This audit covered the following file from address
0xc730e6eD9586351B802498d27a18F41E78f759Fb:

File SHA-1 hash

BitChain.sol 95f75d7f94d62cf933dd97c8b67063fc5f8ea0c9

21

You might also like