Logo: N.A.
Name: B9 Claim Rewards Distributor Contract
Ticker Symbol: N.A.
Network: Pulsechain
Contract Address: 0x40b5D3abCe7e49aec639d0c19F77FAb491430714
Purpose: A smart contract designed to distribute $B9 tokens to eligible claimers
Project URL: https://b9.xyz/events-free-claim-mechanics/
Project social media: N.A.
Contract Source Code: N.A.

REVIEWED BY: CaptainBurningWater

Contract Name: B9RewardsDistributor

Review Date: 01/11/2023

Reviewer: Captain Burning Water

Contract Purpose and Scope:

The B9RewardsDistributor is a smart contract designed to distribute rewards in B9 tokens to eligible claimers. It includes features for depositing and withdrawing tokens, creating new claim versions, and allowing claimants to claim their rewards using a Merkle proof.

Security Considerations:

  1. Reentrancy: Doesn’t appear to have functions susceptible to traditional reentrancy attacks.
  2. Access Control: The contract uses the onlyOwner modifier to restrict access to the owner for critical functions.
  3. Data Validation: There are no direct vulnerabilities related to data inputs, apparent in the contract.
  4. External Calls: The contract interacts with external dependencies, including the MerkleProof library and the IERC20 interface, which represents an ERC-20 token. These interactions are generally safe because these are well-established and widely-used libraries and interfaces in the Ethereum ecosystem.
  5. Error Handling: The contract does include specific safety measures to handle potential errors or exceptions with clear error messages.
  6. Delegatecall and Call: there are no direct uses of delegatecall or call.
  7.  Events and Logs: Overall, it appears that the contract uses events and logs correctly to provide important information about contract actions.

Code Review:

  1. Modifiers: The onlyOwner and claimPhaseOver modifiers appear to be correctly implemented and used where needed.
  2. Owner Functions: The owner has the ability to deposit and withdraw tokens, transfer ownership, create new claim versions and burn tokens.
  3. Claim Functions: The claim function allows claimants to claim their rewards using Merkle proofs.
  4. Claim Amount Calculation: The _claimAmount function calculates the claim amount based on the current day.
  5. Claim Day Calculation: The _claimDay function calculates the current claim day based on the current day value.
  6. Create New Claim: With the createNewClaim the owner can create new claim versions with updated parameters for subsequent claim phases.
  7. Burn Remaining Tokens: With the burnRemainingTokens if there are tokens left in the contract after the claim phase, the owner can choose to burn them, effectively destroying those tokens.


  1. Safety Considerations: The contract does include specific safety measures to handle potential errors or exceptions making use of requires.
  2. External Dependencies: The contract relies on external dependencies such as the OpenZeppelin MerkleProof library and the IERC20 interface.

Code implementation notes:

  • The IERC20 can’t be imported due the fact of the addition of the burn function (not in the original implementation) used in burnRemainingTokens(). That it is an essential part of this contract to burn tokens after the claim phase.



In conclusion, the B9RewardsDistributor contract appears to be well-structured, following best practices for smart contract development. It aims to facilitate the distribution of rewards in B9 tokens to eligible claimers. The contract incorporates a range of features and safeguards, but it’s important to maintain a comprehensive understanding of its functionality and security considerations.

Here are some key takeaways from the review of the B9RewardsDistributor contract:

  1. Security: The contract demonstrates security-conscious coding practices, such as access control with an owner modifier, use of require statements for input validation, and appropriate error handling.
  2. Transparency: The contract uses events to emit information about various contract activities, providing transparency and enabling external parties to monitor contract actions.
  3. External Dependencies: The contract interacts with external dependencies like the MerkleProof library and an ERC-20 token contract, which is a common and secure practice. However, it’s crucial to ensure that these dependencies are well-audited and trusted.
  4. Claim Mechanism: The contract includes a claim mechanism that calculates rewards based on the current day, and it employs a Merkle root hash for claim verification, which is a standard approach for distributing rewards.
  5. Error Handling: The contract effectively employs require and revert statements with informative error messages, enhancing usability and aiding in debugging.

Overall, the B9RewardsDistributor contract appears to be well-designed and secure.




This is a complete review of the B9RewardsDistributorPct smart contract whose code can be found at https://scan.mypinata.cloud/ipfs/bafybeidn64pd2u525lmoipjl4nh3ooa2imd7huionjsdepdsphl5slfowy/#/address/0x40b5D3abCe7e49aec639d0c19F77FAb491430714?tab=contract.

Although the code is not overly complex, I would advise anyone who is reading this document to also look at other reviews and analyses of the contract! Some issues or security concerns might not be covered here due to my lack of knowledge or understanding of the contract. Getting a second opinion is always a good thing to keep in mind!


