Halborn Logo

Brokkr Protocol P1 Contracts - Brokkr


Prepared by:

Halborn Logo

HALBORN

Last Updated 04/26/2024

Date of Engagement by: March 7th, 2022 - March 23rd, 2022

Summary

100% of all REPORTED Findings have been addressed

All findings

10

Critical

0

High

3

Medium

2

Low

3

Informational

2


1. AUDIT SUMMARY

Brokkr engaged Halborn to conduct a security assessment on CosmWasm smart contracts beginning on March 7th, 2022 and ending on March 23rd, 2022.

The security engineers involved on the audit are blockchain and smart-contract security experts with advanced penetration testing, smart-contract hacking, and deep knowledge of multiple blockchain protocols.

The purpose of this audit is to achieve the following:

    • Ensure that smart contract functions work as intended.

    • Identify potential security issues with the smart contracts.

In summary, Halborn identified some improvements to reduce the likelihood and impacts of the risks, which were mostly addressed by the Brokkr team. The main ones are the following:

    • Transfer the ownership of contracts to a Governance contract to control changes through voting. Otherwise, enable additional security measures for them.

    • When unstaking or claiming BRO rewards, remove stakers' info only if, additionally to the already existing conditions, pending bBRO reward is also zero.

    • Consider the block height of each stake when calculating the bBRO rewards.

    • Apply one of the proposed on-chain oracle strategies to avoid that TWAP prices become stale.

    • Split owner address transfer functionality to allow transfer to be completed by recipient.

External threats, such as financial related attacks, oracle attacks, and inter-contract functions and calls should be validated for expected logic and state.

2. TEST APPROACH & 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 the smart contract audit. 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 audit:

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

    • Manual code read and walkthrough.

    • Manual assessment of use and safety for the critical Rust variables and functions in scope to identify any contracts logic related vulnerability.

    • Fuzz testing (Halborn custom fuzzing tool)

    • Checking the test coverage (cargo tarpaulin)

    • Scanning of Rust files for vulnerabilities (cargo audit) \newline

3. SCOPE

