Halborn Logo

icon

Gorples Coin - Entangle Labs


Prepared by:

Halborn Logo

HALBORN

Last Updated 07/04/2024

Date of Engagement by: May 16th, 2024 - May 23rd, 2024

Summary

100% of all REPORTED Findings have been addressed

All findings

14

Critical

0

High

0

Medium

2

Low

4

Informational

8


1. Introduction

The Entangle team engaged Halborn to conduct a security assessment on their smart contracts beginning on 05-16-2024 and ending on 05-23-2024. The security assessment was scoped to the smart contracts provided in the https://github.com/Entangle-Protocol/gorples-evm/commit/29cfaa7512c9d4103cd04d173a95ec800ca2f7da GitHub repository. Commit hashes and further details can be found in the Scope section of this report. The Gorples Coin codebase in scope consists of a deflationary token with equal cross-chain distribution.

2. Assessment Summary

Halborn was provided 1 week for the engagement and assigned 1 full-time security engineer to review the security of the smart contracts in scope. The engineer is a blockchain and smart contract security expert with advanced penetration testing and smart contract hacking skills, and deep knowledge of multiple blockchain protocols.

The purpose of the assessment is to:

    • Identify potential security issues within the smart contracts.

    • Ensure that smart contract functionality operates as intended.

In summary, Halborn identified some improvements to reduce the likelihood and impact of risks, which were accepted and addressed by the Entangle Team. The main identified issues were:

    • Missing verification of chain of origin while redeeming. (RISK ACCEPTED)

    • Inconsistent burn application to sender due to recipient exclusion. (RISK ACCEPTED)

    • Missing pause mechanism prevents intended halting of core bridging functionality. (SOLVED)

3. Test Approach and Methodology

Halborn performed a combination of manual review of the code and automated security testing to balance efficiency, timeliness, practicality, and accuracy in regard to the scope of this assessment. While manual testing is recommended to uncover flaws in logic, process, and implementation; automated testing techniques help enhance coverage of smart contracts and can quickly identify items that do not follow security best practices.

The following phases and associated tools were used throughout the term of the assessment:

    • Research into architecture, purpose and use of the platform.

    • Smart contract manual code review and walkthrough to identify any logic issue.

    • Thorough assessment of safety and usage of critical Solidity variables and functions in scope that could led to arithmetic related vulnerabilities.

    • Local testing with custom scripts (Foundry).

    • Fork testing against main networks (Foundry).

    • Static analysis of security for scoped contract, and imported functions (Slither).

3.1 Out-of-scope

    • External libraries and financial-related attacks.

    • New features/implementations after/within the remediation commit IDs.

    • Changes that occur outside of the scope of PRs.

    • The following files, while they affect the in-scope code, were not specifically reviewed as part of the scope and are assumed to not contain any issues:

      • contracts/ChestManager.sol

      • @entangle_protocol/oracle-sdk/contracts/lib/PhotonFunctionSelectorLib.sol

4. RISK METHODOLOGY

Every vulnerability and issue observed by Halborn is ranked based on two sets of Metrics and a Severity Coefficient. This system is inspired by the industry standard Common Vulnerability Scoring System.
The two Metric sets are: Exploitability and Impact. Exploitability captures the ease and technical means by which vulnerabilities can be exploited and Impact describes the consequences of a successful exploit.
The Severity Coefficients is designed to further refine the accuracy of the ranking with two factors: Reversibility and Scope. These capture the impact of the vulnerability on the environment as well as the number of users and smart contracts affected.
The final score is a value between 0-10 rounded up to 1 decimal place and 10 corresponding to the highest security risk. This provides an objective and accurate rating of the severity of security vulnerabilities in smart contracts.
The system is designed to assist in identifying and prioritizing vulnerabilities based on their level of risk to address the most critical issues in a timely manner.

4.1 EXPLOITABILITY

Attack Origin (AO):
Captures whether the attack requires compromising a specific account.
Attack Cost (AC):
Captures the cost of exploiting the vulnerability incurred by the attacker relative to sending a single transaction on the relevant blockchain. Includes but is not limited to financial and computational cost.
Attack Complexity (AX):
Describes the conditions beyond the attacker’s control that must exist in order to exploit the vulnerability. Includes but is not limited to macro situation, available third-party liquidity and regulatory challenges.
Metrics:
EXPLOITABILIY METRIC (mem_e)METRIC VALUENUMERICAL VALUE
Attack Origin (AO)Arbitrary (AO:A)
Specific (AO:S)
1
0.2
Attack Cost (AC)Low (AC:L)
Medium (AC:M)
High (AC:H)
1
0.67
0.33
Attack Complexity (AX)Low (AX:L)
Medium (AX:M)
High (AX:H)
1
0.67
0.33
Exploitability EE is calculated using the following formula:

