Halborn Logo

Kakeru Contracts - Kakeru


Prepared by:

Halborn Logo

HALBORN

Last Updated 07/16/2024

Date of Engagement by: January 22nd, 2024 - March 27th, 2024

Summary

100% of all REPORTED Findings have been addressed

All findings

21

Critical

1

High

0

Medium

4

Low

6

Informational

10


1. Introduction

Kakeru engaged Halborn to conduct a security assessment of the Kakeru contracts, beginning on January 22nd, 2024 and ending on March 23th, 2024. This security assessment was scoped to the smart contracts in the GitHub repository.

Kakeru facilitates seamless integration of liquidity across different chains, thereby enhancing capital efficiency and ensuring equitable fee distribution to platform participants.

With a primary objective of transforming nUSD into a secure, interest-bearing, and fully decentralized stablecoin, Kakeru introduces innovative mechanisms backed by INJ and nINJ assets to provide users with a robust financial instrument for navigating the complexities of decentralized finance.

2. Assessment Summary

The team at Halborn assigned a full-time security engineer to verify the security of the smart contracts. The security engineer is a blockchain and smart-contract security expert with advanced penetration testing, smart-contract hacking, and deep knowledge of multiple blockchain protocols.

The purpose of this assessment is to:

    • Ensure that smart contract functions operate as intended

    • Identify potential security issues with the smart contracts

In summary, Halborn identified some improvements to reduce the likelihood and impact of risks, which were mostly addressed by the Kakeru team:

    • Modify the access control that can be bypassed to mint any number of stable coins by any user.

    • Modify the repeated "if" condition that prevents part of the code execution.

    • Add user access control to the withdraw entry point.

    • Modify token calculation to avoid truncation losses.

    • Add administrator access control to the register_contracts entry point.

    • Add more validations to time constraints.

    • Add more validations to the amounts to be shared.


3. Test Approach and Methodology

Halborn performed a combination of manual 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 the code and can quickly identify items that do not follow the security best practices. The following phases and associated tools were used during the assessment:

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

    • Manual code read and walk through.

    • Manual Assessment of use and safety for the critical Rust variables and functions in scope to identify any arithmetic related vulnerability classes.

    • Architecture related logical controls.

    • Cross contract call controls.

    • Scanning of Rust files for vulnerabilities(cargo audit)

    • Review and improvement of integration tests.

    • Deployment to local testnet interacting with Injective CLI (injectived).

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
(c) Items in scope:
  • gryphon-staking-contracts/contracts/basset_inj_hub
  • gryphon-staking-contracts/contracts/basset_inj_reward
  • gryphon-staking-contracts/contracts/basset_inj_rewards_dispatcher
↓ Expand ↓
Out-of-Scope: Third party dependencies, Economic attacks.
Files and Repository
(a) Repository: gryphon-market-contracts
(c) Items in scope:
  • gryphon-market-contracts/contracts/custody_base
  • gryphon-market-contracts/contracts/custody_binj
  • gryphon-market-contracts/contracts/distribution_model
↓ Expand ↓
Out-of-Scope: Third party dependencies, Economic attacks
Files and Repository
(a) Repository: gryphon-cdp-contracts
(c) Items in scope:
  • gryphon-cdp-contracts/contracts/central_control
  • gryphon-cdp-contracts/contracts/custody
  • gryphon-cdp-contracts/contracts/liquidation_queue
↓ Expand ↓
Out-of-Scope: Third party dependencies, Economic attacks.
Files and Repository
(a) Repository: gryphon-token-contracts
(c) Items in scope:
  • gryphon-token-contracts/contracts/boost
  • gryphon-token-contracts/contracts/dispatcher
  • gryphon-token-contracts/contracts/distribute
↓ Expand ↓
Out-of-Scope: Third party dependencies, Economic attacks.
Files and Repository
(a) Repository: gryphon-basset-convert
(c) Items in scope:
  • gryphon-basset-convert/contracts/basset_converter
  • gryphon-basset-convert/contracts/basset_token
  • gryphon-basset-convert/packages
Out-of-Scope: Third party dependencies, Economic attacks.
Files and Repository
(a) Repository: gryphon-oracle
(c) Items in scope:
  • gryphon-oracle/contracts/mock_oracle
  • gryphon-oracle/contracts/oracle_pyth
  • gryphon-oracle/packages
