Skip to main content

Bondex Token and Token Vesting

Smart Contract Security Assessment

Mar 07, 2024

Bondex

SUMMARY


ABSTRACT

Dedaub was commissioned to perform a security audit of the Bondex Token and Bondex Token Vesting protocol.


BACKGROUND

The Bondex Token is an upgradable ERC20 token with burn and permit functionality. Upon initialisation, the token can be configured to be upgradable by an address with the UPGRADER_ROLE, as well as to mint a specific supply and to give this supply to an initial supply holder address.

The Bondex Token Vesting contract holds a number of tokens for a given address (claimant), and allows the address to claim these funds according to a configurable vesting schedule. In particular the contract can be configured to allow the address to claim a certain percentage of the funds immediately, while the rest of the funds are distributed linearly over a vesting period once the set cliff period has passed. These Vesting contracts are cloned using a Vesting Factory contract, so that each address can have its own Vesting contract.


SETTING & CAVEATS

This audit report mainly covers the following contracts of the Bondex Token repository at commit number:

bb26f0bce57862d89f67209ee7b42f761a8d47aa
contracts
  • BaseERC20.sol
  • interfaces
    • IBaseERC20.sol
  • storage
    • StorageBaseERC20.sol

As well as the contracts of the Bondex Token Vesting repository at commit number:

21f214ad7cdb8cb08dd066b51339db32e682da2a
contracts
  • interfaces
    • IVesting.sol
  • storage
    • StorageVesting.sol
  • VestingFactory.sol
  • Vesting.sol

Two auditors worked on the codebase for 2 days on the above two repositories.

Following the audit, the team made some changes to the Bondex Token Vesting repository at the following commit number in order to address the issues identified by the audit:

1dcee236d2979519d1ac46764f1ebc57a754f4f9

The auditors have reviewed the fixes and confirm that they fix the issues marked as resolved in the report below.

The audit’s main target is security threats, i.e., what the community understanding would likely call "hacking", rather than the regular use of the protocol. Functional correctness (i.e. issues in "regular use") is a secondary consideration. Typically it can only be covered if we are provided with unambiguous (i.e. full-detail) specifications of what is the expected, correct behavior. In terms of functional correctness, we often trusted the code’s calculations and interactions, in the absence of any other specification. Functional correctness relative to low-level calculations (including units, scaling and quantities returned from external protocols) is generally most effectively done through thorough testing rather than human auditing.


VULNERABILITIES & FUNCTIONAL ISSUES

This section details issues affecting the functionality of the contract. Dedaub generally categorizes issues according to the following severities, but may also take other considerations into account such as impact or difficulty in exploitation:

Category
Description
CRITICAL
Can be profitably exploited by any knowledgeable third-party attacker to drain a portion of the system’s or users’ funds OR the contract does not function as intended and severe loss of funds may result.
HIGH
Third-party attackers or faulty functionality may block the system or cause the system or users to lose funds. Important system invariants can be violated.
MEDIUM
Examples:
  • User or system funds can be lost when third-party systems misbehave.
  • DoS, under specific conditions.
  • Part of the functionality becomes unusable due to a programming error.
LOW
Examples:
  • Breaking important system invariants but without apparent consequences.
  • Buggy functionality for trusted users where a workaround exists.
  • Security issues which may manifest when the system evolves.

Issue resolution includes “dismissed” or “acknowledged” but no action taken, by the client, or “resolved”, per the auditors.


CRITICAL SEVERITY

[No critical severity issues]


HIGH SEVERITY

[No high severity issues]


MEDIUM SEVERITY

[No medium severity issues]


LOW SEVERITY

L1

Token holders could accidentally burn their tokens

L1LOW

Token holders could accidentally burn their tokens
acknowledged

Acknowledged

The team has indicated that the burn functionality will only be used mistakenly with very low probability and thus this issue does not have a significant impact on the operation of the protocol.

The BaseERC20 contract extends OpenZeppelin’s ERC20BurnableUpgradeable while not imposing any restriction on who can call its burn functions. This means that token holders could accidentally burn their tokens by mistakenly calling the burn() function. The same is true for approved addresses. Even though this scenario might be improbable there is no reason to not restrict the burn functionality only to the addresses that are expected to burn part of token supply as planned by the tokenomics of the protocol.