\begin{enumerate} \item CosmWasm Smart Contracts \begin{enumerate} \item Repository: \href{https://github.com/block42-blockchain-company/brotocol-token-contracts/tree/main}{brotocol-token-contracts} \item Commit ID: \href{https://github.com/block42-blockchain-company/brotocol-token-contracts/tree/6e5b287382d1c3c29d568851ce3038ffff7407a3}{6e5b287382d1c3c29d568851ce3038ffff7407a3} \item Contracts in scope: \begin{enumerate} \item airdrop \item bonding-v1 \item oracle \item staking-v1 \item vesting \item whitelist-sale \end{enumerate} \end{enumerate} \end{enumerate}

Out-of-scope: External libraries and financial related attacks

4. RISK METHODOLOGY

Vulnerabilities or issues observed by Halborn are ranked based on the risk assessment methodology by measuring the LIKELIHOOD of a security incident and the IMPACT should an incident occur. This framework works for communicating the characteristics and impacts of technology vulnerabilities. The quantitative model ensures repeatable and accurate measurement while enabling users to see the underlying vulnerability characteristics that were used to generate the Risk scores. For every vulnerability, a risk level will be calculated on a scale of 5 to 1 with 5 being the highest likelihood or impact.
RISK SCALE - LIKELIHOOD
  • 5 - Almost certain an incident will occur.
  • 4 - High probability of an incident occurring.
  • 3 - Potential of a security incident in the long term.
  • 2 - Low probability of an incident occurring.
  • 1 - Very unlikely issue will cause an incident.
RISK SCALE - IMPACT
  • 5 - May cause devastating and unrecoverable impact or loss.
  • 4 - May cause a significant level of impact or loss.
  • 3 - May cause a partial impact or loss to many.
  • 2 - May cause temporary impact or loss.
  • 1 - May cause minimal or un-noticeable impact.
The risk level is then calculated using a sum of these two values, creating a value of 10 to 1 with 10 being the highest level of security risk.
Critical
High
Medium
Low
Informational
  • 10 - CRITICAL
  • 9 - 8 - HIGH
  • 7 - 6 - MEDIUM
  • 5 - 4 - LOW
  • 3 - 1 - VERY LOW AND INFORMATIONAL

5. SCOPE

Out-of-Scope: New features/implementations after the remediation commit IDs.

6. Assessment Summary & Findings Overview

Critical

0

High

3

Medium

2

Low

3

Informational

2

Impact x Likelihood

HAL-01

HAL-05

HAL-06

HAL-07

HAL-08

HAL-04

HAL-02

HAL-03

HAL-09

HAL-10

Security analysisRisk levelRemediation Date
POSSIBILITY TO TRANSFER AN ARBITRARY AMOUNT OF TOKENS OUT OF SOME CONTRACTSHighFuture Release
BBRO TOKENS ARE LOST WHEN UNSTAKING OR CLAIMING REWARDSHighSolved - 03/26/2022
BBRO REWARDS SCHEMA COULD PRODUCE UNFAIR ADVANTAGESHighPartially Solved
PRICES FROM TWAP ORACLE CAN BECOME STALEMediumSolved - 03/23/2022
PRIVILEGED ADDRESS CAN BE TRANSFERRED WITHOUT CONFIRMATIONMediumSolved - 03/21/2022
MISSING VALIDATION FOR BASE RATE AND GROWTH PARAMETERSLowSolved - 03/26/2022
MISSING VALIDATION FOR BONDING DISCOUNTS AND REWARD RATIOLowSolved - 03/26/2022
MISSING VALIDATION FOR MIN AND MAX VALUES OF LOCKUP PERIODLowSolved - 03/26/2022
VESTING SCHEDULES COULD BE UNCLAIMABLEInformationalAcknowledged
SLIGHT ROUNDING ISSUES WHEN PROVIDING LP TOKENSInformationalAcknowledged

7. Findings & Tech Details

7.1 POSSIBILITY TO TRANSFER AN ARBITRARY AMOUNT OF TOKENS OUT OF SOME CONTRACTS

// High

Description

The bonding-v1 and mvp-treasury contracts allow an admin (Brokkr team) to transfer an arbitrary amount of tokens out of them. Two possible attack scenarios will be described below.

\textbf{\underline{Attack scenario 1}:}

  1. A malicious (or compromised) admin calls the update_config function in the bonding-v1 contract to change the value of treasury_contract to an address controlled by him.

  2. As a consequence of Step 1, every time users provide LP tokens or UST to bonding-v1 contract, they will be transferred to the malicious address.

\textbf{\underline{Attack scenario 2}:}

  1. A malicious (or compromised) admin calls the spend function in mvp-treasury contract with recipient = .

  2. As a consequence of Step 1, the aforementioned function could fully drain the mvp-treasury contract.

Code Location

A malicious (or compromised) admin could change the value of treasury_contract in bonding-v1 to an address controlled by him:

contracts/bonding-v1/src/commands.rs

pub fn update_config(
    deps: DepsMut,
    owner: Option<String>,
    lp_token: Option<String>,
    rewards_pool_contract: Option<String>,
    treasury_contract: Option<String>,

contracts/bonding-v1/src/commands.rs

    if let Some(rewards_pool_contract) = rewards_pool_contract {
        config.rewards_pool_contract = deps.api.addr_canonicalize(&rewards_pool_contract)?;
    }

    if let Some(treasury_contract) = treasury_contract {
        config.treasury_contract = deps.api.addr_canonicalize(&treasury_contract)?;
    }

\color{black}\color{white}spend function allows a malicious (or compromised) admin to fully drain the mvp-treasury contract:

contracts/mvp-treasury/src/commands.rs

pub fn spend(
    deps: DepsMut,
    env: Env,
    asset_info: AssetInfo,
    recipient: String,
) -> Result<Response, ContractError> {
    let balance = queries::query_asset_balance(deps.as_ref(), env, asset_info.clone())?.amount;
    if balance.is_zero() {
        return Err(ContractError::InsufficientFunds {});
    }

    let asset = Asset {
        info: asset_info,
        amount: balance,
    };

    Ok(Response::new()
        .add_messages(vec![
            asset.into_msg(&deps.querier, deps.api.addr_validate(&recipient)?)?
        ])
        .add_attributes(vec![("action", "spend")]))
}
Score
Impact: 5
Likelihood: 3
Recommendation

PENDING: The Brokkr team stated that they plan to transfer the ownership of the contracts to a full-fledged governance contract setup in the midterm, which will severely reduce the likelihood of liquidity loss exposure attacks. Meanwhile, they will handle the Brokkr admin by a multisig wallet, as suggested in the recommendation.

7.2 BBRO TOKENS ARE LOST WHEN UNSTAKING OR CLAIMING REWARDS

// High

Description

When a user unstake or claim BRO rewards in staking-v1 contract, his information is totally removed from STAKERS item if the following conditions are true:

  • Total staked amount is zero
  • Pending BRO reward is zero

Under the mentioned circumstances, the user won't be able to claim his bBRO rewards, even if the amount is greater than zero, i.e.: he totally loses all his bBRO tokens.

Code Location

User's information is totally removed from STAKERS item when he unstakes:

contracts/staking-v1/src/commands.rs

// decrease stake amount
state.total_stake_amount = state.total_stake_amount.checked_sub(amount)?;
staker_info.unlocked_stake_amount = staker_info.unlocked_stake_amount.checked_sub(amount)?;

if staker_info.pending_bro_reward.is_zero() && staker_info.total_staked()?.is_zero() {
    remove_staker_info(deps.storage, &sender_addr_raw);
} else {
    store_staker_info(deps.storage, &sender_addr_raw, &staker_info)?;
}

\color{black}\color{white}User's information is totally removed from STAKERS item when he claims his BRO rewards:

contracts/staking-v1/src/commands.rs

staker_info.pending_bro_reward = Uint128::zero();
staker_info.unlock_expired_lockups(&env.block)?;

if staker_info.total_staked()?.is_zero() {
    remove_staker_info(deps.storage, &sender_addr_raw);
} else {
    store_staker_info(deps.storage, &sender_addr_raw, &staker_info)?;
}
Score
Impact: 3
Likelihood: 5
Recommendation

SOLVED: The issue was fixed in commit f9bbc72a85ff872b36691c4992d7a86439a4bba2.

7.3 BBRO REWARDS SCHEMA COULD PRODUCE UNFAIR ADVANTAGES

// High

Description

When users stake, the compute_normal_bbro_reward function is called to calculate the bBRO rewards accrued. Because of the calculation of bbro_reward value depends on the subtraction between last_distribution_block and last_balance_update, it is possible the following attack scenario:

\textbf{\underline{Attack scenario}:}

  1. Just after a reward distribution, an attacker stakes 10_000000 BRO (Block height: 200).

  2. Attacker waits until there is another reward distribution and he front-runs the transaction to additionally stake 80_000000 BRO before the distribution of BRO tokens (Block height: 700).

  3. The attacker unstakes 80_000000 BRO just after the reward distribution (could be even in the same block!).

  4. The calculation of his bBRO reward will consider that 90_000000 BRO has been staked for 500 blocks (700 - 200). However, only 10_000000 BRO of the total has been staked for 500 blocks. This situation gives the attacker an unfair advantage.

Code Location

contracts/staking-v1/src/state.rs

let stake_amount = self.total_staked()?;

if stake_amount.is_zero() || state.last_distribution_block < self.last_balance_update {
    return Ok(());
}

let epoch_info = query_epoch_info(querier, epoch_manager_contract)?;

let epochs_staked = Uint128::from(state.last_distribution_block - self.last_balance_update)
    .checked_div(Uint128::from(epoch_info.epoch))?;

let bbro_per_epoch_reward =
    stake_amount.checked_div(epoch_info.epochs_per_year())? * epoch_info.bbro_emission_rate;

let bbro_reward = bbro_per_epoch_reward.checked_mul(epochs_staked)?;
self.pending_bbro_reward = self.pending_bbro_reward.checked_add(bbro_reward)?;
self.last_balance_update = current_block;

Ok(())
Score
Impact: 3
Likelihood: 5
Recommendation

PARTIALLY SOLVED: The Brokkr team claimed that contracts will be launched to mainnet with the following parameters:

  • epoch = 17,280 blocks (1 day)
  • unstake_period_blocks = 241,920 blocks (14 days)

Because of this setup, the attack vector mentioned above is not profitable unless the rewards are distributed after 14 days, which is highly unlikely, especially with the use of an off-chain trigger (out-of-scope of this audit).

If the above-mentioned parameters are subsequently incorrectly changed with the update_config function, this security issue could arise again, so it has been marked as "Partially Solved".

7.4 PRICES FROM TWAP ORACLE CAN BECOME STALE

// Medium

Description

consult_price function in contracts/oracle/src/queries.rs calculates the return value using the price_average stored in oracle contract's storage (PRICE_LAST). However, the function does not previously validate if the price has been updated within a reasonable timeframe.

As a consequence, prices calculated in this TWAP oracle can rapidly become stale if users do not bond tokens frequently enough or if Brokkr's off-chain trigger does not work correctly (out-of-scope for this audit), which could affect negatively users' operations or protocol funds.

Code Location

price_average is extracted from oracle contract's storage (PRICE_LAST) without validating if the price has been updated within a reasonable timeframe:

contracts/oracle/src/queries.rs

let config = load_config(deps.storage)?;
let price_last = load_price_cumulative_last(deps.storage)?;

let price_average = if config.asset_infos[0].equal(&asset) {
    price_last.price_0_average
} else if config.asset_infos[1].equal(&asset) {
    price_last.price_1_average
} else {
    return Err(StdError::generic_err("Invalid asset info"));
};

\color{black}\color{white}Return value of consult_price function is calculated using price_average, even if this value is stale:

contracts/oracle/src/queries.rs

} else {
    let price_precision = Uint256::from(10_u128.pow(TWAP_PRECISION.into()));
    Uint256::from(amount) * price_average / Decimal256::from_uint256(price_precision)
};

Ok(ConsultPriceResponse {
    amount: consult_price.into(),
})
Score
Impact: 3
Likelihood: 4
Recommendation

SOLVED: The issue was fixed in commit 44a54bddf48a4e28b711449ddcb13d9bf31afdb9.

7.5 PRIVILEGED ADDRESS CAN BE TRANSFERRED WITHOUT CONFIRMATION

// Medium

Description

An incorrect use of the update_config function in contracts can set owner to an invalid address and inadvertently lose control of the contracts, which cannot be undone in any way. Currently, the owner of the contracts can change owner address using the aforementioned function in a single transaction and without confirmation from the new address.

The affected smart contracts are the following:

  • airdrop
  • bonding-v1
  • oracle
  • staking-v1
  • vesting

Code Location

contracts/airdrop/src/commands.rs

pub fn update_config(deps: DepsMut, owner: Option<String>) -> Result<Response, ContractError> {
    let mut config = load_config(deps.storage)?;

    if let Some(owner) = owner {
        config.owner = deps.api.addr_canonicalize(&owner)?;
    }

contracts/bonding-v1/src/commands.rs

pub fn update_config(
    deps: DepsMut,
    owner: Option<String>,
    lp_token: Option<String>,
    rewards_pool_contract: Option<String>,
    treasury_contract: Option<String>,
    astroport_factory: Option<String>,
    oracle_contract: Option<String>,
    ust_bonding_reward_ratio: Option<Decimal>,
    ust_bonding_discount: Option<Decimal>,
    lp_bonding_discount: Option<Decimal>,
    min_bro_payout: Option<Uint128>,
    vesting_period_blocks: Option<u64>,
    lp_bonding_enabled: Option<bool>,
) -> Result<Response, ContractError> {
    let mut config = load_config(deps.storage)?;

    if let Some(owner) = owner {
        config.owner = deps.api.addr_canonicalize(&owner)?;
    }

contracts/oracle/src/commands.rs

pub fn update_config(
    deps: DepsMut,
    owner: Option<String>,
    price_update_interval: Option<u64>,
) -> Result<Response, ContractError> {
    let mut config = load_config(deps.storage)?;

    if let Some(owner) = owner {
        config.owner = deps.api.addr_canonicalize(&owner)?;
    }

contracts/staking-v1/src/commands.rs

pub fn update_config(
    deps: DepsMut,
    owner: Option<String>,
    paused: Option<bool>,
    unstake_period_blocks: Option<u64>,
    min_staking_amount: Option<Uint128>,
    min_lockup_period_epochs: Option<u64>,
    max_lockup_period_epochs: Option<u64>,
    base_rate: Option<Decimal>,
    linear_growth: Option<Decimal>,
    exponential_growth: Option<Decimal>,
) -> Result<Response, ContractError> {
    let mut config = load_config(deps.storage)?;

    if let Some(owner) = owner {
        config.owner = deps.api.addr_canonicalize(&owner)?;
    }

contracts/vesting/src/commands.rs

pub fn update_config(
    deps: DepsMut,
    owner: Option<String>,
    genesis_time: Option<u64>,
) -> Result<Response, ContractError> {
    let mut config = load_config(deps.storage)?;
    if let Some(owner) = owner {
        config.owner = deps.api.addr_canonicalize(&owner)?;
    }
Score
Impact: 4
Likelihood: 2
Recommendation

SOLVED: The issue was fixed in commit 79549c38936e99a89a1fa7aa7e38456032f47389.

7.6 MISSING VALIDATION FOR BASE RATE AND GROWTH PARAMETERS

// Low

Description

instantiate and update_config functions in staking-v1 contract do not validate that values of base_rate, linear_growth or exponential_growth are less or equal than a threshold (e.g.: 0.1) predefined in the contract.

The aforementioned values are used to calculate premium bBRO rewards. If those values are not correctly set, the premium bBRO rewards for users could be much higher than expected.

Code Location

instantiate function does not validate that base_rate, linear_growth or exponential_growth are less or equal than a predefined threshold:

contracts/staking-v1/src/contract.rs

store_config(
    deps.storage,
    &Config {
        owner: deps.api.addr_canonicalize(&msg.owner)?,
        paused: false,
        bro_token: deps.api.addr_canonicalize(&msg.bro_token)?,
        rewards_pool_contract: deps.api.addr_canonicalize(&msg.rewards_pool_contract)?,
        bbro_minter_contract: deps.api.addr_canonicalize(&msg.bbro_minter_contract)?,
        epoch_manager_contract: deps.api.addr_canonicalize(&msg.epoch_manager_contract)?,
        unstake_period_blocks: msg.unstake_period_blocks,
        min_staking_amount: msg.min_staking_amount,
        lockup_config: LockupConfig {
           min_lockup_period_epochs: msg.min_lockup_period_epochs,
           max_lockup_period_epochs: msg.max_lockup_period_epochs,
           base_rate: msg.base_rate,
           linear_growth: msg.linear_growth,
           exponential_growth: msg.exponential_growth,
        },
    },
)?;

\color{black}\color{white}update_config function does not validate that base_rate, linear_growth or exponential_growth are less or equal than a predefined threshold:

contracts/staking-v1/src/commands.rs

if let Some(base_rate) = base_rate {
    config.lockup_config.base_rate = base_rate;
}

if let Some(linear_growth) = linear_growth {
    config.lockup_config.linear_growth = linear_growth;
}

if let Some(exponential_growth) = exponential_growth {
    config.lockup_config.exponential_growth = exponential_growth;
}
Score
Impact: 3
Likelihood: 2
Recommendation

SOLVED: The issue was fixed in commit 032d729b4cddd49c990fdb5d9e78c608d21f0d25.

7.7 MISSING VALIDATION FOR BONDING DISCOUNTS AND REWARD RATIO

// Low

Description

instantiate and update_config functions in bonding-v1 contract do not validate that values of ust_bonding_reward_ratio, ust_bonding_discount or lp_bonding_discount are less or equal than 1.

The aforementioned values are used to calculate rewards distribution and amounts of BRO tokens to claim. If those values are not correctly set, operations will throw error messages and won't allow legitimate users to claim their rewards, thus generating a denial of service (DoS) in Brokkr protocol.

Code Location

instantiate function does not validate that ust_bonding_discount or lp_bonding_discount are less or equal than 1:

contracts/bonding-v1/src/contract.rs

if msg.ust_bonding_reward_ratio > Decimal::from_str("1.0")?
    || msg.ust_bonding_reward_ratio <= Decimal::zero()
{
    return Err(ContractError::InvalidUstBondRatio {});
}

store_config(
    deps.storage,
    &Config {
        owner: deps.api.addr_canonicalize(&msg.owner)?,
        bro_token: deps.api.addr_canonicalize(&msg.bro_token)?,
        lp_token: deps.api.addr_canonicalize(&msg.lp_token)?,
        rewards_pool_contract: deps.api.addr_canonicalize(&msg.rewards_pool_contract)?,
        treasury_contract: deps.api.addr_canonicalize(&msg.treasury_contract)?,
        astroport_factory: deps.api.addr_canonicalize(&msg.astroport_factory)?,
        oracle_contract: deps.api.addr_canonicalize(&msg.oracle_contract)?,
        ust_bonding_reward_ratio: msg.ust_bonding_reward_ratio,
        ust_bonding_discount: msg.ust_bonding_discount,
        lp_bonding_discount: msg.lp_bonding_discount,
        min_bro_payout: msg.min_bro_payout,
        vesting_period_blocks: msg.vesting_period_blocks,
        lp_bonding_enabled: msg.lp_bonding_enabled,
    },
)?;

\color{black}\color{white}update_config function does not validate that ust_bonding_reward_ratio, ust_bonding_discount or lp_bonding_discount are less or equal than 1:

contracts/bonding-v1/src/commands.rs

if let Some(ust_bonding_reward_ratio) = ust_bonding_reward_ratio {
    config.ust_bonding_reward_ratio = ust_bonding_reward_ratio;
}

if let Some(ust_bonding_discount) = ust_bonding_discount {
    config.ust_bonding_discount = ust_bonding_discount;
}

if let Some(lp_bonding_discount) = lp_bonding_discount {
    config.lp_bonding_discount = lp_bonding_discount;
}
Score
Impact: 3
Likelihood: 2
Recommendation

SOLVED: The issue was fixed in commit e80b7dc97ff20b683cd27d7a4cdaa6d7d60c1076.

7.8 MISSING VALIDATION FOR MIN AND MAX VALUES OF LOCKUP PERIOD

// Low

Description

instantiate and update_config functions in staking-v1 contract do not validate that min_lockup_period_epochs is less than max_lockup_peri od_epochs.

The aforementioned values are used to validate the lockup period when staking locked BRO tokens or locking a previous staked amount. If those values are not correctly set, operations will throw error messages and won't allow legitimate users to stake or lock BRO tokens, thus generating a denial of service (DoS) in Brokkr protocol.

Code Location

instantiate function does not validate that min_lockup_period_epochs is less than max_lockup_period_epochs:

contracts/staking-v1/src/contract.rs

store_config(
    deps.storage,
    &Config {
        owner: deps.api.addr_canonicalize(&msg.owner)?,
        paused: false,
        bro_token: deps.api.addr_canonicalize(&msg.bro_token)?,
        rewards_pool_contract: deps.api.addr_canonicalize(&msg.rewards_pool_contract)?,
        bbro_minter_contract: deps.api.addr_canonicalize(&msg.bbro_minter_contract)?,
        epoch_manager_contract: deps.api.addr_canonicalize(&msg.epoch_manager_contract)?,
        unstake_period_blocks: msg.unstake_period_blocks,
        min_staking_amount: msg.min_staking_amount,
        lockup_config: LockupConfig {
           min_lockup_period_epochs: msg.min_lockup_period_epochs,
           max_lockup_period_epochs: msg.max_lockup_period_epochs,
           base_rate: msg.base_rate,
           linear_growth: msg.linear_growth,
           exponential_growth: msg.exponential_growth,
        },
    },
)?;

\color{black}\color{white}update_config function does not validate that min_lockup_period_epochs is less than max_lockup_period_epochs:

contracts/staking-v1/src/commands.rs

if let Some(min_lockup_period_epochs) = min_lockup_period_epochs {
    config.lockup_config.min_lockup_period_epochs = min_lockup_period_epochs;
}

if let Some(max_lockup_period_epochs) = max_lockup_period_epochs {
    config.lockup_config.max_lockup_period_epochs = max_lockup_period_epochs;
}
Score
Impact: 3
Likelihood: 2
Recommendation

SOLVED: The issue was fixed in commit b9c1e4ad60fa79e030737e5374a8b027c147d091.

7.9 VESTING SCHEDULES COULD BE UNCLAIMABLE

// Informational

Description

When registering new vesting accounts, the vesting contract sets the last claimed time to the configured genesis time. That parameter could be greater than the expiration date of some vesting schedules, such that vesting_info.last_claim_time > vesting_info.end_time, which makes it unclaimable. However, it is possible for the administrator to update genesis time and set new vesting schedules.

Code Location

packages/services/src/vesting.rs,

if current_time > schedule.end_time && self.last_claim_time < schedule.end_time {
    claimable_amount += schedule.bro_amount;
}
Score
Impact: 2
Likelihood: 1
Recommendation

\textbf{ACKNOWLEDGED:} The Brokkr team acknowledged this finding.

7.10 SLIGHT ROUNDING ISSUES WHEN PROVIDING LP TOKENS

// Informational

Description

When calculating the return values in get_share_in_assets function every time users provide LP tokens to bonding-v1 contract, the "multiply before divide" principle is not followed, which could generate slight rounding issues in some edge scenarios.

Code Location

contracts/bonding-v1/src/commands.rs

fn get_share_in_assets(
    querier: &QuerierWrapper,
    bro_pool: &Asset,
    ust_pool: &Asset,
    lp_amount: Uint128,
    lp_token_addr: Addr,
) -> StdResult<(Uint128, Uint128)> {
    let total_share = query_supply(querier, lp_token_addr)?;
    let share_ratio = Decimal::from_ratio(lp_amount, total_share);

    Ok((bro_pool.amount * share_ratio, ust_pool.amount * share_ratio))
}
Score
Impact: 1
Likelihood: 2
Recommendation

\textbf{ACKNOWLEDGED:} The Brokkr team acknowledged this finding.

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.

© Halborn 2024. All rights reserved.