Out-of-Scope: Third party dependencies, Economic attacks.
Files and Repository
(a) Repository: swap-extension
(c) Items in scope:
  • swap-extension/contracts/mock_swap_pair
  • swap-extension/contracts/swap_astroport
  • swap-extension/packages
Out-of-Scope: Third party dependencies, Economic attacks
Remediation Commit ID:
Out-of-Scope: New features/implementations after the remediation commit IDs.

6. Assessment Summary & Findings Overview

Critical

1

High

0

Medium

4

Low

6

Informational

10

Security analysisRisk levelRemediation Date
User can mint any amount of stable coinsCriticalSolved - 01/20/2024
Fragment of code never executedMediumSolved - 04/07/2024
Lack of variable initializationMediumNot Applicable
Exchange rate not updated properlyMediumNot Applicable
Missing slashing checkMediumNot Applicable
Lack of access control in withdraw entry pointLowSolved - 04/07/2024
Token loss due to truncationLowSolved - 04/07/2024
Lack of admin access controlLowSolved - 05/25/2024
No time validationsLowSolved - 04/07/2024
The re-stake function is partially suppressedLowNot Applicable
Lack of validation could block the tokensLowSolved - 04/07/2024
Optional input could lead to failure in executionInformationalAcknowledged
Wrong implementationInformationalSolved - 05/25/2024
No address validationInformationalSolved - 04/07/2024
Entry point not neededInformationalSolved - 05/25/2024
Repeated conditionInformationalSolved - 05/24/2024
No error handlingInformationalSolved - 04/07/2024
Incorrect claimable amount calculationInformationalSolved - 02/28/2024
Set token metadata during instantiationInformationalAcknowledged
Useless contractInformationalSolved - 04/07/2024
Parameter not usedInformationalSolved - 05/24/2024

7. Findings & Tech Details

7.1 User can mint any amount of stable coins

// Critical

Description

The mint_stable_coin function allows to mint a certain amount of stable coins depending on the amount of collateral deposited by the user. The validations are performed by the Custody contract, verifying that the user has already deposited the corresponding collateral, however, this control can be bypassed by directly calling this entry point in the Central Control contract.

The access control of the mint_stable_coin function is not correctly implemented. It is a nested "if" where the first condition checks if info.sender is different from the Custody contract and the second checks if it is equal to the minter value.

Since the minter value is an input included in the message, it can be set by the attacker to the same value as info.sender and bypass the access control. After this, there is no validation on collateral_amount and collateral_contract values, so the minting process is performed with any amount chosen by the attacker.

This is the affected code snippet:

Vulnerable code
Proof of Concept

Attacker transaction:

injectived tx wasm execute <CDP_CENTRAL_CONTROL_ADDRESS>'{"mint_stable_coin":{"minter":"<USER_ADDRESS", "stable_amount": "1000", "collateral_amount": "1000", "collateral_contract": "<COLLATERAL_CONTRACT>"}}' --chain-id <CHAIN_ID> --node <NETWORK> --from user  --gas=auto --gas-prices=1000000000000inj

Executed on testnet:

injectived tx wasm execute inj1aflk2ftzem4emzjmv5ve27af4m3n0ggsz6z45w '{"mint_stable_coin":{"minter":"inj1ph5h805jd4cmgj2jemhgln9ssjkk744ca44avl", "stable_amount": "1000", "collateral_amount": "1000", "collateral_contract": "inj1t4uk4ntn3l0wa7dcqsjl5enenzaaer2alrfmcm"}}' --chain-id injective-888 --node https://testnet.sentry.tm.injective.network:443 --from user  --gas=auto --gas-prices=1000000000000inj 

Link to the transaction in the explorer:

https://testnet.explorer.injective.network/transaction/0x43edfd119a82e0787f7af20e9b88c849a88b6cdfef0950ec6c64964cc2cb98c2/

BVSS
Recommendation

It is recommended to modify the access control by separating the nested "if" into two conditions to avoid being bypassed, or to include more controls regarding the amount of the collateral and the collateral contract after the first access control.

Remediation Plan

SOLVED: The condition if sender_raw == config.custody_contract has been added before storing the collateral information included in the message.

Remediation Hash

7.2 Fragment of code never executed

// Medium

