Sakai Vault security record

As of 3 Nov 2026, Sakai Vault has 4 audit reports by 2 security firms on Trustblock; the latest is by CertiK, dated 15 Jul 2024. Those reports list no open critical findings. It also holds 8 deployed contracts on 1 chain.

Critical findings
No open critical findings
Across the 4 reports, 2 critical findings were reported. Each is marked fixed by the firm that reported it. See the findings
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.

Audits4

Newest first

AuditSecurity firmDateScopeCritical findingsReport
SakaiDAO - AuditSecurity firm: CertiKDate: Scope: 1 address on BNB Chain2 critical findings, fixedOpen · skynet.certik.com
Sakaivault - AuditSecurity firm: CertiKDate: Scope: 4 addresses on BNB ChainNo open critical findingsOpen · skynet.certik.com
SakaiDAOSecurity firm: SolidProofDate: Scope: 1 address on BNB ChainNo open critical findingsOpen · IPFS
Sakai VaultSecurity firm: SolidProofDate: Scope: 2 addresses on BNB ChainNo open critical findingsOpen · IPFS

Findings41

Statuses are the security firm’s own, from each report.

SeverityOpenAcknowledgedFixed
Critical002
High060
Medium136
Low1706
CriticalDuplicate CalculationSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

According to the logic of the function `_calculateReward()`, new reward will be accumulated to the `pendingCalculateReward`. Once new epoch is triggered, the pending reward will be distributed to each share. ```solidity function _calculateReward(uint256 rewardAmount) internal { /** this function for calculate reward and count dividendsPerShare */ pendingCalculateReward += rewardAmount; if(totalShares > 0 && (lastResetEpochAt + resetEpochEvery <= block.timestamp)){ uint256 amount = pendingCalculateReward; totalDividends += amount; dividendsPerShare += (dividendsPerShareAccuracyFactor * amount / totalShares); currentEpoch++; lastResetEpochAt = block.timestamp; pendingCalculateReward = 0; } else { pendingCalculateReward += rewardAmount; } } ``` However, the pending reward is added twice if new epoch is not triggered.

Open the report
CriticalInsufficient Rewards due to Tax DeductionSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

According to the logic of the function `_claimReward()`, a reward won't be sent to the claimer if there isn't enough rewards in the contract. However, a certain amount of tax had been deducted and distributed to the staking pool and vault proportionally. ```solidity function _claimReward(address account) internal { /** this function for claim reward */ require(account != address(0), "SakaiDAO: account is the zero address"); require(shares[account].amount > 0, "SakaiDAO: account is not stake"); uint256 originalAmount = dividendOf(account); uint256 amount = _deductTaxClaimReward(originalAmount); if(IERC20(tokenAddress).balanceOf(address(this)) >= amount){ IERC20(tokenAddress).transfer(account, amount); _setClaimed(account, originalAmount); } } ``` If the actual reward transferred to the contract is less than the accumulated reward obtained by the referrer, then when the referrer claims the reward, the token amount in the contract will become very low. The above-mentioned scenario will occur, resulting in the incorrect transfer of tokens from the contract to the staking pool and vault, causing losses for the users. For example, when claiming the reward, if the condition `IERC20(tokenAddress).balanceOf(address(this)) >= amount` is not met, the user will not get the reward, but a portion of the reward has been distributed to the staking pool and vault. Therefore reducing the rewards for the user. In an extreme case, users may repeatedly claim the reward but not receive any rewards. Instead, the reward tokens will all be distributed to the staking pool and vault.

Open the report
MediumIncorrect Key OrderSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

According to the code logic of the function `_stake()`, referrer rewards will be calculated to different layer referrer proportionally. State variable `referrerTotalAmountByAccount` is used to record how many rewards are earned by referrer through inviting staker. ```solidity if(referrerLayer3 != address(0)){ uint256 amountLayer3 = amount * percentDistributionForReferrerLayer3 / percentDenominator; rewardsFromReferrer[referrerLayer3] += amountLayer3; referrerPendingAmount[referrerLayer3] += amountLayer3; referrerTotalAmountByAccount[referrerLayer3][account] += amountLayer3; totalAccumulateReferrerAmount += amountLayer3; totalPendingReferrerAmount += amountLayer3; } ``` However, the mapping key order is incorrect when calculating rewards for layer3 referrer. The right mapping key should be the referred person and the referrer which is `[account][referrerLayer3]`.

Open the report
MediumIncorrect Ratio of Referrer RewardSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

The `require()` check in the function `updatePercentDistributionForReferrer()` requires the total proportion of `_layer1`, `_layer2` and `_layer3` should equal to `percentDenominator`. According to the calculation logic for referral rewards in the function `_stake()`, this means the referrer rewards equal to the users' staked amount which seems to be very high. ```solidity function updatePercentDistributionForReferrer( uint256 _layer1, uint256 _layer2, uint256 _layer3 ) external onlyOwner { require( _layer1 + _layer2 + _layer3 == percentDenominator, "SakaiDAO: Maximum Referrer should 100%" ); percentDistributionForReferrerLayer1 = _layer1; percentDistributionForReferrerLayer2 = _layer2; percentDistributionForReferrerLayer3 = _layer3; emit UpdatePercentDistributionReferrer(_layer1, _layer2, _layer3); } ``` Besides, the initial sum of these three percentage is 6% which is inconsistent with the `require` check.

Open the report
MediumIncorrect Reward AddedSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

According to the code logic of the function `_stake()`, referrer rewards will be calculated to different layer referrer proportionally. ```solidity if(referrerLayer2 != address(0)){ uint256 amountLayer2 = amount * percentDistributionForReferrerLayer2 / percentDenominator; rewardsFromReferrer[referrerLayer2] += amountLayer1; referrerPendingAmount[referrerLayer2] += amountLayer2; referrerTotalAmountByAccount[account][referrerLayer2] += amountLayer2; totalAccumulateReferrerAmount += amountLayer2; totalPendingReferrerAmount += amountLayer2; } ``` However, the correct new added reward should be `amountLayer2` but not `amountLayer1` when calculating reward for `referrerLayer2`.

Open the report
MediumPotential Risk Of Losing RewardsSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

In the **sakaiDAO** contract, when a user stakes or unstakes, the contract updates the user's shares through the `_setShares()` function and calculates the user's accumulated dividends with the latest shares. This part is considered as already claimed rewards. ```solidity function _setShare(address account, uint256 amount) internal { ... totalShares = totalShares - shares[account].amount + amount; shares[account].amount = amount; shares[account].totalExcluded = _getCumulativeDividend(shares[account].amount); ... } function _getCumulativeDividend(uint256 share) internal view returns (uint256) { /** this function for calculate cumulative dividend */ return share * dividendsPerShare / dividendsPerShareAccuracyFactor; } ``` However, before the user performs the operation, `dividendsPerShare` might change. If the user doesn't call the `claimReward()` function to receive rewards before staking or unstaking, the user will lose these part of rewards.

Open the report
MediumSending Incorrect Amount When Claiming ReffererSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

According to the design, we believe that `percentTaxClaimReferrer` represents the fee charged when the referrer claims rewards. This fee will be distributed among all stakers, and the remaining rewards, after deducting the fee, will be transferred to the referrer. However, the contract transfers the fee to the referrer, and the remaining rewards are distributed to all stakers. ```solidity function _claimRewardReferrer(address account) internal { ... uint256 amountAfterTax = amount * percentTaxClaimReferrer / percentDenominator; uint256 amountForReferrer = amount - amountAfterTax; IERC20(tokenAddress).transfer(account, amountAfterTax); _calculateReward(amountForReferrer); emit ReceiveRewardReferrer(account, amountAfterTax, currentEpoch); } ```

Open the report
MediumLack of Sanity CheckSakaivault - Audit · CertiK · 26 Jun 2024Fixed

The `withdraw()` function can be called by anyone to retrieve the tokens they deposit into the contract, but there is no check if the variable `_stakers[msg.sender].balance` is greater than 0. If someone whose balance is equal to 0 calls this method multiple times, then `totalAddressStaked` will keep decreasing and other users will not be able to retrieve tokens, and the tokens will be locked into the contract.

Open the report
LowLack of Sanity ChecksSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

In the contract **SakaiDAO**, the `_percentTaxClaimReferrer` lacks of sanity checks. It is better to add a reasonable range check for `_percentTaxClaimReferrer` to avoid potential risks caused by incorrect input.

Open the report
LowPrecision Loss When Distributing Tax RewardSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

The function `_distributeTaxClaimReward()` is used to distribute tax to staking pool and vault proportionally. However, there may be a precision loss issue if calculate related values by multiplying each ratio. ```solidity function _distributeTaxClaimReward(uint256 taxAmount) internal { /** * 1. Distribute tax to staking pool * 2. Distribute tax to vault capital */ uint256 totalTax = percentTaxForStakingPool + percentTaxForVaultCapital; uint256 amountForStakingPool = taxAmount * percentTaxForStakingPool / totalTax; IERC20(tokenAddress).transfer(addressStakingPool, amountForStakingPool); uint256 amountForVaultCapital = taxAmount * percentTaxForVaultCapital / totalTax; IERC20(tokenAddress).transfer(addressVaultCapital, amountForVaultCapital); } ``` It means there is a small amount of tokens will be locked in the current contract.

Open the report
Show 4 more
LowUnchecked ERC-20 `transfer()`/`transferFrom()` CaSakaiDAO - Audit · CertiK · 15 Jul 2024Fixed

The return values of the `transfer()` and `transferFrom()` calls in the smart contract are not checked. Some ERC-20 tokens' transfer functions return no values, while others return a bool value, they should be handled with care. If a function returns `false` instead of reverting upon failure, an unchecked failed transfer could be mistakenly considered successful in the contract. ```solidity=550 IERC20(tokenAddress).transfer(account, amount); ``` --- ```solidity=564 IERC20(tokenAddress).transfer(account, amountAfterTax); ``` --- ```solidity=603 IERC20(tokenAddress).transfer(addressStakingPool, amountForStakingPool); ``` --- ```solidity=606 IERC20(tokenAddress).transfer(addressVaultCapital, amountForVaultCapital); ``` --- ```solidity=699 IERC20(tokenAddress).transferFrom(_msgSender(), address(this), amount); ``` --- ```solidity=759 IERC20(tokenAddress).transfer(_msgSender(), amountAfterTax); ``` --- ```solidity=795 IERC20(token).transfer(msg.sender, IERC20(token).balanceOf(address(this))); ``` --- ```solidity=860 IERC20(tokenAddress).transferFrom(_msgSender(), address(this), rewardAmount); ```

Open the report
LowPotential Flashloan Attack to Bypass Minimum StakSakaivault - Audit · CertiK · 26 Jun 2024Fixed

Flash loans are a way to borrow large amounts of money for a certain fee. The requirement is that the loans need to be returned within the same transaction in a block. If not, the transaction will be reverted. An attacker can use the borrowed money as the initial funds for an exploit to enlarge the profit or manipulate the token price in decentralized exchanges. The `getAmountsOut()` function relies on price calculations that are based on-chain, meaning they are susceptible to flash-loan attacks by manipulating the price of given pairs to the attacker's benefit.

Open the report
LowPotential Reentrancy AttackSakaivault - Audit · CertiK · 26 Jun 2024Fixed

A reentrancy attack can occur when the contract creates a function that makes an external call to another untrusted contract before resolving any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the external call resolved the effects. ### External call(s) ```solidity=1658 ERC20token.safeTransfer(msg.sender, balance); ``` ### State variables written after the call(s) ```solidity=1663 delete _stakers[msg.sender]; ```

Open the report
LowVariable Can Be Declared as ImmutableSakaivault - Audit · CertiK · 26 Jun 2024Fixed

The variable `minimumStakeEqualtoUSDT`, `interestRate`, and `interestRateDenominator` assigned in the constructor can be declared as `Immutable`. Immutable state variables can be assigned during contract creation but will remain constant throughout the lifetime of a deployed contract. An advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since they are not in storage. ```solidity=1358 uint256 public minimumStakeEqualtoUSDT; uint256 public interestRate; uint256 public interestRateDenominator; ```

Open the report

Contracts8

AddressChainSource code
0x6729324119a210cf493fa7455e581df9245d1609Chain: BNB ChainSource code: Verified source
0xb8de9c3ede83cd4bba7982273e6c1c0852201abdChain: BNB ChainSource code: Verified source
0xc20a079c7962d9fc92173cda349e80d484dfa42aChain: BNB ChainSource code: Verified source
0x43b35e89d15b91162dea1c51133c4c93bdd1c4afChain: BNB ChainSource code: Verified source
0xfa8b0197219b6cc8f3336039d50bc03220a368ceChain: BNB ChainSource code: Verified source
0x8f2e2bad6020d8bb5bf947199cae8eb86d24cfd7Chain: BNB ChainSource code: Verified source
0x65d4cb29e0edaefb18246b22513483c21a8033f4Chain: BNB ChainSource code: Verified source
0xebb30b32e74ad446b00b6333c372c5a1ccabda7dChain: BNB ChainSource code: Verified source

Show this on your site

Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.

Content

Facts

Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="sakai-vault"
></div>
</body>

Paste the code where the label should appear. It uses no API credit and updates by itself.

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us