E=meE = \prod m_e

4.2 IMPACT

Confidentiality (C):
Measures the impact to the confidentiality of the information resources managed by the contract due to a successfully exploited vulnerability. Confidentiality refers to limiting access to authorized users only.
Integrity (I):
Measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of data stored and/or processed on-chain. Integrity impact directly affecting Deposit or Yield records is excluded.
Availability (A):
Measures the impact to the availability of the impacted component resulting from a successfully exploited vulnerability. This metric refers to smart contract features and functionality, not state. Availability impact directly affecting Deposit or Yield is excluded.
Deposit (D):
Measures the impact to the deposits made to the contract by either users or owners.
Yield (Y):
Measures the impact to the yield generated by the contract for either users or owners.
Metrics:
IMPACT METRIC (mIm_I)METRIC VALUENUMERICAL VALUE
Confidentiality (C)None (I:N)
Low (I:L)
Medium (I:M)
High (I:H)
Critical (I:C)
0
0.25
0.5
0.75
1
Integrity (I)None (I:N)
Low (I:L)
Medium (I:M)
High (I:H)
Critical (I:C)
0
0.25
0.5
0.75
1
Availability (A)None (A:N)
Low (A:L)
Medium (A:M)
High (A:H)
Critical (A:C)
0
0.25
0.5
0.75
1
Deposit (D)None (D:N)
Low (D:L)
Medium (D:M)
High (D:H)
Critical (D:C)
0
0.25
0.5
0.75
1
Yield (Y)None (Y:N)
Low (Y:L)
Medium (Y:M)
High (Y:H)
Critical (Y:C)
0
0.25
0.5
0.75
1
Impact II is calculated using the following formula:

I=max(mI)+mImax(mI)4I = max(m_I) + \frac{\sum{m_I} - max(m_I)}{4}

4.3 SEVERITY COEFFICIENT

Reversibility (R):
Describes the share of the exploited vulnerability effects that can be reversed. For upgradeable contracts, assume the contract private key is available.
Scope (S):
Captures whether a vulnerability in one vulnerable contract impacts resources in other contracts.
Metrics:
SEVERITY COEFFICIENT (CC)COEFFICIENT VALUENUMERICAL VALUE
Reversibility (rr)None (R:N)
Partial (R:P)
Full (R:F)
1
0.5
0.25
Scope (ss)Changed (S:C)
Unchanged (S:U)
1.25
1
Severity Coefficient CC is obtained by the following product:

C=rsC = rs

The Vulnerability Severity Score SS is obtained by:

S=min(10,EIC10)S = min(10, EIC * 10)

The score is rounded up to 1 decimal places.
SeverityScore Value Range
Critical9 - 10
High7 - 8.9
Medium4.5 - 6.9
Low2 - 4.4
Informational0 - 1.9

5. SCOPE

Files and Repository
(a) Repository: gorples-evm
(b) Assessed Commit ID: 29cfaa7
(c) Items in scope:
  • contracts/token/GorplesCoin.sol
Out-of-Scope: contracts/interfaces/IBabburuzu.sol, contracts/interfaces/IBorpaFactory.sol, contracts/interfaces/IBorpaMaster.sol, contracts/interfaces/IBorpaPair.sol, contracts/interfaces/IBorpaRouter.sol, contracts/interfaces/IBorpaToken.sol, contracts/interfaces/ICDTFactory.sol, contracts/interfaces/IChestManager.sol, contracts/interfaces/INFTHandler.sol, contracts/interfaces/INFTPool.sol, contracts/interfaces/INFTPoolFactory.sol, contracts/interfaces/ITrillionRouter.sol, contracts/interfaces/IUniswapV2Router01.sol, contracts/interfaces/IXBorpaToken.sol, contracts/interfaces/IYieldBooster.sol, contracts/test/ChestManager_Test.sol, contracts/test/xBorpaToken_Test.sol, contracts/token/BorpaToken.sol, contracts/token_mocks/BorpaToken_Test.sol, contracts/token_mocks/BSC_USDT.sol, contracts/token_mocks/BSC_WETH.sol, contracts/token_mocks/ETH_USDT.sol, contracts/token_mocks/ETH_WETH.sol, contracts/token_mocks/MOCKWETH.sol, contracts/token_mocks/USDC.sol, contracts/Babburuzu.sol, contracts/BorpaGateway.sol, contracts/ChestManager.sol, contracts/MasterBorpa.sol, contracts/NFTPool.sol, contracts/NFTPoolFactory.sol, contracts/xBorpaToken.sol, contracts/YieldBooster.sol
Remediation Commit ID:
Out-of-Scope: New features/implementations after the remediation commit IDs.