Description

The mint and burn functions of the ve_ninja token contract have some code that is never executed.

The condition if msg_sender.ne(&fund) is repeated twice in both functions: the first time is an access control that, if the condition is met, the transaction is reverted, so the second condition would never be executed (call to RefreshRewards).

In the case of the mint function, this will bypass the vote_config.max_minted check, being able to mint more tokens than the configured maximum.

Repeated condition in mint functionRepeated condition in burn function
BVSS
Recommendation

It is recommended to modify the condition if it is necessary to execute the call to RefreshReward, although it is not strictly necessary since the update_reward function is called at the beginning of the remaining operations.

However, the vote_config.max_minted check should be taken into account.

Remediation Plan

SOLVED: The code fragments that were not executed have been removed. As a result, the max_minted parameter is no longer meaningful, since there are no more places where it is used.

Remediation Hash

7.3 Lack of variable initialization

// Medium

Description

The unstake function of the fund contract allows to unstake and withdraw previously staked tokens.

However, the call to withdraw which is inside the unstake function does not work the first time it is executed since the variable time2fullredemption used in the function get_claim_able_token is not initialized, having the same value as the variable last_withdraw_time_user.

This means that the behavior of the contract is not the same the first time it is executed as it is the following times.

Unstake functionWithdraw functionget_claim_able_token function
BVSS
Recommendation

It is recommended to initialize the variable time2fullredemption before executing the withdraw function to have the same behavior in all executions of the contract. Otherwise, this could have unexpected consequences in the off chain modules that communicate with the contract.

Remediation Plan

NOT APPLICABLE: The Kakeru team have explained that this is the expected behavior:

  1. When the user deposits tokens into the fund contract, they receive ve_tokens.

  2. To redeem tokens, the user first calls unstake. This destroys their ve_tokens and records the start time for linear release. On the first unstake, the withdraw method only records this start time. On subsequent unstakes, the withdraw method settles any previously unwithdrawn tokens.

7.4 Exchange rate not updated properly

// Medium

Description

The execute_bond function of the basset_inj_hub contract allows delegating to the validators a certain amount of staking, as well as to calculate the amount of binj/stinj tokens to be minted in exchange.

At some point in the function, the binj and stinj exchange rates are updated for future transactions, however, the stinj exchange rate is not updated correctly as the update_stinj_exchange_rate function call is only performed if the bond type is BondType::BondRewards, instead of BondType::StInj.

The affected code snippet:

Exchange rate update
BVSS
Recommendation

It is recommended to modify the exchange rate update according to the corresponding bond type.

Remediation Plan

NOT APPLICABLE: The Kakeru team have explained that this is the expected behavior:

When a user bonds new INJ, it does not affect the exchange rate of INJ and stINJ. Only claiming INJ and staking again will affect this exchange rate.

bINJ and stINJ serve different purposes. The target exchange rate of bINJ and INJ is 1:1. If the exchange rate changes due to a slash, a peg fee is needed to restore it to 1:1. The exchange rate is updated in real-time so new users can bond at the previous rate. The goal of stINJ is to increase the exchange rate gradually, allowing users to accumulate staking profits. When a user unbonds stINJ, Check_Slashing is called, and any loss is shared equally among all users.

7.5 Missing slashing check

// Medium

Description

The execute_burn function from the basset_inj_token_binj contract does not call the CheckSlashing entry point of the basset_inj_hub contract like all other Burn calls, leading to an incorrect State value in the Hub contract.

execute_burn functionexecute_burn_from function
BVSS
Recommendation

It is recommended to add the CheckSlashing call to the Hub contract in the execute_burn function to keep the binj exchange rate updated after modifying the total_supply of the token.

Remediation Plan

NOT APPLICABLE: The Kakeru team have explained that this is the expected behavior:

The execute_burn function can only be called by the hub contract and does not require checkSlashing to change the exchange rate, which remains unchanged. The execute_burn_from function will destroy bINJ but does not initiate the unbond operation, so no INJ will be obtained. In this case, checkSlashing will distribute the excess INJ to all bINJ holders.

7.6 Lack of access control in withdraw entry point

// Low

Description

The withdraw function of the fund contract allows withdrawing previously unstaked tokens.

Since this function is used in both internal and external calls, the user entry is needed to specify the user address that receives the withdrawal.