This smart contract is designed to distribute rewards, in the form B9 tokens, to eligible users, based on a provided Merkle Root. (B9 contract – 0xE676a1E969Feaef164198496bd787e0269f7b237).

The amount of claimable tokens is based on the amount of B9 tokens addresses held at the time of the snapshot. They can be claimed during a 14-day period, but the claimable amount decays linearly daily.

After each claim phase ends, the contract owner can burn all unclaimed tokens and start a new claiming phase, either with the same parameters or new ones. The owner must also deposit enough B9 in the contract before each claiming phase so users can successfully claim them.

As it stands, the contract can be used for multiple claiming phases (65,536 to be exact), and the code has been optimized to be as gas-efficient as possible.

The contract also features functions that allow the contract owner to withdraw from the contract any ETH or ERC20 tokens sent to it by mistake, making it so no funds will ever be mistakenly lost by being sent directly to the contract’s address.

On the other hand, any B9 tokens that are sent to the contract cannot be withdrawn and will be burned every time the burnRemainingTokens function is run.

Code Review

The B9RewardsDistributorPct smart contract manages a reward system where users can claim a percentage of B9 tokens based on their holdings, verified through a Merkle proof. It includes functions for managing the claim phases, token deposits, and withdrawals, along with safeguards and error handling.

The contract is designed to be owner-controlled with the ability to transition ownership, update claim parameters when creating new claiming phases, and handle unexpected token deposits.

  • Dependencies:
  • IERC20: @openzeppelin/contracts/token/ERC20/IERC20.sol – Standard interface implemented by all ERC-20 tokens. Allows the contract to call functions shared by all ERC-20 tokens, namely the balanceOf, transferFrom, and transfer functions
  • IERC20WithBurn: Interface created to simply add the burn function to IERC20 since the B9 token implements ERC20Burnable, hence we know it supports it.
  • MerkleProof: (@openzeppelin/contracts/utils/cryptography/MerkleProof.sol) – Library used for the verification of Merkle Tree proofs. Allows the verify function from this library to be used to confirm that the users are eligible to claim.

These are well-known and widely used for smart contract development, which reduces the risk of common vulnerabilities and security concerns.

  • Constants:
    • DECIMAL_MULTIPLIER: Number of decimal places = 1018.
    • PERCENT_MULTIPLIER: Number of decimal places divided by 100 for percentage calculations = 1016.
    • MAX_CLAIM_DAYS: The maximum claim days = 14.
    • B9_TOKEN_ADDRESS: The B9 token address (different for Test vs Main Blockchain).

  • Private State Variables:
    • s_owner:
      • The contract owner, initially the deployer.
    • s_claimVersion:
      • Tracks the current claim version. Starts at 0 and can go up to 65535.
    • s_claimStartDate:
      • The Start Date for the current claim phase.
    • s_claimPercentage:
      • Percentage of claimable B9 tokens based on the user’s snapshotted amount of tokens held.
      • Starts at 69% for the first claim phase and can be updated for the following claim phases.
    • s_B9TokenBalance:
      • Balance of B9 tokens held by the contract that is deposited via the depositTokens
      • Tokens sent directly to the contract’s address are not accounted for here.
    • s_claimMerkleRoot:
      • Merkle root hash for claim verification. It will be updated on every new claim phase.
    • s_isClaimed:
      • Mapping which tracks an address’ claim status for each claiming phase.

All state variables (except the mapping) are private for gas efficiency and the contract provides getters for all of them. When possible, the contract packs storage variables in the same memory slot to be more efficient when updating them.

  • Custom Errors:
    • NotOwner:
      • Thrown when the caller is not the owner of the contract for onlyOwner
    • ZeroAddressCannotBeOwner:
      • Thrown in the transferOwnership function to prevent transferring ownership to the zero address.
    • ClaimPhaseOngoing:
      • Thrown in functions that should only be executed after the claim phase ends, such as burnRemainingTokens and createNewClaim, to ensure they are not called prematurely.
    • ClaimPhaseNotStarted:
      • Thrown in the _claim function when users try to claim tokens before the claim phase begins.
    • ClaimPhaseEnded:
      • Thrown in the _claim function when users try claiming tokens after the claim phase is over.
    • AmountMustBePositive:
      • Thrown in the depositTokens function when the amount of tokens being deposited is not positive.
    • DepositB9TokensFailed:
      • Thrown in the depositTokens function to signal a failure in transferring B9 tokens from the user to the contract.
    • InvalidClaimPercentage:
      • Thrown in the createNewClaim function when the claim percentage is not within the valid range (1 to 100).
    • InsufficientFundsInTheContract:
      • Thrown in various functions like _claim, withdrawAllEthTo, and withdrawTokensTo, when there are insufficient funds in the contract for the desired operation.
    • AlreadyClaimed:
      • Thrown in the _claim function when a user tries to claim more than once per claim phase.
    • InvalidClaimData:
      • Thrown in the _claim function when the provided claim data is invalid or does not match the Merkle proof.
    • FailedToTranferClaimedTokens:
      • Thrown in the _claim function to handle the case where the transfer of B9 tokens to the claimer fails.
    • WithdrawEthFailed:
      • Thrown in the withdrawAllEthTo function to handle failures in sending ETH to the specified recipient.
    • WithdrawTokenFailed:
      • Thrown in the withdrawTokensTo function to handle failures in transferring ERC20 tokens to the specified recipient.
    • B9TokensCannotBeWithdrawn:
      • Thrown in the withdrawTokensTo function when trying to withdraw B9 tokens from the contract.

