After a line by line manual analysis and automated review, Guardian Audits has concluded that:
- Published
- Language
- Solidity
- Chains
- BNB Chain
- Sector
- DEXs and AMMs
- 0 Critical
- 2 High
- 9 Medium
- 12 Low
- 0 Informational
Scope
Overview
After a line by line manual analysis and automated review, Guardian Audits has concluded that:
- Bridges’ smart contracts have a MEDIUM RISK SEVERITY
- Bridges’ smart contracts have an ACTIVE OWNERSHIP
- Bridges’ smart contract owner has multiple “write” privileges. Centralization risk correlated to the active ownership is MEDIUM
Findings 23
-
GLOBAL-1 Medium Centralization Risk Centralization / Privilege Partially resolved
Description
Privileged addresses have authority over many functions that may be used to negatively disrupt the project. Some important privileges include:
GoldenGate
ownercan withdraw all funds.ownercan set admins which are able to dilute allocation of other pools.ownercan set the migrator contract which can lead to loss of LP if malicious.
TokenVesting
ownercan arbitrarily set the fee and fee address which can lead to loss of user funds.
BridgesRef
feeToSettercan arbitrarily set the distribution rate.feeToSettercan withdraw any ERC-20 token in the contract.
BridgesRouter
feeSettercan set a arbitrary referral and dividend tracker contract.
BridgesFactory
feeToSettercan set an arbitrary start time for when a pair can be tradeable.
Recommendation
Ensure that the privileged addresses are multi-sig and/or introduce timelock for improved community oversight. Optionally introduce
requirestatements to limit the scope of the exploits that can be carried out by the privileged addresses.Resolution
Bridges Team: - All centralized BNB and token withdrawal functions have been removed from the GoldenGate contract,
- The possibility to change fees on the TokenVault has been removed as well.
- The GoldenGate migrator has to be there for an eventual V2 in the future.
- For the same reason all update functions on the tracker are necessary.
- Every privileged address will be a multi-sig with trusted members in production.
-
GG-1 Medium DoS Dividends Denial-of-Service Resolved
Description
Due to the unbounded
forloop indistributeDividends, there is a risk of a DoS attack. Anytime a new address deposits to pool 0, they are added to theusersBridgeslist. A malicious party can keep generating new addresses and deposit miniscule amounts of LP to makedistributeDividendsexceed the block gas limit, stopping all dividends.Recommendation
Process the users in smaller batches, set a cap on number of users who can receive dividends, or modify the dividend allocation logic entirely such that a
forloop is not needed.For an alternative approach, see this “pointsPerShare” implementation:
https://github.com/indexed-finance/dividends/tree/master/contracts
Resolution
Bridges Team: - The dividend distribution mechanism was updated to a dividendsPerShare model.
-
GG-2 High Reenter Dividends Reentrancy Resolved
Description
Because the dividends paid to a user is only updated after the external call sending them funds, it is possible for a malicious contract to re-enter and keep draining
divamount of BNB on each call.Recommendation
Add a
nonReentrantmodifier from OpenZeppelin’s ReentrancyGuard or utilize the check-effects-interactions pattern.Resolution
Bridges Team: - Added the
lockmodifier todeposit,depositLocked,relock, andwithdraw. -
GG-3 Medium Shorten Lock Logical Error Resolved
Description
In the
transferLockfunction_to.stakeUntilis set to_from.stakeUntil. Therefore, it is possible to shorten the lock period by transferring from an address with a shorter lock to an address with a longer lock.Recommendation
When transferring a lock, adopt a push then pull pattern where the receiver needs to accept an incoming lock, and then adopt the longer lock period when combining locks. Alternatively, make each lock its own unique NFT token.
Resolution
Bridges Team: - Now requires that the
_to.amount = 0. -
GG-4 Medium DoS Deposit and Withdraw Denial-of-Service Resolved
Description
Because depositing and withdrawing from pool 0 relies on a successful BNB transfer for the dividends payment, it is possible to prevent deposits and withdrawals. If a user were to drain the BNB from the contract using the re-entracy described earlier or the owner drained the BNB using the
BNBfunction, then thecallwould fail and the transaction would revert.Recommendation
Refactor the dividend payments so they are separate from withdrawals and deposits.
Resolution
Bridges Team: - Dividends have been refactored and the emergency BNB function has been removed.
-
GG-5 Medium Dividend Sniping Frontrunning Acknowledged
Description
Because it is possible to publicly see transactions that are sending value to
distributeDividends, bots can frontrun the distribution. This way addresses may sandwich a deposit and withdrawal around a distribution in order to unfairly accumulate dividends while never effectively holding the token.Recommendation
Introduce a warmup period, or require locking for dividends.
Resolution
Bridges Team: - We think this is unlikely as it would require swapping for tokens and providing/removing liquidity to achieve a return, it would likely be gas/slippage cost prohibitive.
-
GG-6 Low Redundant Boolean Check Optimization Resolved
Description
The check
user.alreadyHere == falsecan be simplified to!user.alreadyHere.Recommendation
Replace
user.alreadyHere == falsewith!user.alreadyHere.Resolution
Bridges Team: - The simplification was made.
-
GG-7 Low Typo Typo Resolved
Description
Withdraw is spelled
witdhrawin the error message on line 265.Recommendation
Correct it to
withdraw.Resolution
Bridges Team: - Typo has been fixed.
-
GG-8 Low Duplicate Code Lines Optimization Resolved
Description
The statement
user.rewardDebt = user.amount.mul(pool.accBRGPerShare).div(1e12)is repeated on line 275.Recommendation
Remove the first occurrence on line 273.
Resolution
Bridges Team: - The first duplicate was removed.
-
GG-9 Low Superfluous Code Optimization Resolved
Description
In the emergencyWithdraw function, the statement
user.userLockedAmount =user.userLockedAmount.sub(user.userLockedAmount)is equivalent touser.userLockedAmount = 0.Recommendation
Replace the inefficient statement with
user.userLockedAmount = 0.Resolution
Bridges Team: - Updated to
= 0. -
GG-10 Low Cannot Withdraw Max Amount Logical Error Resolved
Description
In the
Bep20andBNBfunctions therequirestatements specify a value<the contract balance, meanwhile it may be intended to remove a value equal to the contract balance.Recommendation
Confirm whether or not the exact contract balance should be able to be withdrawn and optionally update accordingly.
Resolution
Bridges Team: - Updated to
<= -
GG-11 Low Superfluous Code Optimization Resolved
Description
In the
Bep20function thepayablecast on line 331 is unnecessary as thesafeTransferfunction simply accepts an address.Recommendation
Remove the
payablecast.Resolution
Bridges Team: - Removed the payable cast.
-
GG-12 Low Lack of camelCase Code Cleanliness Resolved
Description
The function
pendingbridgesdoes not abide by camelCase naming conventions.Recommendation
Rename it to
pendingBridges.Resolution
Bridges Team: - The function has been renamed.
-
TV-1 Low Setting Default Values Optimization Resolved
Description
The
sharesvariable is initialized to 0. This is unnecessary because the default value for theuint256type is 0.Recommendation
Remove the assignment.
Resolution
Bridges Team: - Removed the assignment.
-
BRT-1 Medium Unable to Swap ETH Logical Error Resolved
Description
Because the trading fee
tradingFeeis taken beforeamountsis calculated, there may not be enough BNB to deposit into the WBNB contract. Thus, the transaction will revert and the swap will fail.Recommendation
Take the trading fee after the swap has occurred, or account for the trading fee in
getAmountsIn.Resolution
Bridges Team: - Added amounts adjustment for the fee.
-
BRF-1 Medium Accidental Magnification Logical Error Resolved
Description
In the
distributefunction eachuser.earnedamount is multiplied by thedisRatebut in the withdraw function, theuser.earnedamount is not divided by some divisor that corresponds to thedisRate.Recommendation
Add a divisor for the
disRateand use it to adjustuser.earnedvalues in either thedistributeorwithdrawfunctions as needed.Resolution
Bridges Team: - This is intended behavior, we treat the disRate as a multiplier.
-
BRF-2 Medium Unbounded disRate Privilege / Logical Error Resolved
Description
There is no bound to how high the
disRatevariable can be set in thesetDisRatefunction. Therefore, the distributed amount in thedistributefunction may exceed the providedamount.Recommendation
Implement a maximum cap for
disRate. A cap of ~ 57% (1/1.75) would allow for a maximum distribution ofamount.Resolution
Bridges Team: - Set a disRate cap of 1000.
-
BRF-3 Low Superfluous Code Optimization Resolved
Description
The
Distributedvariable is not referenced at all.Recommendation
Remove the
Distributedvariable.Resolution
Bridges Team: - The variable has been removed.
-
BRF-4 Low Typo Typo Resolved
Description
The
withelistTokens, withelistUsers,andwithelistUserfunctions all have a typo.Recommendation
Correct them to
whitelistTokens,whitelistUsers, andwhitelistUser.Resolution
Bridges Team: - Typos have been corrected.
-
BRF-5 Low Cannot Withdraw Max Amount Logical Error Resolved
Description
In the
withdrawfunction therequirestatement specifies a value<the contract balance, meanwhile it may be intended to remove a value equal to the contract balance.Recommendation
Confirm whether or not the exact contract balance should be able to be withdrawn and optionally update accordingly.
Resolution
Bridges Team: - Updated to
<=to be able to withdraw the max amount. -
FACT-1 Low Unimplemented Interface Methods Logical Error Resolved
Description
The
BridgesFactorycontract fails to provide implementations for thefeeTo,tradingStart, andsetFeeTofunctions defined in theIBridgesFactoryinterface.Recommendation
Either add implementations for these functions in the
BridgesFactorycontract or remove them from theIBridgesFactoryinterface.Resolution
Bridges Team: - Unimplemented functions have been removed from the interface.
-
PAIR-1 High Duplicate Dividends Logical Error Resolved
Description
In the
mintfunction, the only precondition for adding an address to theuserslist is if the balance of the address is 0. Additionally, an address is not removed from theuserslist if it transfers it’s balance of the BridgesPair token.This way an address can continually
mintand transfer/burn it’s tokens to enter theuserslist multiple times. Multiple entries in theuserslist will result in multiple dividends being paid out in thedistributeDividendsfunction.Recommendation
Add a check for addresses that are already in the
userslist.Resolution
Bridges Team: - Dividends have been refactored to a dividendsPerShare implementation.
-
PAIR-2 Medium DoS Dividends Denial-of-Service Resolved
Description
Due to the unbounded
forloop indistributeDividends, there is a risk of a DoS attack. A malicious party can keep generating new addresses and minting minimal amounts of the BridgesPair token to makedistributeDividendsexceed the block gas limit, stopping all dividends.Recommendation
Process the users in smaller batches, set a cap on number of users who can receive dividends, or modify the dividend allocation logic entirely such that a
forloop is not needed.For an alternative approach, see this “pointsPerShare” implementation:
https://github.com/indexed-finance/dividends/tree/master/contracts
Resolution
Bridges Team: - Dividends have been refactored to a dividendsPerShare implementation.
No findings match.
More from Bridges Exchange
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