In the case of an external call using the entry point ExecuteMsg::Withdraw, the user input parameter is not checked against the info.sender parameter. Because of this, withdrawal of unlocked tokens can be performed by any user at any time. This means that the legitimate user could lose the opportunity to re-stake if any other user performs the withdrawal using his address.

It is recommended to add some access control for info.sender at the entry point of ExecuteMsg::Withdraw, before calling the withdraw function.

Withdraw entry point
BVSS
Recommendation

It is recommended to add some access control for info.sender at the entry point of ExecuteMsg::Withdraw, before calling the withdraw function.

Remediation Plan

SOLVED: The withdraw entry point has been modified so that it only accepts the call from the user info.sender.

Remediation Hash

7.7 Token loss due to truncation

// Low

Description

The _add_single_user function of the dispatcher contract allows configuring the UserState struct, which stores the information related to a user for claiming future rewards.

In this function, the calculation of user_per_lock_amount parameter is done by an integer division, this means that in the case of a remainder, tokens are not taken into account due to truncation.

The user_claim function does not check in the last period if user_state.claimed_lock_amount is equal to user_state.total_user_lock_amount, so the tokens not considered due to truncation will not be unlocked.

The affected code snippet:

Integer division

It is important to note that the severity of this problem may vary depending on the number of periods configured in the dispatcher contract and the number of decimal places of the claim_token.

Proof of Concept

Integration test using the test functions provided in the code but modifying the dispatcher configuration periods from 25 to 23.

The total amount claimed for user Tom is 3999999989 instead of 4000000000, which is the user's locked amount.

fn test_claim_tokens() {
    let block_time = 1688128676u64;
    let creator = Addr::unchecked(CREATOR);
    let tom_address = Addr::unchecked("tom");
    let mut app = mock_app(creator.clone(), vec![], Some(block_time));

    // init cw20 token
    let cw20_contract_id = store_cw20_contract(&mut app);
    let cw20instance_msg: cw20_base::msg::InstantiateMsg = mock_cw20_instantiate_msg();
    let cw20_token = app
        .instantiate_contract(
            cw20_contract_id,
            creator.clone(),
            &cw20instance_msg,
            &[], // no funds
            String::from("cw20_token"),
            None,
        )
        .unwrap();

    // init dispatcher contract
    let dispatcher_contract_id = store_dispatcher_contract(&mut app);
    let dispatcher_instance_msg: crate::msg::InstantiateMsg =
        crate::testing::mock_fn::mock_instantiate_msg(cw20_token.clone());
    let dispatcher_contact = app
        .instantiate_contract(
            dispatcher_contract_id,
            creator.clone(),
            &dispatcher_instance_msg,
            &[], // no funds
            String::from("dispatcher_contact"),
            None,
        )
        .unwrap();

    // transfer cw20 token to dispatcher contract
    let transfer_amount = Uint128::from(10_000_000_000_000u128);
    let res = transfer_token(
        &creator,
        &dispatcher_contact,
        &mut app,
        &cw20_token,
        transfer_amount,
    );
    assert!(res.is_ok());

    // not add user yet , so can not claim
    let add_user_msg = mock_add_users_msg();
    let res = add_users(&creator, &mut app, &add_user_msg, &dispatcher_contact);
    assert!(res.is_ok());

    app.update_block(|block| {
        block.time = Timestamp::from_seconds(1688828677 + 100u64 + 86400 * 30 * 26);
        block.height += 1000000u64;
    });

    // tom claim
    let res = user_claim(&tom_address, &mut app, &dispatcher_contact);
    assert!(res.is_ok());

    // check tom token balance
    let tom_info_after = query_user_info(&mut app, &dispatcher_contact, &tom_address);

    let res = get_token_balance(&mut app, &cw20_token, &tom_address);
    let tom_token_balance = Uint256::from(res.balance);
    assert_eq!(
        tom_info_after.state.claimed_lock_amount,
        Uint256::from(tom_info_after.current_period) * tom_info_after.state.user_per_lock_amount
    );

    assert_eq!(tom_info_after.state.last_claimed_period, 23);
    assert_eq!(tom_token_balance, tom_info_after.state.claimed_lock_amount);
  
    assert_eq!(
        tom_info_after.state.last_claimed_period,
        tom_info_after.current_period
    );

    assert_eq!(
        tom_info_after.state.claimed_lock_amount,
        Uint256::from(4_000_000_000u128)
    );
}
BVSS
Recommendation

