Skip to content

Instantly share code, notes, and snippets.

@WPSmartContracts
Last active September 25, 2023 19:54
Show Gist options
  • Save WPSmartContracts/4e82f76a9501323473f778eeba767a4d to your computer and use it in GitHub Desktop.
Save WPSmartContracts/4e82f76a9501323473f778eeba767a4d to your computer and use it in GitHub Desktop.
// 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