6. Assessment Summary & Findings Overview

Critical

0

High

0

Medium

2

Low

4

Informational

8

Security analysisRisk levelRemediation Date
Missing verification of chain of origin while redeemingMediumRisk Accepted
Inconsistent burn application to sender due to recipient exclusionMediumRisk Accepted
Minting and burning functions available during contract paused stateLowRisk Accepted
Missing pause mechanism prevents intended halting of core bridging functionalityLowSolved - 05/23/2024
Risk of EVM version incompatibility across chainsLowPartially Solved - 06/23/2024
Replay attack due to unprotected TX hash for redeemsLowRisk Accepted
Unoptimized loopsInformationalSolved - 05/23/2024
Missing events in setter functionsInformationalAcknowledged
Missing state variables visibility modifiersInformationalSolved - 05/23/2024
Missing error descriptionInformationalAcknowledged
Floating pragmaInformationalSolved - 05/23/2024
Unfollowed naming conventionsInformationalSolved - 05/23/2024
Incomplete NATSPEC documentationInformationalSolved - 05/23/2024
Public functions not called within the contract can be made externalInformationalSolved - 05/23/2024

7. Findings & Tech Details

7.1 Missing verification of chain of origin while redeeming

// Medium

Description

In the redeem() function, the _fromChain parameter decoded from the b calldata parameter is not validated for correctness. This function is designed to facilitate cross-chain token transfers, but this lack of validation could lead to the completion of a bridging interaction with an incorrect or incompatible chain id.

Proof of Concept

In the following scenario, an arbitrary and invalid chain id is used to execute the redeem() function:

function setupContract() public {
  vm.startPrank(ADMIN);
  borpaToken.setChestManager(address(chestManager));
  borpaToken.setFeeCollector(feeCollector);
  borpaToken.setEndPoint(address(endpoint));
  borpaToken.setBridgeRouterAddress(BRIDGE_ROUTER);
  borpaToken.setBorpaMaster(BORPA_MASTER);
  uint256[] memory chainIds = new uint256[](1);
  uint256[] memory minAmounts = new uint256[](1);
  chainIds[0] = _eobChainId;
  minAmounts[0] = 1;
  borpaToken.setMinBridgeAmount(chainIds, minAmounts);
  vm.stopPrank();
}

function test_redeemInvalidFromChain(uint amount, uint fee) public {
  setupContract();
  vm.startPrank(address(endpoint));
  bytes32 testBytes = keccak256("bytes32Value");
  bytes memory recipientInBytes = abi.encode(alice);

  amount = bound(amount, 1, type(uint128).max);
  fee = bound(fee, 0, amount);
  uint amountMinusFee = amount - fee;

  uint arbitraryInvalidFromChain = 1234567890;

  bytes memory parameters = abi.encode(recipientInBytes, amount, fee, [testBytes, testBytes], 1234567890, testBytes);
  bytes memory b = abi.encode(testBytes, 0, 0, [testBytes, testBytes], parameters);
  borpaToken.redeem(b);

  uint256 expectedAliceBalance = amountMinusFee - ((amountMinusFee * 100) / 10000);
  if (amount < 100) expectedAliceBalance = amountMinusFee;
  assertEq(borpaToken.balanceOf(alice), expectedAliceBalance);

  uint256 expectedfeeCollectorBalance = fee - (fee * 100) / 10000;
  if (fee < 100) expectedfeeCollectorBalance = fee;
  assertEq(borpaToken.balanceOf(feeCollector), expectedfeeCollectorBalance);
}
Arbitrary chain of origin
BVSS
Recommendation

Ensure that the _fromChain parameter is indeed a valid chain id that the protocol supports.

Remediation Plan