It is recommended to modify the calculation of user_per_lock_amount to take into account the remainder in case of non-exact division. This could be done by changing integers to decimals in the calculations, or by including the remaining amount in the last period.


Remediation Plan

SOLVED: The issue has been solved since the entire dispatcher contract has been removed from the workspace.

Remediation Hash

7.8 Lack of admin access control

// Low

Description

The register_contracts function does not have an access control associated with an admin/owner address, instead it relies on the situation where the contracts are not yet configured.

Although the probability of accessing the market contract right after instantiation when the contracts are not configured is low, there is a minimal chance that an attacker could replace the contracts with malicious ones.

The vulnerable code:

register_contract function
BVSS
Recommendation

It is recommended to include an access control associated with the owner/administrator's address, as this operation cannot be undone.

Remediation Plan

SOLVED: An access control has been added to verify that only config.owner_addr can execute this entry point.

Remediation Hash

7.9 No time validations

// Low

Description

There are some time-related parameters that should be checked if they are not set in the past to avoid erroneous calculations.

This concerns the following parameters:

  • msg.start_lock_period_time in the update_config function of the dispatcher contract.

  • rule_msg.start_linear_release_time in the add_rule_config function of the distribute contract.

update_config function from Dispatcher contractadd_rule_config function from Distribute contract
BVSS
Recommendation

It is recommended to check if the start time parameters are not set in the past before saving them, in order not to distort the calculations.

Remediation Plan

SOLVED: The dispatcher contract has been deleted and the rule_msg.start_linear_release_time from the distribute contract is checked before storing it.

Remediation Hash

7.10 The re-stake function is partially suppressed

// Low

Description

The re_stake functionality of the fund contract is partially suppressed due to the withdrawal included in the unstake function.

Since the tokens are automatically withdrawn in the same transaction as the unstake operation, there is no possibility to execute the re-stake of those tokens because the get_claim_able_token function will return zero amount.

The only amount that could be re-staked is the reserved tokens pending of withdrawal that are calcualted by the get_reserved_token_for_vesting.

re-stake function from fund contract
BVSS
Recommendation

It is recommended to separate the withdraw function from the unstake operation in order to give the possibility to the user of re-staking those tokens.

Remediation Plan

NOT APPLICABLE: The Kakeru team have explained that this is the expected behavior:

When an unstake operation occurs, the claimable portion is automatically sent to the user. This is by design.

When re_stake operates, the value returned by get_claim_able_token depends on the time elapsed since the last unstake operation. Since unstake releases tokens linearly, the value of get_claim_able_token increases over time. Thus, the re_stake operation will re-stake both released and yet-to-be-released tokens.

7.11 Lack of validation could block the tokens

// Low

Description

The instantiate and add_rule_config functions of the distribute contract allow creating different rules for distributing Ninja tokens to users.

There are no validations on the three amounts configured in a rule: rule_total_amount, start_release_amount and unlock_linear_release_amount.

It should be checked that rule_total_amount = start_release_amount + unlock_linear_release_amount⁣. Otherwise, if the claim is made after the end time, it will not be possible to claim any token as the claimable amount would be higher than the total and the transaction will be reverted.

Code in which the check is missing


BVSS
Recommendation

It is recommended to check that rule_total_amount = start_release_amount + unlock_linear_release_amount in the affected functions to avoid tokens being permanently locked for the user.

Remediation Plan

SOLVED: The validation rule_total_amount = start_release_amount + unlock_linear_release_amount has been added to the instantiate and add_rule_config functions.

Remediation Hash

7.12 Optional input could lead to failure in execution

// Informational

Description

The execute_dispatch_rewards function from basset_inj_rewards_dispatcher contract executes the following call to the UpdateGlobalIndex entry point of the basset_inj_reward contract:

Entry point call

However, the format of the entry point in the basset_inj_reward contract is the following:

UpdateGlobalIndex execution message

The airdrop_hooks parameter included in the first call would cause a failure in case the optional value was other than None, reverting the entire transaction from the UpdateGlobalIndex entry point of the basset_inj_hub contract. This would also cause a failure in the redelegations entry point of the basset_inj_validator_registry contract.