CENTRALIZATION ISSUES

It is often desirable for DeFi protocols to assume no trust in a central authority, including the protocol’s owner. Even if the owner is reputable, users are more likely to engage with a protocol that guarantees no catastrophic failure even in the case the owner gets hacked/compromised. We list issues of this kind below. (These issues should be considered in the context of usage/deployment, as they are not uncommon. Several high-profile, high-value protocols have significant centralization threats.)

[No centralisation issues]


OTHER / ADVISORY ISSUES

This section details issues that are not thought to directly affect the functionality of the project, but we recommend considering them.

A1

Missing comment in __Vesting_init_unchained function of the Vesting contract

A1ADVISORY

Missing comment in __Vesting_init_unchained function of the Vesting contract
resolved

The @notice comment for the __Vesting_init_unchained function of the Vesting contract lists the conditions when the call should revert. However it is missing the condition where the address of the config.token is the zero address.

A2

Vesting tgeTime could be enforced to be > block.timestamp

A2ADVISORY

Vesting tgeTime could be enforced to be > block.timestamp
resolved

Resolved

The team has added a comment to the __Vesting_init_unchained function to indicate that the vesting tgeTime can be in the past for flexibility purposes during deployment.

Execution of the function Vesting::__Vesting_init_unchained reverts if config.tgeTime + config.cliffDuration + config.vestingDuration < block.timestamp. This means that when a vesting contract is initialized the config.tgeTime can be less than block.timestamp, which we consider to be a bit counter intuitive as the vesting would have to have started at some point in the past. The check could be changed to config.tgeTime < block.timestamp.

A3

Incorrect comment in IVesting

A3ADVISORY

Incorrect comment in IVesting
resolved

The comment that describes the purpose of VestingConfig::tgePercentage is incorrect in saying that this is “the percentage of the total amount that will be vested at the TGE”, as according to the implementation, it is the percentage of the total amount that will not be vested at the TGE, but it will be available immediately.

A4

Unused variable

A4ADVISORY

Unused variable
resolved

The function Vesting::getClaimableAmount computes the amount of the Vesting contract that is claimable at the time that it is called. At some point it sets the timePassed local variable to $.config.vestingDuration but never uses it again.

Vesting::getClaimableAmount:138
if ($.config.tgeTime + $.config.cliffDuration < block.timestamp) {
uint256 timePassed = block.timestamp -
$.config.tgeTime -
$.config.cliffDuration;

if (timePassed >= $.config.vestingDuration) {
timePassed = $.config.vestingDuration; // Dedaub: unused variable
totalClaimable = $.config.totalAmount;
} else {
uint256 vestedAmount = (amountExcludingTGE * timePassed) /
$.config.vestingDuration;

totalClaimable += vestedAmount;
}
}

A5

Magic constant in the Vesting contract

A5ADVISORY

Magic constant in the Vesting contract
resolved

The Vesting contact uses the magic constant 100 * (10**PERCENTAGE_DECIMALS) at a couple of places without explicitly defining it in one place. This constant corresponds to the percentage 100%, with an accuracy of PERCENTAGE_DECIMALS decimals.

A6

Compiler bugs

A6ADVISORY

Compiler bugs
info

The code is compiled with Solidity 0.8.24. Currently version 0.8.24, does not have any known bugs, but we advise consulting this list every so often for any newly discovered ones.



DISCLAIMER

The audited contracts have been analyzed using automated techniques and extensive human inspection in accordance with state-of-the-art practices as of the date of this report. The audit makes no statements or warranties on the security of the code. On its own, it cannot be considered a sufficient assessment of the correctness of the contract. While we have conducted an analysis to the best of our ability, it is our recommendation for high-value contracts to commission several independent audits, a public bug bounty program, as well as continuous security auditing and monitoring through Dedaub Security Suite.


ABOUT DEDAUB

Dedaub offers significant security expertise combined with cutting-edge program analysis technology to secure some of the most prominent protocols in DeFi. The founders, as well as many of Dedaub's auditors, have a strong academic research background together with a real-world hacker mentality to secure code. Protocol blockchain developers hire us for our foundational analysis tools and deep expertise in program analysis, reverse engineering, DeFi exploits, cryptography and financial mathematics.