RISK ACCEPTED: The Entangle team has made a business decision to accept the risk of this finding and not alter the contracts, stating that:

The "fromChain" parameter is transmitted by the agent transmitter and would fail if chain id is not equal to chain id of network where event is fetched. On source network, active chains are also regulated with the minBridgeAmount parameter, which also is a flag of network support. If destination chain is not supported tx should fail on src chain with min bridge amount is not set.

References
GorplesCoin.sol#L341-L351

7.2 Inconsistent burn application to sender due to recipient exclusion

// Medium

Description

The _update() function in the GorplesCoin contract performs the calculation of tokens to burn prior to updating the balances of the involved addresses.

However, the calculation of the fees to burn is not executed when either of the two addresses involved is excluded from the burn mechanism. This results in the burn not being applied even if only the recipient (to address) is excluded from the burn, while the intention seems to be to burn tokens from the from address.

function _update(address from, address to, uint256 amount) internal override {
if (blacklisted[from] || blacklisted[to]) revert BorpaToken__E11();

    if (!isExcludedFromBurn[from] && !isExcludedFromBurn[to]) {
      uint256 fees = (amount * BURN_PERCENTAGE) / BURN_DENOMINATOR;
      if (fees > 0) {
        amount -= fees;
        totalSystemBurnedAmount = totalSystemBurnedAmount + fees;
        super._update(from, address(0), fees);
      }
    }
    super._update(from, to, amount);
}
Proof of Concept

In the following scenario, the recipients of the minted tokens are excluded from burning and the fee is not calculated for the sender:

function test_recipientExcludedFromBurn(uint _mintAmount, uint _fee, uint256 _burnAmount) public {
  _mintAmount = bound(_mintAmount, 1, type(uint128).max);
  _fee = bound(_fee, 0, _mintAmount);
  setupContract();

  //Exclude alice from burning
  vm.startPrank(ADMIN);
  address[] memory addresses = new address[](2);
  bool[] memory status = new bool[](2);
  addresses[0] = alice;
  addresses[1] = feeCollector;
  status[0] = true;
  status[1] = true;
  borpaToken.setBurnExcluded(addresses, status);
  vm.stopPrank();

  bytes32 testBytes = keccak256("bytes32Value");
  bytes memory recipientInBytes = abi.encode(alice);

  bytes memory parameters = abi.encode(recipientInBytes, _mintAmount, _fee, [testBytes, testBytes], 0, testBytes);
  bytes memory b = abi.encode(testBytes, 0, 0, [testBytes, testBytes], parameters);
  vm.startPrank(address(endpoint));
  borpaToken.redeem(b);

  assertEq(borpaToken.balanceOf(alice), _mintAmount - _fee);
  assertEq(borpaToken.balanceOf(feeCollector), _fee);
}
Fee not calculated from sender when recipient is excluded
BVSS
Recommendation

To ensure that the burn fee is applied correctly based on the from address alone, the logic should be modified to check and exclude fee calculation only if the from address is excluded from the burn mechanism, e.g.,:

if (!isExcludedFromBurn[from]) {
  ...
}

Remediation Plan

RISK ACCEPTED: The Entangle team has made a business decision to accept the risk of this finding and not alter the contracts, stating that:

It was designed to take fee only on the path, where both from and to are NOT excluded. So, if 1 of these addresses is excluded (e.g. one of our smart contracts) - fee will not be taken even if the excluded address is "to". You can imagine approve+deposit situation for this example.

References
GorplesCoin.sol#L383-L402

7.3 Minting and burning functions available during contract paused state

// Low

Description

In the GorplesCoin contract, there are several state-changing functions that remain accessible even when the contract is paused, as they lack the whenNotPaused() modifier.

These functions allow for tokens to be minted, approved, transferred or burned while the contract is paused, which can lead to several issues, such as inconsistent state management, off-chain monitoring discrepancies and potential security risks, as the paused state is typically intended to halt all operations that can modify the contract’s state, providing a safe window to address issues.

The affected functions are:

  • claimMasterRewards()

  • burn()

  • systemBurn()

  • approve()

  • transfer()

  • transferFrom()

BVSS
Recommendation

It is recommended to verify the paused state of the contract across all crucial state-changing functions by applying the whenNotPaused modifier. This ensures that these operations are halted when the contract is paused, maintaining the integrity and security of the contract.

Remediation Plan