Since at the moment all airdrop_hooks values are hardcoded as None or are in commented code fragments, the criticality has been reduced to Informational.

BVSS
Recommendation

It is recommended to modify the UpdateGlobalIndex message input in the basset_inj_reward contract if a value of airdrop_hooks other than None is to be used in the future.

Remediation Plan

ACKNOWLEDGED: The Kakeru team has acknowledged this issue.

7.13 Wrong implementation

// Informational

Description

The implementation of the remove_validator function is almost the same as the redelegations function, but with some incorrectness such as not making the call to UpdateGlobalIndex.

Since the redelegations function is complete and correct, the code of the remove_validator function can be trimmed, leaving only the remove operation in the REGISTRY map.

Score
Recommendation

It is recommended to modify the remove_validator function leaving only the REGISTRY removal operation to save some gas.

Remediation Plan

SOLVED: Finally, the Kakeru team has decided to add the UpdateGlobalIndex call to the remove_validator function, so the redelegations function (Redelegate Proxy entry point) remains as a complementary measure.

Remediation Hash

7.14 No address validation

// Informational

Description

As a general good practice, it is always recommended to validate addresses entered in contracts to avoid unexpected errors.

The lack of address validation affects the following parameters:

Address parameter

Function

Contract

claim_token

instantiate

token-contracts/ dispatcher

user_msg.user

add_single_user

token-contracts/ dispatcher

msg.gov

distribute_token

distribute_ve_token

instantiate

token-contracts/ distribute

distribute_token

distribute_ve_token

update_config

token-contracts/ distribute

rule_owner

update_rule_config

token-contracts/ distribute

msg.gov

instantiate

token-contracts/

ninja

msg.gov

lock_token

punish_receiver

instantiate

token-contracts/

treasure

msg.gov

instantiate

token-contracts/

ve_ninja

fund

update_config

token-contracts/

ve_ninja

msg.gov

msg.staking_token

msg.rewards_token

msg.boost

msg.fund

msg.reward_controller

instantiate

token-contracts/

staking

msg.gov

msg.token

instantiate

token-contracts/

fund

Score
Recommendation

It is recommended to validate the addresses entered the contracts using the deps.api.addr_validate() function or by converting the address to CanonicalAddr type.

Remediation Plan

SOLVED: The issue has been solved for the following addresses:

Address parameter

Function

Contract

Commit

claim_token

instantiate

token-contracts/ dispatcher

50efde4fe2a66b5fc3546ce41b64d47b52d5b27f

user_msg.user

add_single_user

token-contracts/ dispatcher

50efde4fe2a66b5fc3546ce41b64d47b52d5b27f

msg.gov

distribute_token

distribute_ve_token

instantiate

token-contracts/ distribute

eedeb6ce165db2a6d32a0ffb05923efa9d51b470

distribute_token

distribute_ve_token

update_config

token-contracts/ distribute

eedeb6ce165db2a6d32a0ffb05923efa9d51b470

rule_owner

update_rule_config

token-contracts/ distribute

eedeb6ce165db2a6d32a0ffb05923efa9d51b470

msg.gov

instantiate

token-contracts/

ninja

eedeb6ce165db2a6d32a0ffb05923efa9d51b470

msg.gov

lock_token

punish_receiver

instantiate

token-contracts/

treasure

eedeb6ce165db2a6d32a0ffb05923efa9d51b470

msg.gov

instantiate

token-contracts/

ve_ninja

eedeb6ce165db2a6d32a0ffb05923efa9d51b470

fund

update_config

token-contracts/

ve_ninja

eedeb6ce165db2a6d32a0ffb05923efa9d51b470

msg.gov

msg.staking_token

msg.rewards_token

msg.boost

msg.fund

msg.reward_controller

instantiate

token-contracts/

staking

eedeb6ce165db2a6d32a0ffb05923efa9d51b470

msg.gov

msg.token

instantiate

token-contracts/

fund

eedeb6ce165db2a6d32a0ffb05923efa9d51b470


Remediation Hash

7.15 Entry point not needed

// Informational

Description

The SwapToRewardDenom entry point of the basset_inj_reward (staking) contract can only be called from the basset_inj_rewards_dispatcher contract, but there is no call to this entry point in that contract, so it is not needed.


