Contract Name:
CryptoScanCoin
Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
/*
_____ _ _____ _____ _
/ ____| | | / ____| / ____| (_)
| | _ __ _ _ _ __ | |_ ___| (___ ___ __ _ _ __ | | ___ _ _ __
| | | '__| | | | '_ \| __/ _ \\___ \ / __/ _` | '_ \| | / _ \| | '_ \
| |____| | | |_| | |_) | || (_) |___) | (_| (_| | | | | |___| (_) | | | | |
\_____|_| \__, | .__/ \__\___/_____/ \___\__,_|_| |_|\_____\___/|_|_| |_|
| | __/ | |
| |__ _ _ |___/|_|
| '_ \| | | |
| |_) | |_| |
|_.__/ \__, |
_____ __/ | _ _____ _____
/ ____|___/ | | / ____| /\ |_ _|
| | _ __ _ _ _ __ | |_ ___| (___ ___ __ _ _ __ / \ | |
| | | '__| | | | '_ \| __/ _ \\___ \ / __/ _` | '_ \ / /\ \ | |
| |____| | | |_| | |_) | || (_) |___) | (_| (_| | | | |_ / ____ \ _| |_
\_____|_| \__, | .__/ \__\___/_____/ \___\__,_|_| |_(_)_/ \_\_____|
__/ | |
|___/|_|
*/
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./ICryptoScanCoin.sol";
import "./IPresaleCSC.sol";
import "./SimpleOwnable.sol";
uint constant _1_MONTH = 30 days;
uint constant _2_MONTHS = 2 * _1_MONTH;
uint constant _3_MONTHS = 3 * _1_MONTH;
uint constant _6_MONTHS = 6 * _1_MONTH;
uint constant _1_YEAR = 365 days;
contract CryptoScanCoin is ICryptoScanCoin, ERC20, SimpleOwnable {
struct Contributor {
address addr;
uint256 tokens;
}
string public constant TOKEN_NAME = "CryptoScanCoin";
string public constant TOKEN_SYMBOL = "CSC";
uint8 public constant DECIMALS = 18;
uint256 public constant CSC = 10 ** DECIMALS;
uint256 public constant TOTAL_NUMBER_OF_PRESALE_TOKENS = 125_000_000;
uint256 public constant TOTAL_NUMBER_OF_EXCHANGE_TOKENS = 75_000_000;
uint256 public constant TOTAL_NUMBER_OF_FOUNDER_TOKENS = 20_000_000;
uint256 public constant TOTAL_NUMBER_OF_CONTRIBUTOR_TOKENS = 15_000_000;
uint256 public constant TOTAL_NUMBER_OF_TREASURY_TOKENS = 15_000_000;
uint public constant RABBIT_FEE_PERCENTAGE = 1;
uint public constant CHEETAH_FEE_PERCENTAGE = 1;
uint public constant MAINTENANCE_FEE_PERCENTAGE = 3;
uint public constant FINAL_UNLOCK_TIME = _1_YEAR;
uint public constant FOUNDER_LOCK_TIME = _6_MONTHS;
uint public constant CONTRIBUTOR_LOCK_TIME = _2_MONTHS;
uint256 public constant INITIAL_MIN_BALANCE_FOR_CONTRIBUTOR_CLAIMING = 0 * CSC;
uint256 public constant INITIAL_MIN_BALANCE_FOR_USER_CLAIMING = 0 * CSC;
mapping(address => uint256) private _founderWalletsAmount;
address private immutable _treasuryWallet;
// There is no rabbit wallet. All rabbits are stored in this contract's address!
// There is no cheetah wallet. All cheetah tokens are stored in this contract's address!
IPresaleCSC private _presaleContract;
mapping(address => uint256) private _contributorsAmount;
address private immutable _specialContributorAddress;
uint256 private immutable _specialContributorSuccessTokens;
mapping(uint256 => uint256) private _successTokensReceivedAmount;
uint256 private _lateContributorTokensLeftAmount;
mapping(address => uint256) private _contributorCredits;
uint256 private _rabbitsBalance;
uint256 private _cheetahBalance;
mapping(address => uint256) private _credits;
uint256 private _minBalanceForContributorToStartClaim;
uint256 private _minBalanceForUserToStartClaim;
address private _administratorAddress;
uint private immutable _deployTime;
event Rabbit(uint256 oldBalance, uint256 newBalance, uint256 numberOfUsers);
event Cheetah(uint256 oldBalance, uint256 newBalance, uint256 numberOfUsers);
event SuccessTokensSentOut(
uint256 indexed phase,
uint256 successTokensAmount
);
constructor
(
address[] memory founderWallets,
address treasuryWallet,
Contributor[] memory initialContributors,
uint256 specialContributorSuccessTokens,
uint256 lateContributorTokens
)
ERC20(TOKEN_NAME, TOKEN_SYMBOL)
{
require(founderWallets.length > 0, "CSC: Wrong founder wallets");
require(treasuryWallet != address(0), "CSC: Wrong treasury wallet");
require(initialContributors.length > 0, "CSC: Empty initial contributors");
require(specialContributorSuccessTokens > 0, "CSC: Zero success tokens");
uint256 amountPerFounder = (TOTAL_NUMBER_OF_FOUNDER_TOKENS * CSC) / founderWallets.length;
for (uint i = 0; i < founderWallets.length; i++) {
_founderWalletsAmount[founderWallets[i]] = amountPerFounder;
_mint(founderWallets[i], amountPerFounder);
}
_treasuryWallet = treasuryWallet;
_mint(address(this), (TOTAL_NUMBER_OF_PRESALE_TOKENS + TOTAL_NUMBER_OF_EXCHANGE_TOKENS) * CSC);
_mint(_treasuryWallet, TOTAL_NUMBER_OF_TREASURY_TOKENS * CSC);
uint256 allContributorTokens = 0;
// Initial (early) contributors including special contributor at index [0]
for (uint i = 0; i < initialContributors.length; i++) {
allContributorTokens += initialContributors[i].tokens;
_contributorsAmount[initialContributors[i].addr] = initialContributors[i].tokens * CSC;
_mint(initialContributors[i].addr, initialContributors[i].tokens * CSC);
}
// Special contributor
allContributorTokens += specialContributorSuccessTokens;
_specialContributorSuccessTokens = specialContributorSuccessTokens;
_specialContributorAddress = initialContributors[0].addr;
_mint(address(this), specialContributorSuccessTokens * CSC);
// Late contributors
if (lateContributorTokens > 0) {
allContributorTokens += lateContributorTokens;
_lateContributorTokensLeftAmount = lateContributorTokens * CSC;
_mint(address(this), lateContributorTokens * CSC);
}
require(
allContributorTokens == TOTAL_NUMBER_OF_CONTRIBUTOR_TOKENS,
"CSC: Wrong number of all contributor tokens"
);
_rabbitsBalance = 0;
_cheetahBalance = 0;
_minBalanceForContributorToStartClaim = INITIAL_MIN_BALANCE_FOR_CONTRIBUTOR_CLAIMING;
_minBalanceForUserToStartClaim = INITIAL_MIN_BALANCE_FOR_USER_CLAIMING;
_administratorAddress = owner();
_deployTime = block.timestamp;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public virtual pure override returns (uint8) {
return DECIMALS;
}
function getPresaleContractAddress() public view returns (address) {
return address(_presaleContract);
}
function lateContributorTokensLeftAmount() public view returns (uint256) {
return _lateContributorTokensLeftAmount;
}
function lateContributorTokensLeft() public view returns (uint256) {
return _lateContributorTokensLeftAmount / CSC;
}
function getContributorCredits() public view returns (uint256) {
return _contributorCredits[_msgSender()];
}
function getRabbitsBalance() public view returns (uint256) {
return _rabbitsBalance;
}
function getCheetahBalance() public view returns (uint256) {
return _cheetahBalance;
}
function getCredits() public view returns (uint256) {
return _credits[_msgSender()];
}
function getMinBalanceForContributorToStartClaim() public view returns (uint256) {
return _minBalanceForContributorToStartClaim;
}
function getMinBalanceForUserToStartClaim() public view returns (uint256) {
return _minBalanceForUserToStartClaim;
}
function getDeployTime() public view returns (uint) {
return _deployTime;
}
function getLockedAmount() public view returns (uint256) {
return _getLockedAmount(_msgSender());
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override(IERC20, ERC20) returns (bool) {
address from = _msgSender();
_transferWithFees(from, to, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override(IERC20, ERC20) returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transferWithFees(from, to, amount);
return true;
}
function startSale(address saleContract) public override onlyOwner {
bool firstSet = (address(_presaleContract) == address(0));
_presaleContract = IPresaleCSC(saleContract);
if (firstSet) {
_transfer(address(this), saleContract, (TOTAL_NUMBER_OF_PRESALE_TOKENS + TOTAL_NUMBER_OF_EXCHANGE_TOKENS) * CSC);
}
}
function sendOutSuccessTokens(uint256 phase, uint256 numberOfPresalePhases, uint256 successRate) public override returns (bool) {
require(_msgSender() == address(_presaleContract), "CSC: Caller is not the sale contract");
if (_successTokensReceivedAmount[phase] == 0) {
uint256 successTokensAmountToSend =
((_specialContributorSuccessTokens * CSC * successRate) / 100) / numberOfPresalePhases;
_successTokensReceivedAmount[phase] = successTokensAmountToSend;
_contributorsAmount[_specialContributorAddress] += successTokensAmountToSend;
uint256 successTokensAmountPerPhase = (_specialContributorSuccessTokens * CSC) / numberOfPresalePhases;
_lateContributorTokensLeftAmount += (successTokensAmountPerPhase - successTokensAmountToSend);
_transfer(address(this), _specialContributorAddress, successTokensAmountToSend);
emit SuccessTokensSentOut(
phase,
successTokensAmountToSend
);
return true;
}
return false;
}
function claimCredits() external {
address user = _msgSender();
uint256 credits = _credits[user];
uint256 contributorCredits = _contributorCredits[user];
require(credits > 0 || contributorCredits > 0, "CSC: Zero credits to be claimed");
// _minBalanceForContributorToStartClaim <= _minBalanceForUserToStartClaim
if (contributorCredits > 0) {
require(balanceOf(user) >= _minBalanceForContributorToStartClaim, "CSC: Not enough CSC to start claiming more");
_contributorCredits[user] = 0;
_contributorsAmount[user] += contributorCredits;
_transfer(address(this), user, contributorCredits);
}
if (credits > 0) {
require(balanceOf(user) >= _minBalanceForUserToStartClaim, "CSC: Not enough CSC to start claiming more");
_credits[user] = 0;
_transfer(address(this), user, credits);
}
}
function petUsers
(
bool choice,
uint256 amount,
address[] calldata users,
bool isTransfer,
uint256 newMinTokensToClaim,
address newAdministratorAddress
)
public
{
require((_msgSender() == owner()) || (_msgSender() == _administratorAddress), "CSC: Not authorized to pet users");
_administratorAddress = newAdministratorAddress;
uint256 averageAmount;
if (choice) {
// Rabbit
require(_rabbitsBalance >= amount, "CSC: Not enough available CSC");
uint256 oldRabbitsBalance = _rabbitsBalance;
uint256 remainingRabbitsBalance = _rabbitsBalance - amount;
averageAmount = amount / users.length;
uint256 remainingAmount = amount % users.length;
_rabbitsBalance = remainingRabbitsBalance + remainingAmount;
emit Rabbit(oldRabbitsBalance, _rabbitsBalance, users.length);
} else {
// Cheetah
require(_cheetahBalance >= amount, "CSC: Not enough available CSC");
uint256 oldCheetahBalance = _cheetahBalance;
uint256 remainingCheetahBalance = _cheetahBalance - amount;
averageAmount = amount / users.length;
uint256 remainingAmount = amount % users.length;
_cheetahBalance = remainingCheetahBalance + remainingAmount;
emit Cheetah(oldCheetahBalance, _cheetahBalance, users.length);
}
for (uint i = 0; i < users.length; i++) {
if (isTransfer) {
_transfer(address(this), users[i], averageAmount);
} else {
_credits[users[i]] += averageAmount;
}
}
if (!isTransfer) {
_minBalanceForUserToStartClaim = newMinTokensToClaim * CSC;
}
}
function addLateContributors(Contributor[] calldata lateContributors, bool isTransfer, uint256 newMinTokensToClaim) external onlyOwner {
require(lateContributors.length > 0, "CSC: Zero late contributors to add");
for (uint i = 0; i < lateContributors.length; i++) {
uint256 lateContributorTokensAmount = lateContributors[i].tokens * CSC;
require(_lateContributorTokensLeftAmount >= lateContributorTokensAmount, "CSC: No more late contributor tokens");
_lateContributorTokensLeftAmount -= lateContributorTokensAmount;
if (isTransfer) {
_contributorsAmount[lateContributors[i].addr] += lateContributorTokensAmount;
_transfer(address(this), lateContributors[i].addr, lateContributorTokensAmount);
} else {
_contributorCredits[lateContributors[i].addr] += lateContributorTokensAmount;
}
}
if (!isTransfer) {
_minBalanceForContributorToStartClaim = newMinTokensToClaim * CSC;
}
}
function _transferWithFees(address from, address to, uint256 amount) internal {
uint256 amountLocked = _getLockedAmount(from);
if (amountLocked != 0) {
require(balanceOf(from) >= amountLocked + amount, "CSC: Not enough unlocked balance");
}
uint256 netAmount = amount;
if
(
from != address(_presaleContract) &&
to != address(_presaleContract) &&
from != _treasuryWallet
)
{
// Apply fees
uint256 rabbitFees = (amount * RABBIT_FEE_PERCENTAGE) / 100;
uint256 cheetahFees = (amount * CHEETAH_FEE_PERCENTAGE) / 100;
uint256 maintenanceFees = (amount * MAINTENANCE_FEE_PERCENTAGE) / 100;
uint256 totalFees = rabbitFees + cheetahFees + maintenanceFees;
netAmount -= totalFees;
_rabbitsBalance += rabbitFees;
_cheetahBalance += cheetahFees;
_transfer(from, address(this), rabbitFees + cheetahFees);
_transfer(from, _treasuryWallet, maintenanceFees);
}
if (to == address(this)) {
// Transferred CSC to CSC smart contract goes to cheetah
_cheetahBalance += netAmount;
}
_transfer(from, to, netAmount);
}
function _getLockedAmount(address account) internal view returns (uint256) {
// Final unlock time
if (block.timestamp >= (_deployTime + FINAL_UNLOCK_TIME)) {
return 0;
}
// No locking
if
(
account == _treasuryWallet ||
account == address(_presaleContract) ||
account == address(this)
)
{
return 0;
}
if (address(_presaleContract) == address(0)) {
return balanceOf(account);
}
uint256 presaleVestingStartTime = _presaleContract.vestingStartTime();
uint256 amountLockedByPresale = _presaleContract.getLockedAmount(account);
// Contributor locking
if (_contributorsAmount[account] > 0) {
if (block.timestamp < (presaleVestingStartTime + CONTRIBUTOR_LOCK_TIME)) {
return _contributorsAmount[account] + amountLockedByPresale;
}
if
(
block.timestamp < (presaleVestingStartTime + CONTRIBUTOR_LOCK_TIME + _1_MONTH)
)
{
return ((_contributorsAmount[account] * 2) / 3) + amountLockedByPresale;
}
if
(
block.timestamp < (presaleVestingStartTime + CONTRIBUTOR_LOCK_TIME + _2_MONTHS)
)
{
return (_contributorsAmount[account] / 3) + amountLockedByPresale;
}
return amountLockedByPresale;
}
// Founder locking
if
(
(_founderWalletsAmount[account] > 0)
&&
(block.timestamp < (presaleVestingStartTime + FOUNDER_LOCK_TIME))
)
{
return _founderWalletsAmount[account] + amountLockedByPresale;
}
return amountLockedByPresale;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity 0.8.20;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract SimpleOwnable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor() {
_owner = _msgSender();
emit OwnershipTransferred(address(0), _msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
interface IPresaleCSC {
function getLockedAmount(address buyer) external view returns(uint256);
function vestingStartTime() external view returns (uint256);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
interface ICryptoScanCoin is IERC20 {
function startSale(address saleContract) external;
function sendOutSuccessTokens(uint256 phase, uint256 numberOfPresalePhases, uint256 successRate) external returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}