RISK ACCEPTED: The Entangle team has made a business decision to accept the risk of this finding and not alter the contracts, stating that:

Pause/unpause functionality was designed specifically for the bridge functions to not have transactions "stuck" in between networks during updates of agent transmitters network of Photon CCM. When bridge is paused we want Borpa ecosystem to continue functioning.

References
GorplesCoin.sol#L172-L188
GorplesCoin.sol#L407-L412
GorplesCoin.sol#L414-L417

7.4 Missing pause mechanism prevents intended halting of core bridging functionality

// Low

Description

The GorplesCoin contract inherits from OpenZeppelin's PausableUpgradeable contract and implements the whenNotPaused modifier in the redeem() and bridge() functions. However, the contract does not include an external method to enable the pause mechanism to temporarily halt the contract's functionality.

A pause mechanism is essential for emergency situations, such as discovering a critical bug or vulnerability, and can prevent further damage to the contract and its users. Without a pause mechanism, the contract may be vulnerable to exploitation or unintended behavior in case of an emergency, and the whenNotPaused modifier will have no effect, as the paused state is unreachable with current implementation.

BVSS
Recommendation

Add an external method to enable the pause mechanism. This method should only be accessible by authorized addresses.

Remediation Plan

SOLVED: The Entangle team has addressed the finding in commit fe8eb2b by following the mentioned recommendation.

Remediation Hash
References
GorplesCoin.sol

7.5 Risk of EVM version incompatibility across chains

// Low

Description

From Solidity versions 0.8.20 through 0.8.24, the default target EVM version is set to Shanghai, which results in the generation of bytecode that includes PUSH0 opcodes. Starting with version 0.8.25, the default EVM version shifts to Cancun, introducing new opcodes for transient storage, TSTORE and TLOAD.

In this aspect, it is crucial to select the appropriate EVM version when it's intended to deploy the contracts on networks other than the Ethereum mainnet, which may not support these opcodes. Failure to do so could lead to unsuccessful contract deployments or transaction execution issues, particularly over cross-chain functionality like the codebase in scope.

BVSS
Recommendation

Make sure to specify the target EVM version when using Solidity versions from 0.8.20 and above if deploying to chains that may not support newly introduced opcodes. Additionally, it is crucial to stay informed about the opcode support of different chains to ensure smooth deployment and compatibility.

Remediation Plan

PARTIALLY SOLVED: The Entangle team has partially solved the issue by setting an explicit Solidity compiler version to, ⁣ but0.8.24 lacks a specific EVM version for compilation and deployment. Further testing is required to ensure cross-chain compatibility.

Remediation Hash
References
GorplesCoin.sol#L2

7.6 Replay attack due to unprotected TX hash for redeems

// Low

Description

In the GorplesCoin contract, the redeem() function is susceptible to a replay attack, where the txHash parameter could potentially be used multiple times to execute the function with the same data.

This txHash parameter, as decoded from the b calldata, is used as part of the bridging transaction confirmation without any mechanism to ensure its uniqueness or to prevent its reuse. As a result, the contract may be susceptible to a replay attack, where the same txHash could potentially be used to execute the redeem() function multiple times, leading to unauthorized or unintended transactions.

function redeem(bytes calldata b) external onlyRole(ENDPOINT) whenNotPaused {
    (, , , , bytes memory params) = abi.decode(b, (bytes32, uint256, uint256, bytes32[2], bytes));
    (bytes memory _to, uint256 _amount, uint256 _fee, bytes32[2] memory _txhash, uint256 _fromChain, bytes32 _marker) = abi.decode(
        params,
        (bytes, uint256, uint256, bytes32[2], uint256, bytes32)
    );
    address to = abi.decode(_to, (address));
    _mint(to, _amount - _fee);
    _mint(feeCollector, _fee);
    emit BridgeDone(to, _amount, _txhash, _fromChain, _marker);
}

It is worth mentioning that the ENDPOINT contract that executes the redeem() function will verify if the hash of the complete OperationData has been executed previously, as referenced in the following out-of-scope contract: https://github.com/Entangle-Protocol/photon-cross-chain-messaging/blob/develop/contracts/EndPoint.sol#L421.

Proof of Concept

In the following scenario, the same txHash is used twice to execute the same function call:

function test_redeemTxReplayAttack(uint amount, uint fee) public {
  setupContract();
  vm.startPrank(address(endpoint));
  bytes32 testBytes = keccak256("bytes32Value");
  bytes memory recipientInBytes = abi.encode(alice);

  amount = bound(amount, 1, type(uint128).max);
  fee = bound(fee, 0, amount);
  uint amountMinusFee = amount - fee;

  bytes memory parameters = abi.encode(recipientInBytes, amount, fee, [testBytes, testBytes], 0, testBytes);
  bytes memory b = abi.encode(testBytes, 0, 0, [testBytes, testBytes], parameters);

  borpaToken.redeem(b);
  borpaToken.redeem(b);
}
Duplicated TX hash
BVSS
Recommendation

Introduce additional validation in the redeem() function to verify that each txHash has not been used in previous transactions.

Remediation Plan

RISK ACCEPTED: The Entangle team has made a business decision to accept the risk of this finding and not alter the contracts, stating that:

Protected by MSC (Master Smart Contract in Photon CCM) and EndPoint contract, which calls Borpa on destination chain.

References
GorplesCoin.sol#L341-L351

7.7 Unoptimized loops

// Informational

Description

Throughout the GorplesCoin contract, there are several instances of unoptimized for loop declarations that may incur in higher gas costs than necessary. The affected functions are:

  • setMinBridgeAmount()

  • setBlacklisted()

  • setBurnExcluded()

  • pendingEmission()


Score
Recommendation

Optimize the for loop declarations to reduce gas costs. Best practices include: the non-redundant initialization of the iterator with a default value (i=0 instead of simply i), the use of the pre-increment operator (++i), and the unchecked keyword for incrementing by 1. For example:

for (uint256 i; i < loopAmount;) {
  // code logic
  unchecked { ++i; }
}

Additionally, it can be considered to remove unneeded declaration of local variables inside loops. Particularly, in the setMinBridgeAmount() function:

uint256 key = chainIds[i];
uint256 value = minAmounts[i];
minBridgeAmounts[key] = value;

Here, the key and value variables can be omitted if the intention is to use these values only once:

minBridgeAmounts[chainIds[i]] = minAmounts[i];

Remediation Plan

SOLVED: The Entangle team has addressed the finding in commit c116124 by following the mentioned recommendation.

Remediation Hash
References
GorplesCoin.sol#L223-L229
GorplesCoin.sol#L251-L255
GorplesCoin.sol#L269-L271
GorplesCoin.sol#L285-L287

7.8 Missing events in setter functions

// Informational

Description

Throughout the GorplesCoin contract, there are several instances where administrative functions change contract state by modifying core state variables. However, these changes are not reflected in any event emission.

These functions include:

  • setBorpaMaster()

  • setMinBridgeAmount()

  • setBlacklisted()

  • setBurnExcluded()

  • setEmissionStart()

  • setChestManager()

  • setEndPoint()

  • setBridgeRouterAddress()

  • setFeeCollector()

Score
Recommendation

Introduce event declarations and ensure their emission within the aforementioned functions. This change will enhance transparency and facilitate accurate off-chain monitoring.

Remediation Plan

ACKNOWLEDGED: The Entangle team made a business decision to acknowledge this finding and not alter the contracts, stating:

Not sure if we need that since all functions are meant to be invoked once, and every address of setting is public by default (or will be after fixes).

References
GorplesCoin.sol#L242-L244
GorplesCoin.sol#L246-L256
GorplesCoin.sol#L263-L272
GorplesCoin.sol#L279-L288
GorplesCoin.sol#L294-L298
GorplesCoin.sol#L304-L308
GorplesCoin.sol#L313-L317
GorplesCoin.sol#L322-L325
GorplesCoin.sol#L329-L332

7.9 Missing state variables visibility modifiers

// Informational

Description

In the GorplesCoin contract, some of the state variables declared are not explicitly declared with any visibility modifiers. In Solidity, the compiler will assume any state variable declared in the contract as internal by default. However, it is considered best practice to explicitly specify visibility to enhance clarity and prevent ambiguity. Clearly labeling the visibility of all variables and functions will help in maintaining clear and understandable code.

The affected variables are: protocolId, eobChainId, endPoint and minBridgeAmounts.

Score
Recommendation

Explicitly define the visibility for each state variable within the contract.

Remediation Plan

SOLVED: The Entangle team has addressed the finding in commit efdbc68 by following the mentioned recommendation.

Remediation Hash
References
GorplesCoin.sol#L51-L53
GorplesCoin.sol#L69

7.10 Missing error description

// Informational

Description

In the setFeeCollector() function, the if (_feeCollector == address(0)) revert(); statement is missing an error description in case of not meeting the requirements.

Providing clear error descriptions helps developers and users quickly identify issues when transactions do not proceed as expected.

Score
Recommendation

Add an error description to the aforementioned statement.

Remediation Plan

ACKNOWLEDGED: The Entangle team made a business decision to acknowledge this finding and not alter the contracts, stating that:

The function is only called by admin with one possible error.

References
BorpaToken.sol#L330

7.11 Floating pragma

// Informational

Description

The file in scope currently use floating pragma version ^0.8.20, which means that the code can be compiled by any compiler version that is greater than or equal to 0.8.20, and less than 0.9.0.

However, it is recommended that contracts should be deployed with the same compiler version and flags used during development and testing. Locking the pragma helps to ensure that contracts do not accidentally get deployed using another pragma. For example, an outdated pragma version might introduce bugs that affect the contract system negatively.

Score
Recommendation

Lock the pragma version to the same version used during development and testing.

Remediation Plan

SOLVED: The Entangle team has addressed the finding in commit 077dbcc by following the mentioned recommendation.

Remediation Hash
References
GorplesCoin.sol#L2

7.12 Unfollowed naming conventions

// Informational

Description

In Solidity, it is a common practice to name internal functions with a single underscore prefix. This is to indicate that these functions are not meant to be used outside of the contract. However, the emitAllocations() function in the GorplesCoin contract does not follow this naming convention.

Additionally, the startRecepient variable from the BorpaToken contract appears to be misnamed. While this typo does not affect the functionality of the contract, it can lead to confusion when reading through the codebase.


Score
Recommendation

To adhere to best practices and improve code readability, it is recommended to rename the internal function emitAllocations() to _emitAllocations(). Additionally, rename the startRecepient variable to startRecipient.

Remediation Plan

SOLVED: The Entangle team has addressed the finding in commit a16656a by following the mentioned recommendation.

Remediation Hash
References
GorplesCoin.sol#L195
GorplesCoin.sol#L61

7.13 Incomplete NATSPEC documentation

// Informational

Description

While the majority of the functions in the GorplesCoin have NATSPEC comments in functions, there are still some functions and state variables that are missing these comments, which may their readability and the overall user experience. Proper NATSPEC documentation is crucial for enhancing the clarity and usability of smart contracts, especially for developers and end-users interacting with them.

Score
Recommendation

Add proper NATSPEC documentation to the entirety of the contract.

Remediation Plan

SOLVED: The Entangle team has addressed the finding in commit 4388085 by following the mentioned recommendation.

Remediation Hash
References
GorplesCoin.sol#L39-L70
GorplesCoin.sol#L87
GorplesCoin.sol#L383
GorplesCoin.sol#L414-L425

7.14 Public functions not called within the contract can be made external

// Informational

Description

The GorplesCoin contract currently defines the currentEmissionRate() function with public visibility, even though it is not called from within the smart contract, resulting in higher gas costs than necessary.

Score
Recommendation

Modify the aforementioned function with the external visibility modifier.

Remediation Plan

SOLVED: The Entangle team has addressed the finding in commit f0bac73 by following the mentioned recommendation.

Remediation Hash
References
GorplesCoin.sol#L125-L127

8. Automated Testing

Static Analysis Report

Description

Halborn used automated testing techniques to enhance the coverage of certain areas of the scoped contracts. Among the tools used was Slither, a Solidity static analysis framework. After Halborn verified the smart contracts in the repository and was able to compile them correctly into their abi and binary format, Slither was run against the contracts. This tool can statically verify mathematical relationships between Solidity variables to detect invalid or inconsistent usage of the contracts' APIs across the entire code-base.

The security team assessed all findings identified by the Slither software, however, findings with related to external dependencies are not included in the below results for the sake of report readability.

Output

Slither results

The findings obtained as a result of the Slither scan were reviewed, and not included in the report because they were determined as false positives.

Unit tests and fuzzing

The original repository used the Foundry environment to develop and test the smart contracts. All original tests were executed successfully. Additionally, these tests were extended locally to generate additional fuzz tests against a local fork of the Ethereum main network that covered ~100,000 runs per test. These additional tests were run successfully.

Foundry fuzz testing results

Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.