Unnecesary entry point
Score
Recommendation

It is recommended to eliminate the unnecessary entry point.

Remediation Plan

SOLVED: The SwapToRewardDenom entry point has been set to Deprecated, generating an Error in case it is called.

Remediation Hash

7.16 Repeated condition

// Informational

Description

The if condition inside execute_convert_to_basset function from basset_converter contract uses the same condition on both sides of the OR operator.

Repeated condition
Score
Recommendation

It is recommended to eliminate the repeated conditions for code hygiene.

Remediation Plan

SOLVED: The repeated condition config.native_denom.is_none() has been replaced by config.basset_token_address.is_none().

Remediation Hash

7.17 No error handling

// Informational

Description

There is no validation of amount < balance_of in the withdraw function of the staking contract. In case of insufficient balance the code will panic without any error handling.

withdraw function
Score
Recommendation

It is recommended to check if amount < balance_of and handle the error if this condition is met.

Remediation Plan

SOLVED: The error is handled correctly.

Remediation Hash

7.18 Incorrect claimable amount calculation

// Informational

Description

The parameter rule_state.last_claim_linear_release_time used in the claim function is not updated after the claim is performed, but is used in the query_claimable_info function to calculate the claimable amount.

This means that the diff_time will always be calculated from the start_linear_release_time, rather than from the point in time at which the last claim was made. Since the previously claimed amount is subtracted from the claimable one, this does not have a major impact on the calculations.

However, it is recommended to refactor the code and remove the unused parameter rule_state.last_claim_linear_release_time.

Affected code snippet of claim function
Score
Recommendation

It is recommended to refactor the code and remove the unused parameter rule_config_state.last_claim_linear_release_time.

Remediation Plan

SOLVED: The query_claimable_info query has been refactored and the rule_config_state.last_claim_linear_release_release_time parameter has been correctly updated.

Remediation Hash

7.19 Set token metadata during instantiation

// Informational

Description

The stable_pool contract creates a new stable currency during instantiation, however, the metadata configuration is performed in the separate function set_token_metadata, which has its own entry point, thus completing the creation of the stable currency in two transactions.


Score
Recommendation

It is recommended to call the set_token_metadata function during instantiation to avoid a mint call without configured metadata.

Remediation Plan

ACKNOWLEDGED: The Kakeru team has acknowledged this issue.

7.20 Useless contract

// Informational

Description

The treasure contract performs token locking/unlocking operations, but there is no reward in return, so it does not make sense from a business point of view.

Score
Recommendation

It is recommended to review the purpose of the treasure contract and whether it makes sense from a business perspective for the user.

Remediation Plan

SOLVED: The treasure contract has been deleted.

Remediation Hash

7.21 Parameter not used

// Informational

Description

In the add_validator function of the basset_inj_validator_registry contract, the hub_address parameter is not needed since there is no call to the AddValidator entry point in the basset_inj_hub contract.

Unnecessary parameter
Score
Recommendation

It is recommended to eliminate those parameters that are not used for code hygiene.

Remediation Plan

SOLVED: The unused hub_address parameter has been removed from the access control. The variable has been deleted in the commit 8e7f0fce4708f7fbfc27fd6e7609f4d65f6cbe05

Remediation Hash

8. Automated Testing

Halborn used automated security scanners to assist with detection of well-known security issues and vulnerabilities. Among the tools used was cargo audit, a security scanner for vulnerabilities reported to the RustSec Advisory Database. All vulnerabilities published in https://crates.io are stored in a repository named The RustSec Advisory Database. cargo audit is a human-readable version of the advisory database which performs a scanning on Cargo.lock. Security Detections are only in scope. To better assist the developers maintaining this code, the auditors are including the output with the dependencies tree, and this is included in the cargo audit output to better know the dependencies affected by unmaintained and vulnerable crates.

Contracts

ID

Package

Description

swap-extension

staking

oracle

market

cdp

basset-convert

RUSTSEC-2020-0025

bigint 4.4.3

Unmaintained

staking

market

cdp

RUSTSEC-2024-0012

serde-json-wasm 0.5.1

Stack overflow during recursive JSON parsing

staking

RUSTSEC-2020-0168

mach 0.3.2

Unmaintained

cdp


ahash 0.7.6

Yanked

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.