Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · May 2022

Exchange, Part 1

for Bridges Exchange

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

21 resolved · 1 partially resolved · 1 acknowledged

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

  1. GLOBAL-1 Medium Centralization Risk Centralization / Privilege Partially resolved
    Location
    Global

    Description

    Privileged addresses have authority over many functions that may be used to negatively disrupt the project. Some important privileges include:

    GoldenGate

    • owner can withdraw all funds.
    • owner can set admins which are able to dilute allocation of other pools.
    • owner can set the migrator contract which can lead to loss of LP if malicious.

    TokenVesting

    • owner can arbitrarily set the fee and fee address which can lead to loss of user funds.

    BridgesRef

    • feeToSetter can arbitrarily set the distribution rate.
    • feeToSetter can withdraw any ERC-20 token in the contract.

    BridgesRouter

    • feeSetter can set a arbitrary referral and dividend tracker contract.

    BridgesFactory

    • feeToSetter can 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 require statements 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.
  2. GG-1 Medium DoS Dividends Denial-of-Service Resolved
    Location
    GoldenGate.sol: 181

    Description

    Due to the unbounded for loop in distributeDividends, there is a risk of a DoS attack. Anytime a new address deposits to pool 0, they are added to the usersBridges list. A malicious party can keep generating new addresses and deposit miniscule amounts of LP to make distributeDividends exceed 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 for loop 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.

  3. GG-2 High Reenter Dividends Reentrancy Resolved
    Location
    GoldenGate.sol: 228, 258

    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 div amount of BNB on each call.

    Recommendation

    Add a nonReentrant modifier from OpenZeppelin’s ReentrancyGuard or utilize the check-effects-interactions pattern.

    Resolution

    Bridges Team: - Added the lock modifier to deposit, depositLocked, relock, and withdraw.

  4. GG-3 Medium Shorten Lock Logical Error Resolved
    Location
    GoldenGate.sol: 310

    Description

    In the transferLock function _to.stakeUntil is 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.

  5. GG-4 Medium DoS Deposit and Withdraw Denial-of-Service Resolved
    Location
    GoldenGate.sol: 228, 258

    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 BNB function, then the call would 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.

  6. GG-5 Medium Dividend Sniping Frontrunning Acknowledged
    Location
    GoldenGate.sol

    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.

  7. GG-6 Low Redundant Boolean Check Optimization Resolved
    Location
    GoldenGate.sol: 208

    Description

    The check user.alreadyHere == false can be simplified to !user.alreadyHere.

    Recommendation

    Replace user.alreadyHere == false with !user.alreadyHere.

    Resolution

    Bridges Team: - The simplification was made.

  8. GG-7 Low Typo Typo Resolved
    Location
    GoldenGate.sol: 265

    Description

    Withdraw is spelled witdhraw in the error message on line 265.

    Recommendation

    Correct it to withdraw.

    Resolution

    Bridges Team: - Typo has been fixed.

  9. GG-8 Low Duplicate Code Lines Optimization Resolved
    Location
    GoldenGate.sol: 273

    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.

  10. GG-9 Low Superfluous Code Optimization Resolved
    Location
    GoldenGate.sol: 288

    Description

    In the emergencyWithdraw function, the statement user.userLockedAmount = user.userLockedAmount.sub(user.userLockedAmount) is equivalent to user.userLockedAmount = 0.

    Recommendation

    Replace the inefficient statement with user.userLockedAmount = 0.

    Resolution

    Bridges Team: - Updated to = 0.

  11. GG-10 Low Cannot Withdraw Max Amount Logical Error Resolved
    Location
    GoldenGate.sol: 330, 336

    Description

    In the Bep20 and BNB functions the require statements 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 <=

  12. GG-11 Low Superfluous Code Optimization Resolved
    Location
    GoldenGate.sol: 330, 336

    Description

    In the Bep20 function the payable cast on line 331 is unnecessary as the safeTransfer function simply accepts an address.

    Recommendation

    Remove the payable cast.

    Resolution

    Bridges Team: - Removed the payable cast.

  13. GG-12 Low Lack of camelCase Code Cleanliness Resolved
    Location
    GoldenGate.sol: 347

    Description

    The function pendingbridges does not abide by camelCase naming conventions.

    Recommendation

    Rename it to pendingBridges.

    Resolution

    Bridges Team: - The function has been renamed.

  14. TV-1 Low Setting Default Values Optimization Resolved
    Location
    TokenVault.sol: 82

    Description

    The shares variable is initialized to 0. This is unnecessary because the default value for the uint256 type is 0.

    Recommendation

    Remove the assignment.

    Resolution

    Bridges Team: - Removed the assignment.

  15. BRT-1 Medium Unable to Swap ETH Logical Error Resolved
    Location
    BridgesRouter.sol: 168

    Description

    Because the trading fee tradingFee is taken before amounts is 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.

  16. BRF-1 Medium Accidental Magnification Logical Error Resolved
    Location
    BridgesRef.sol: 131

    Description

    In the distribute function each user.earned amount is multiplied by the disRate but in the withdraw function, the user.earned amount is not divided by some divisor that corresponds to the disRate.

    Recommendation

    Add a divisor for the disRate and use it to adjust user.earned values in either the distribute or withdraw functions as needed.

    Resolution

    Bridges Team: - This is intended behavior, we treat the disRate as a multiplier.

  17. BRF-2 Medium Unbounded disRate Privilege / Logical Error Resolved
    Location
    BridgesRef.sol: 127

    Description

    There is no bound to how high the disRate variable can be set in the setDisRate function. Therefore, the distributed amount in the distribute function may exceed the provided amount.

    Recommendation

    Implement a maximum cap for disRate. A cap of ~ 57% (1/1.75) would allow for a maximum distribution of amount.

    Resolution

    Bridges Team: - Set a disRate cap of 1000.

  18. BRF-3 Low Superfluous Code Optimization Resolved
    Location
    BridgesRef.sol: 26

    Description

    The Distributed variable is not referenced at all.

    Recommendation

    Remove the Distributed variable.

    Resolution

    Bridges Team: - The variable has been removed.

  19. BRF-4 Low Typo Typo Resolved
    Location
    BridgesRef.sol: 63, 67, 72

    Description

    The withelistTokens, withelistUsers, and withelistUser functions all have a typo.

    Recommendation

    Correct them to whitelistTokens, whitelistUsers, and whitelistUser.

    Resolution

    Bridges Team: - Typos have been corrected.

  20. BRF-5 Low Cannot Withdraw Max Amount Logical Error Resolved
    Location
    BridgesRef.sol: 156

    Description

    In the withdraw function the require statement 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.

  21. FACT-1 Low Unimplemented Interface Methods Logical Error Resolved
    Location
    BridgesFactory.sol

    Description

    The BridgesFactory contract fails to provide implementations for the feeTo, tradingStart, and setFeeTo functions defined in the IBridgesFactory interface.

    Recommendation

    Either add implementations for these functions in the BridgesFactory contract or remove them from the IBridgesFactory interface.

    Resolution

    Bridges Team: - Unimplemented functions have been removed from the interface.

  22. PAIR-1 High Duplicate Dividends Logical Error Resolved
    Location
    BridgesPair.sol: 124

    Description

    In the mint function, the only precondition for adding an address to the users list is if the balance of the address is 0. Additionally, an address is not removed from the users list if it transfers it’s balance of the BridgesPair token.

    This way an address can continually mint and transfer/burn it’s tokens to enter the users list multiple times. Multiple entries in the users list will result in multiple dividends being paid out in the distributeDividends function.

    Recommendation

    Add a check for addresses that are already in the users list.

    Resolution

    Bridges Team: - Dividends have been refactored to a dividendsPerShare implementation.

  23. PAIR-2 Medium DoS Dividends Denial-of-Service Resolved
    Location
    BridgesPair.sol: 198

    Description

    Due to the unbounded for loop in distributeDividends, 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 make distributeDividends exceed 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 for loop 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.

More from Bridges Exchange

  1. Exchange, Part 2

    8 findings 8 findings: 4 medium, 4 low

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.

Get a quote