The custom errors in the contract provide descriptive and gas-efficient ways to handle exceptional conditions.

  • Events:
    • B9TokensDeposited:
      • Emitted when the owner deposits B9 tokens in the contract.
    • B9TokensBurned:
      • Emitted when the owner burn all existing B9 tokens in the contract.
    • B9TokensClaimed:
      • Emitted when a user successfully claims tokens.
    • ETHWithdrawn:
      • Emitted when the owner withdraws ETH mistakenly sent to the contract.
    • TokenWithdrawn:
      • Emitted when the owner withdraws ERC20 tokens (except B9) mistakenly sent to contract.

The Constructor, on deployment, initializes the contract, setting the contract’s owner as the deployer and starting the first claiming phase with a given Merkle Root.

  • Modifers:
    • onlyOwner:
      • Restricts function access to the contract owner.
    • claimPhaseOver:
      • Restrict function access to after the claim phase has ended.
  • Owner Only Functions:
    • transferOwnership:
      • Transfers contract ownership to a new owner.
    • depositTokens:
      • Allows the owner to deposit B9 tokens from his wallet into the contract.
      • Deposited amount must be positive.
      • The owner must have previously approved the B9 tokens spending.
    • burnRemainingTokens:
      • Allows the owner to burn all remaining B9 tokens in the contract after a claim phase ends.
      • This will also burn B9 tokens directly sent to the contract (not deposited)
    • createNewClaim:
      • Allows the owner to initiate a new claim phase with updated parameters.
      • Guarantees the received claimPercentage is within valid intervals.
      • Only possible after the current claim phase is over.
    • withdrawAllEthTo:
      • Allows the owner to withdraw ETH mistakenly sent to the contract to any address.
    • withdrawTokensTo:
      • Enables the owner to withdraw any ERC20 mistakenly sent tokens sent to the contract, except B9 tokens, to any address.
      • Uses the _checkReceivedTokenBalance to check the balance of the specified ERC20.
  • External Functions:
    • claim:
      • Allows users to claim their rewards using a Merkle proof to verify the provided data.
      • Uses the _claim function for the actual logic.
    • claimAmount:
      • View function that calculates the claimable amount tokens based on the current claim day.
      • Uses the _claimAmout function for the actual calculation.
    • claimDay:
      • View function that returns the current claim day for the ongoing claim version.
    • checkReceivedTokenBalance:
      • View functions that exposes the _checkErc20TokenBalance function to check the balance available for withdrawal of a given ERC20 token.
  • Internal Functions:
    • _claim:
      • Handles the distribution of claimable tokens.
      • Responsible for all the needed verifications, including the Merkle Proof validation via the _verify function, reverting the transaction if necessary with the correct error.
      • Updates s_B9TokenBalance and s_isClaimed based on the when the claim is successful.
    • _today:
      • Returns the current day based on the block timestamp.
      • Will always return the current’s day timestamp at 00:00.
    • _verify:
      • Verifies a Merkle Proof based on the received parameters. Returns true if valid.
    • _claimAmount:
      • Calculates the amount of B9 tokens to be claimed based on the current claim day.
      • The formula is equivalent to: UserAmount x ClaimPercentage * (1 – DaysSinceStart / 14);
    • _checkReceivedTokenBalance:
      • Checks the contract’s balance for a given ERC20 token.


This contract aims to be a simple way for B9 tokens to be distributed to users, based on a predetermined set of eligible addresses, via a claiming function. The way the contract checks eligibility by using a Merkle Root is very common, and it uses well-known and widely used code.

Also, although the contract has an Owner, this poses no risk whatsoever to the end user and those who run the claim. The permissions he has simply allow him to manage its own tokens and decide when and how he will allow the broader community to get them!

No bugs or other risks were found, and the codes seems to function as intended.


Scroll to Top