Safu Investments Presale Platform - Smart Contract Audit Report
Summary
Safu Investments is a launchpad for projects on the Ethereum mainnet to launch in a safe, trusted, and decentralized manner. The platform has had dozens of successful presales in a few short months.
We reviewed Safu Investments' updated contracts at the following addresses on mainnet:- SafuInvestmentsFactory - 0xcD76d39B8979A4025C09dfAD9161C83cD21234b2.
- SafuInvestmentsPresale - 0x619ac3784db339252809e8e80bb61212fee1bbfc (Example; address will be unique for each presale).
- SafuInvestmentsLiquidityLock - 0x442f08673514e386a4a055c927c5cab1ec13e0e30x619ac3784db339252809e8e80bb61212fee1bbfc (Example; address will be unique for each presale).
- SafuInvestmentsInfo - 0x41fc363979c10b3427e8c10727a795439afe9c58.
Notes on the Contracts:The Factory contract exists to deploy Presale and LiquidityLock contracts. The Presale contract allows teams to create presales, and users to contribute ETH to participate in that presale. This contract is re-deployed for each new presale. The LiquidityLock contract holds the liquidity added to Uniswap and shall release it to the team after a set amount of time. The Info contract exists to hold information such as the developers' fee, owner's address, and a counter of presales counducted. Any user can call the createPresle() function on the Factory contract to generate a presale, passing in variables around the presale duration, token price, liquidity lock time & percentage, min & max contributions, hard & soft caps, start & end dates, etc. When a presale is created via this function, a Presale contract will be deployed at a new address for use by the caller. While the sale is active, users can contribute ETH to purchase tokens in a presale at the rate defined by the project team hosting the presale. If the sale's soft cap is not reached, the sale will be cancelled and user's will be able to claim refunds. The team running the presale can also end the sale early if they so choose. At the conclusion of the sale, liquidity will been added to Uniswap and users who bought into the presale can claim their tokens. If the hard cap is reached prior to the sale closing date, the team hosting the presale can conclude the process early; adding liquidity to Uniswap and allowing token claims by investors. When a user claims their tokens, their ETH contribution is sent to the project team running the presale. 24 hours after the presale concludes and liquidity is added, all of the ETH raised will be available to be claimed by the project team. Upon adding liquidity to Uniswap, the amount of liquidity set by the team running the presale will be sent to and locked in the LiquidityLock contract. The liquidity lock contract will hold the LP tokens until the unlock time (set by the project team) is met; at which point the team will be able to claim the LP tokens. A 1% fee or 1 ETH (Whichever is greater) will be sent to the Safu Investments team at the conclusion of the sale. These amounts can be updated to any amount at any time in the Info contract. This, however, would only impact newly deployed presales; not existing ones. The Safu Investments team can choose to exempt presale from developer fees after deployment if desired. The Info contract allows anyone to add an address as a presale, incrementing the counter. While this may be unintneded (as opposed to only allowing the factory to call this), it has no impact on security or safety of the contracts. The contracts utilize SafeMath to prevent overflow issues and properly structure logic around ETH transfers to prevent reentrancy issues. Usage of ERC777 tokens (very uncommon) with the contract could lead to reentrancy issues; thus it is advisable that this style of token not be used with the platform.
Audit Findings Summary:
- No security issues from outside attackers were identified.
- The project lead has completed KYC with our firm.
- Date: March 26th, 2021.
External Threats
Vulnerability Category | Notes | Result |
---|---|---|
Arbitrary Storage Write | N/A | PASS |
Arbitrary Jump | N/A | PASS |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | N/A | PASS |
Deprecated Opcodes | N/A | PASS |
Ether Thief | N/A | PASS |
Exceptions | N/A | PASS |
External Calls | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Multiple Sends | N/A | PASS |
Suicide | N/A | PASS |
State Change External Calls | N/A | Pass |
Unchecked Retval | N/A | PASS |
User Supplied Assertion | N/A | PASS |
Critical Solidity Compiler | N/A | PASS |
Overall Contract Safety | PASS |
Details - SafuInvestmentsFactory Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Lib] Address
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Int] functionStaticCall
- [Int] functionStaticCall
- [Prv] _verifyCallResult
+ Context
- [Int] _msgSender
- [Int] _msgData
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ Ownable (Context)
- [Int] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
+ [Lib] SafeERC20
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeApprove #
- [Int] safeIncreaseAllowance #
- [Int] safeDecreaseAllowance #
- [Prv] _callOptionalReturn #
+ [Lib] SafeMath
- [Int] add
- [Int] sub
- [Int] sub
- [Int] mul
- [Int] div
- [Int] div
- [Int] mod
- [Int] mod
+ [Int] IUniswapV2Factory
- [Ext] getPair
+ SafuInvestmentsFactory
- [Pub] #
- [Int] uniV2LibPairFor
- [Int] initializePresale #
- [Ext] createPresale #
+ SafuInvestmentsInfo (Ownable)
- [Ext] addPresaleAddress #
- [Ext] getPresalesCount
- [Ext] getPresaleAddress
- [Ext] getDevFeePercentage
- [Ext] setDevFeePercentage #
- modifiers: onlyOwner
- [Ext] getMinDevFeeInWei
- [Ext] setMinDevFeeInWei #
- modifiers: onlyOwner
+ [Int] IUniswapV2Router02
- [Ext] addLiquidityETH ($)
+ SafuInvestmentsPresale
- [Pub] #
- [Ext] setAddressInfo #
- modifiers: onlySafuFactory
- [Ext] setGeneralInfo #
- modifiers: onlySafuFactory
- [Ext] setUniswapInfo #
- modifiers: onlySafuFactory
- [Ext] setStringInfo #
- modifiers: onlyPresaleCreatorOrSafuFactory
- [Ext] setSafuInfo #
- modifiers: onlySafuDev
- [Ext] setSafuDevFeesExempted #
- modifiers: onlySafuDev
- [Ext] setOnlyWhitelistedAddressesAllowed #
- modifiers: onlyPresaleCreatorOrSafuFactory
- [Ext] addwhitelistedAddresses #
- modifiers: onlyPresaleCreatorOrSafuFactory
- [Int] getTokenAmount
- [Pub] invest ($)
- modifiers: whitelistedAddressOnly,presaleIsNotCancelled
- [Ext] ($)
- [Ext] addLiquidityAndLockLPTokens #
- modifiers: presaleIsNotCancelled
- [Ext] claimTokens #
- modifiers: whitelistedAddressOnly,presaleIsNotCancelled,investorOnly,notYetClaimedOrRefunded
- [Ext] getRefund #
- modifiers: whitelistedAddressOnly,investorOnly,notYetClaimedOrRefunded
- [Ext] cancelAndTransferTokensToPresaleCreator #
- [Ext] collectFundsRaised #
- modifiers: onlyPresaleCreator
+ TokenTimelock
- [Pub] #
- [Pub] token
- [Pub] beneficiary
- [Pub] releaseTime
- [Pub] release #
+ SafuInvestmentsLiquidityLock (TokenTimelock)
- [Pub] #
- modifiers: TokenTimelock
($) = payable function
# = non-constant function
Details - SafuInvestmentsPresale Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Lib] SafeMath
- [Int] add
- [Int] sub
- [Int] sub
- [Int] mul
- [Int] div
- [Int] div
- [Int] mod
- [Int] mod
+ [Lib] SafeMath
- [Int] add
- [Int] sub
- [Int] sub
- [Int] mul
- [Int] div
- [Int] div
- [Int] mod
- [Int] mod
+ [Int] IUniswapV2Router02
- [Ext] addLiquidityETH ($)
+ SafuInvestmentsPresale
- [Pub] #
- [Ext] setAddressInfo #
- modifiers: onlySafuFactory
- [Ext] setGeneralInfo #
- modifiers: onlySafuFactory
- [Ext] setUniswapInfo #
- modifiers: onlySafuFactory
- [Ext] setStringInfo #
- modifiers: onlyPresaleCreatorOrSafuFactory
- [Ext] setSafuInfo #
- modifiers: onlySafuDev
- [Ext] setSafuDevFeesExempted #
- modifiers: onlySafuDev
- [Ext] setOnlyWhitelistedAddressesAllowed #
- modifiers: onlyPresaleCreatorOrSafuFactory
- [Ext] addwhitelistedAddresses #
- modifiers: onlyPresaleCreatorOrSafuFactory
- [Int] getTokenAmount
- [Pub] invest ($)
- modifiers: whitelistedAddressOnly,presaleIsNotCancelled
- [Ext] ($)
- [Ext] addLiquidityAndLockLPTokens #
- modifiers: presaleIsNotCancelled
- [Ext] claimTokens #
- modifiers: whitelistedAddressOnly,presaleIsNotCancelled,investorOnly,notYetClaimedOrRefunded
- [Ext] getRefund #
- modifiers: whitelistedAddressOnly,investorOnly,notYetClaimedOrRefunded
- [Ext] cancelAndTransferTokensToPresaleCreator #
- [Ext] collectFundsRaised #
- modifiers: onlyPresaleCreator
Details - SafuInvestmentsLiquidityLock Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Int] IUniswapV2Router02
- [Ext] addLiquidityETH ($)
+ SafuInvestmentsPresale
- [Pub] #
- [Ext] setAddressInfo #
- modifiers: onlySafuFactory
- [Ext] setGeneralInfo #
- modifiers: onlySafuFactory
- [Ext] setUniswapInfo #
- modifiers: onlySafuFactory
- [Ext] setStringInfo #
- modifiers: onlyPresaleCreatorOrSafuFactory
- [Ext] setSafuInfo #
- modifiers: onlySafuDev
- [Ext] setSafuDevFeesExempted #
- modifiers: onlySafuDev
- [Ext] setOnlyWhitelistedAddressesAllowed #
- modifiers: onlyPresaleCreatorOrSafuFactory
- [Ext] addwhitelistedAddresses #
- modifiers: onlyPresaleCreatorOrSafuFactory
- [Int] getTokenAmount
- [Pub] invest ($)
- modifiers: whitelistedAddressOnly,presaleIsNotCancelled
- [Ext] ($)
- [Ext] addLiquidityAndLockLPTokens #
- modifiers: presaleIsNotCancelled
- [Ext] claimTokens #
- modifiers: whitelistedAddressOnly,presaleIsNotCancelled,investorOnly,notYetClaimedOrRefunded
- [Ext] getRefund #
- modifiers: whitelistedAddressOnly,investorOnly,notYetClaimedOrRefunded
- [Ext] cancelAndTransferTokensToPresaleCreator #
- [Ext] collectFundsRaised #
- modifiers: onlyPresaleCreator
+ [Lib] Address
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Int] functionStaticCall
- [Int] functionStaticCall
- [Prv] _verifyCallResult
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Lib] SafeERC20
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeApprove #
- [Int] safeIncreaseAllowance #
- [Int] safeDecreaseAllowance #
- [Prv] _callOptionalReturn #
+ TokenTimelock
- [Pub] #
- [Pub] token
- [Pub] beneficiary
- [Pub] releaseTime
- [Pub] release #
+ TokenTimelock
- [Pub] #
- [Pub] token
- [Pub] beneficiary
- [Pub] releaseTime
- [Pub] release #
+ SafuInvestmentsLiquidityLock (TokenTimelock)
- [Pub] #
- modifiers: TokenTimelock
Details - SafuInvestmentsInfo Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ Context
- [Int] _msgSender
- [Int] _msgData
+ Ownable (Context)
- [Int] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
+ SafuInvestmentsInfo (Ownable)
- [Ext] addPresaleAddress #
- [Ext] getPresalesCount
- [Ext] getPresaleAddress
- [Ext] getDevFeePercentage
- [Ext] setDevFeePercentage #
- modifiers: onlyOwner
- [Ext] getMinDevFeeInWei
- [Ext] setMinDevFeeInWei #
- modifiers: onlyOwner