Last active
September 25, 2023 19:54
-
-
Save WPSmartContracts/4e82f76a9501323473f778eeba767a4d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.2; | |
// Standard OpenZeppelin Contracts | |
import "../utils/Context.sol"; | |
import "../token/ERC20/IERC20.sol"; | |
import "../utils/Address.sol"; | |
import "../token/ERC20/utils/SafeERC20.sol"; | |
import "../security/ReentrancyGuard.sol"; | |
import "../security/Pausable.sol"; | |
import "../access/Ownable.sol"; | |
import "../utils/math/Math.sol"; | |
/** | |
* @title Crowdsale | |
* @dev Base contract for managing a token crowdsale. | |
* Allows investors to purchase tokens with ether until the contract is paused. | |
* This contract can be extended to provide additional functionality. | |
* The external interface represents the basic interface for purchasing tokens. | |
* The internal interface is for extensible and modifiable surface of crowdsales. | |
* Do not modify the external interface. Override methods for additional functionality. | |
*/ | |
contract Crowdsale is Ownable, Pausable, ReentrancyGuard { | |
using SafeERC20 for IERC20; | |
// The token being sold | |
IERC20 private _token; | |
// Address where funds are collected | |
address payable private _wallet; | |
// How many token units a buyer gets per wei. | |
// The rate is the conversion between wei and the smallest and indivisible token unit. | |
// So, if you are using a rate of 1 with a ERC20Detailed token with 3 decimals called TOK, | |
// 1 wei will give you 1 unit, or 0.001 TOK. | |
uint256 private _rate; | |
// Amount of wei raised | |
uint256 private _weiRaised; | |
/** | |
* @dev Event emitted when tokens are purchased. | |
* @param purchaser Address of the user who purchased tokens | |
* @param beneficiary Address where purchased tokens were sent | |
* @param value Amount of ether paid for purchase | |
* @param amount Amount of tokens purchased | |
*/ | |
event TokensPurchased(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount); | |
/** | |
* @dev Event emitted when the rate is changed. | |
* @param rate the new rate | |
*/ | |
event RateChanged(uint256 rate); | |
/** | |
* @dev The rate is the conversion between wei and the smallest and indivisible | |
* token unit. So, if you are using a rate of 1 with a ERC20Detailed token | |
* with 3 decimals called TOK, 1 wei will give you 1 unit, or 0.001 TOK. | |
* @param rate_ Number of token units a buyer gets per wei | |
* @param wallet_ Address where collected funds will be forwarded to | |
* @param token_ Address of the token being sold | |
* @param owner_ Address of the contract owner | |
*/ | |
constructor (uint256 rate_, address payable wallet_, IERC20 token_, address owner_) Ownable() { | |
setRate(rate_); | |
setWallet(wallet_); | |
setToken(token_); | |
transferOwnership(owner_); | |
} | |
/** | |
* @dev Owner can change the rate. | |
* @param rate_ Number of token units a buyer gets per wei | |
*/ | |
function setRate(uint256 rate_) public onlyOwner() { | |
require(rate_ > 0, "Crowdsale: rate is 0"); | |
require(_weiRaised == 0, "Crowdsale has started"); | |
emit RateChanged(rate_); | |
_rate = rate_; | |
} | |
/** | |
* @dev Owner can change the wallet. | |
* @param wallet_ Address where collected funds will be forwarded to | |
*/ | |
function setWallet(address payable wallet_) public onlyOwner() { | |
require(wallet_ != address(0), "Crowdsale: wallet is the zero address"); | |
_wallet = wallet_; | |
} | |
/** | |
* @dev verify if an address is a contract | |
* This method relies on extcodesize, which returns 0 for contracts in | |
* construction, since the code is only stored at the end of the | |
* constructor execution. | |
* @param account address to evaluate if it is a contract or not | |
*/ | |
function _isContract(address account) internal view returns (bool) { | |
uint256 size; | |
assembly { | |
size := extcodesize(account) | |
} | |
return size > 0; | |
} | |
/** | |
* @dev Owner can change the token if the crowdsale hasn't started yet. | |
* @param token_ Address of the token being sold | |
*/ | |
function setToken(IERC20 token_) public onlyOwner() { | |
require(weiRaised() == 0, "Crowdsale has started"); | |
require(address(token_) != address(0), "Crowdsale: token is the zero address"); | |
require(_isContract(address(token_)), "Crowdsale: token is not a contract"); | |
_token = token_; | |
} | |
/** | |
* @dev Fallback function ***DO NOT OVERRIDE*** | |
* Note that other contracts will transfer funds with a base gas stipend | |
* of 2300, which is not enough to call buyTokens. Consider calling | |
* buyTokens directly when purchasing tokens from a contract. | |
* This function is automatically called when ether is sent to the contract address. | |
* Users should send Ethers from the beneficiary address in the transaction. | |
*/ | |
receive() external payable { | |
buyTokens(_msgSender()); | |
} | |
/** | |
* @return the token being sold. | |
*/ | |
function token() public view returns (IERC20) { | |
return _token; | |
} | |
/** | |
* @return the address where funds are collected. | |
*/ | |
function wallet() external view returns (address payable) { | |
return _wallet; | |
} | |
/** | |
* @return the number of token units a buyer gets per wei. | |
*/ | |
function rate() external view returns (uint256) { | |
return _rate; | |
} | |
/** | |
* @return the amount of wei raised. | |
*/ | |
function weiRaised() public view returns (uint256) { | |
return _weiRaised; | |
} | |
/** | |
* @dev Low-level token purchase ***DO NOT OVERRIDE*** | |
* Allows users to purchase tokens with ether. | |
* This function has a non-reentrancy guard, so it shouldn't be called by | |
* another `nonReentrant` function. | |
* @param beneficiary Address where purchased tokens will be sent | |
*/ | |
function buyTokens(address beneficiary) public nonReentrant whenNotPaused payable { | |
uint256 weiAmount = msg.value; | |
_preValidatePurchase(beneficiary, weiAmount); | |
// Calculate token amount to be created | |
uint256 tokens = _getTokenAmount(weiAmount); | |
// Update state | |
_weiRaised = _weiRaised + weiAmount; | |
emit TokensPurchased(_msgSender(), beneficiary, weiAmount, tokens); | |
_processPurchase(beneficiary, tokens); | |
_forwardFunds(); | |
} | |
/** | |
* @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met. | |
* Use `super` in contracts that inherit from Crowdsale to extend their validations. | |
* Example from CappedCrowdsale.sol's _preValidatePurchase method: | |
* super._preValidatePurchase(beneficiary, weiAmount); | |
* require(weiRaised().add(weiAmount) <= cap); | |
* @param beneficiary Address performing the token purchase | |
* @param weiAmount Value in wei involved in the purchase | |
*/ | |
function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view { | |
require(beneficiary != address(0), "Crowdsale: beneficiary is the zero address"); | |
require(weiAmount != 0, "Crowdsale: weiAmount is 0"); | |
this; // Silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 | |
} | |
/** | |
* @dev Source of tokens. Override this method to modify the way in which the crowdsale ultimately gets and sends | |
* its tokens. | |
* @param beneficiary Address performing the token purchase | |
* @param tokenAmount Number of tokens to be emitted | |
*/ | |
function _deliverTokens(address beneficiary, uint256 tokenAmount) virtual internal { | |
_token.safeTransfer(beneficiary, tokenAmount); | |
} | |
/** | |
* @dev Executed when a purchase has been validated and is ready to be executed. Doesn't necessarily emit/send | |
* tokens. | |
* @param beneficiary Address receiving the tokens | |
* @param tokenAmount Number of tokens to be purchased | |
*/ | |
function _processPurchase(address beneficiary, uint256 tokenAmount) internal { | |
_deliverTokens(beneficiary, tokenAmount); | |
} | |
/** | |
* @dev Override to extend the way in which ether is converted to tokens. | |
* @param weiAmount Value in wei to be converted into tokens | |
* @return Number of tokens that can be purchased with the specified _weiAmount | |
*/ | |
function _getTokenAmount(uint256 weiAmount) internal view returns (uint256) { | |
return weiAmount * _rate; | |
} | |
/** | |
* @dev Determines how ETH is stored/forwarded on purchases. | |
*/ | |
function _forwardFunds() internal { | |
_wallet.transfer(msg.value); | |
} | |
/** | |
* @dev Pauses the ICO activity. Only the contract owner can call this function. | |
*/ | |
function pause() external onlyOwner() { | |
_pause(); | |
} | |
/** | |
* @dev Resumes the ICO activity. Only the contract owner can call this function. | |
*/ | |
function unpause() external onlyOwner() { | |
_unpause(); | |
} | |
} | |
/** | |
* @title BubblegumCrowdsale | |
* @dev Extension of Crowdsale where tokens are held by a wallet, which approves an allowance to the crowdsale. | |
*/ | |
contract BubblegumCrowdsale is Crowdsale { | |
using SafeERC20 for IERC20; | |
address private _tokenWallet; | |
/** | |
* @dev The rate is the conversion between wei and the smallest and indivisible | |
* token unit. So, if you are using a rate of 1 with a ERC20Detailed token | |
* with 3 decimals called TOK, 1 wei will give you 1 unit, or 0.001 TOK. | |
* @param wallet_ Address where collected funds will be forwarded to | |
* @param token_ Address of the token being sold | |
* @param rate_ Number of token units a buyer gets per wei | |
* @param owner_ Address of the contract owner | |
* @param distributionWallet_ Address holding the tokens, which has approved allowance to the crowdsale. | |
*/ | |
constructor (uint256 rate_, address payable wallet_, IERC20 token_, address distributionWallet_, address owner_) | |
Crowdsale(rate_, wallet_, token_, owner_) { | |
require(distributionWallet_ != address(0), "AllowanceCrowdsale: token wallet is the zero address"); | |
_tokenWallet = distributionWallet_; | |
} | |
/** | |
* @return the address of the wallet that will hold the tokens. | |
*/ | |
function tokenWallet() external view returns (address) { | |
return _tokenWallet; | |
} | |
/** | |
* @dev Checks the amount of tokens left in the allowance. | |
* @return Amount of tokens left in the allowance | |
*/ | |
function remainingTokens() external view returns (uint256) { | |
return Math.min(token().balanceOf(_tokenWallet), token().allowance(_tokenWallet, address(this))); | |
} | |
/** | |
* @dev Overrides parent behavior by transferring tokens from wallet. | |
* @param beneficiary Token purchaser | |
* @param tokenAmount Amount of tokens purchased | |
*/ | |
function _deliverTokens(address beneficiary, uint256 tokenAmount) internal override { | |
token().safeTransferFrom(_tokenWallet, beneficiary, tokenAmount); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment