// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0 ;
import "@openzeppelin/contrats/token/ERC20/ERC20.sol" ;
import "@openzeppelin/contrats/access/Ownable.sol" ;
Le contrat PhilanthropyToken est ERC20, Ownable {
uint256 public constant MAX_TOTAL_SUPPLY = 500 * 10 ** 6 * 10 ** 18 ; // Offre totale maximale de 500 millions de jetons
uint256 public constant MARKET_CAP = 10 * 10 ** 6 * 10 ** 18 ; // La capitalisation boursière par défaut est de 10 millions de tokens.
uint256 public INVESTOR_ALLOCATION = 4 * 10 ** 6 * 10 ** 18 ; // 4 millions de tokens pour les investisseurs
uint256 public constant GENERAL_PUBLIC_ALLOCATION = 5 * 10 ** 6 * 10 ** 18 ; // 5 millions de jetons pour le grand public
uint256 public CHARITY_ALLOCATION = 4 * 10 ** 6 * 10 ** 18 ; // 4 millions de jetons pour les organisations caritatives
uint256 public constant PRESALE_ALLOCATION = 5 * 10 ** 6 * 10 ** 18 ; // 5 millions de tokens pour la prévente
uint256 public PRESALE_TOTAL_SOLD = 0 ;
uint256 public constant INVESTOR_LOCK_PERIOD = 730 days ; // 2 ans Période de blocage pour les investisseurs
uint256 public constant PRESALE_LOCK_PERIOD = 365 days ; // 1 year Lock-up period for presale
uint256 public TOTAL_INITIAL_RELEASED = 0 ;
uint256 public TOTAL_INVESTOR_CLAIMED = 0 ;
uint256 public constant PRESALE_PRICE = 50 * 10 ** 15 ; // 0,50 $ en euros
uint256 public constant TRANSACTION_THRESHOLD = 1 * 10 ** 6 * 10 ** 18 ; // 1m Seuil de transaction pour la demande d'un code secret
uint256 public presaleStartTime ;
// Mapping to track buyer purchase timestamps
mapping(address => uint256) public lastPurchaseTimestamp ;
string public secretCode ;
mapping(address => bool) public admins ;
/**
* Uniquement le modificateur d'administration
*/
modificateur onlyAdmin() {
require(admins[msg.sender] || msg.sender == owner(), "Not an admin") ;
_ ;
}
/**
* constructeur
*/
constructor() ERC20("PhilanthropyToken", "PTPH") {
// Mint the initial supply to the contract owner
_mint(msg.sender, MARKET_CAP) ;
presaleStartTime = block.timestamp ;
// Transfer ownership to the contract deployer!
transferOwnership(msg.sender) ;
}
// Function to add or remove admins
function setAdmin(address _admin, bool _status) external onlyOwner {
admins[_admin] = _status ;
}
/**
* Fonction permettant aux utilisateurs d'acheter des jetons de prévente
* @param amount uint256
*/
function buyPresaleTokens(uint256 amount) external payable {
require(amount > 0, "Montant d'achat non valide") ;
exiger(
block.timestamp >= presaleStartTime,
"La prévente n'a pas encore commencé
) ;
exiger(
block.timestamp <= presaleStartTime + 182.5 days,
"La prévente est terminée
) ;
uint256 totalPrice = montant * PRESALE_PRICE ;
require(msg.value >= totalPrice, "Insufficient funds sent") ;
// Calculate and check remaining presale allocation
uint256 remainingPresaleAllocation = PRESALE_TOTAL_SOLD ;
exiger(
amount <= remainingPresaleAllocation,
"Pas assez de jetons disponibles à l'achat"
) ;
// Transfer tokens to the buyer
Transférer(address(this), msg.sender, amount) ;
// Update the buyer's last purchase timestamp
lastPurchaseTimestamp[msg.sender] = block.timestamp ;
// Refund any excess funds sent
if (msg.value > totalPrice) {
payable(msg.sender).transfer(msg.value - totalPrice) ;
}
PRESALE_TOTAL_SOLD += montant ;
}
/**
* Fonction d'augmentation de la capitalisation boursière
* @param amount uint256
*/
function increaseMarketCap(uint256 amount) external onlyAdmin {
uint256 currentSupply = totalSupply() ;
exiger(
currentSupply + amount <= MAX_TOTAL_SUPPLY,
"Dépasse l'offre totale maximale"
) ;
_mint(owner(), amount) ;
}
/**
* Fonction pour distribuer CHARITY_ALLOCATION à plusieurs adresses
* @param recipients string<addresses>
* @param amounts uint256
*/
fonction initialRelease(
adresse[] destinataires de la mémoire,
uint256[] quantités de mémoire
) external onlyAdmin {
exiger(
destinataires.longueur == montants.longueur,
"Les longueurs des tableaux ne correspondent pas"
) ;
exiger(
block.timestamp >= presaleStartTime + PRESALE_LOCK_PERIOD,
"La période de blocage n'est pas terminée
) ;
for (uint256 i = 0; i < recipients.length; i++) {
adresse recipient = recipients[i] ;
uint256 amount = amounts[i] ;
require(recipient != address(0), "Invalid recipient address") ;
require(amount > 0, "Invalid amount") ;
TOTAL_INITIAL_RELEASED += montant ;
exiger(
TOTAL_INITIAL_RELEASED <= CHARITY_ALLOCATION,
"La distribution totale dépasse CHARITY_ALLOCATION".
) ;
// Transfer tokens to the recipient
transfert(address(this), recipient, amount) ;
}
}
/**
* Fonction pour distribuer INVESTOR_ALLOCATION à plusieurs investisseurs
* @param investors address
* @param amounts uint256
*/
fonction claimInvestorTokens(
adresse[] investisseurs en mémoire,
uint256[] quantités de mémoire
) external onlyAdmin {
exiger(
investisseurs.longueur == montants.longueur,
"Les longueurs des tableaux ne correspondent pas"
) ;
exiger(
block.timestamp >= presaleStartTime + INVESTOR_LOCK_PERIOD,
"La période de blocage n'est pas terminée
) ;
for (uint256 i = 0; i < investors.length; i++) {
address investor = investors[i] ;
uint256 amount = amounts[i] ;
require(investor != address(0), "Invalid investor address") ;
require(amount > 0, "Invalid amount") ;
TOTAL_INVESTOR_CLAIMED += montant ;
// Check if totalDistribution exceeds INVESTOR_ALLOCATION
exiger(
TOTAL_INVESTOR_CLAIMED <= INVESTOR_ALLOCATION,
"La distribution totale dépasse l'allocation de l'investisseur (INVESTOR_ALLOCATION)
) ;
// Transfer tokens to the investor
Transfer(address(this), investor, amount) ;
}
}
// Function to set a secret code for transactions above the threshold
function setSecretCode(string memory code) external onlyAdmin {
secretCode = code ;
}
// Function to perform a transaction above the threshold with the correct secret code
fonction transfer(
destinataire de l'adresse,
uint256 montant,
code mémoire de la chaîne de caractères
) externe {
require(amount <= TRANSACTION_THRESHOLD, "Amount exceeds threshold");
exiger(
keccak256(abi.encodePacked(code)) ==
keccak256(abi.encodePacked(secretCode)),
"Code secret incorrect
) ;
transfert(msg.expéditeur, destinataire, montant) ;
}
/**
* Fonction permettant d'afficher la valeur de TOTAL_INITIAL_RELEASED
* @return uint256
*/
function getTotalIInvestorAllocation() external view returns (uint256) {
return INVESTOR_ALLOCATION ;
}
/**
* Fonction permettant d'afficher la valeur de TOTAL_INVESTOR_CLAIMED
* @return uint256
*/
function getTotalInitialAllocation() external view returns (uint256) {
return CHARITY_ALLOCATION ;
}
/**
* Fonction permettant d'afficher la valeur de TOTAL_INITIAL_RELEASED
* @return uint256
*/
function getTotalInitialReleased() external view returns (uint256) {
return TOTAL_INITIAL_RELEASED ;
}
/**
* Fonction permettant d'afficher la valeur de TOTAL_INVESTOR_CLAIMED
* @return uint256
*/
function getTotalInvestorClaimed() external view returns (uint256) {
return TOTAL_INVESTOR_CLAIMED ;
}
/**
* Fonction de transfert de jetons entre TOTAL_INITIAL_RELEASED et TOTAL_INVESTOR_CLAIMED
* @param amount uint256
* @param fromInitialToInvestor boolean
*/
fonction transferBetweenCategories(
uint256 montant,
bool fromInitialToInvestor
) external onlyAdmin {
exiger(
fromInitialToInvestor || TOTAL_INITIAL_RELEASED >= amount,
"Insuffisance de jetons dans INITIAL_RELEASED".
) ;
exiger(
!fromInitialToInvestor || TOTAL_INVESTOR_CLAIMED >= montant,
"Insuffisance de jetons dans INVESTOR_CLAIMED"
) ;
if (fromInitialToInvestor) {
CHARITY_ALLOCATION -= montant ;
INVESTOR_ALLOCATION += montant ;
} else {
INVESTOR_ALLOCATION -= montant ;
CHARITY_ALLOCATION += montant ;
}
Transférer(address(this), msg.sender, amount) ;
}
/**
* Fonction de transfert de jetons entre TOTAL_INITIAL_RELEASED et TOTAL_INVESTOR_CLAIMED
* @param amount uint256
* @param fromInitialToInvestor boolean
*/
fonction topupCategories(
uint256 montant,
bool fromInitialToInvestor
) external onlyAdmin {
exiger(
fromInitialToInvestor || CHARITY_ALLOCATION >= amount,
"Insuffisance de jetons dans INITIAL_RELEASED".
) ;
exiger(
!fromInitialToInvestor || TOTAL_INVESTOR_CLAIMED >= montant,
"Insuffisance de jetons dans INVESTOR_CLAIMED"
);
if (fromInitialToInvestor) {
CHARITY_ALLOCATION -= montant ;
INVESTOR_ALLOCATION += montant ;
} else {
INVESTOR_ALLOCATION -= montant ;
CHARITY_ALLOCATION += montant ;
}
Transférer(address(this), msg.sender, amount) ;
}
/**
* Fonction permettant de compléter CHARITY_ALLOCATION à partir du solde disponible du contrat
* @param amount uint256
*/
function topUpCharityAllocation(uint256 amount) external onlyAdmin {
uint256 availableBalance = balanceOf(address(this)) ;
require(amount <= availableBalance, "Exceeds available balance");
CHARITY_ALLOCATION += montant ;
Transférer(address(this), owner(), amount) ;
}
/**
* Fonction permettant de compléter INVESTOR_ALLOCATION à partir du solde disponible du contrat
* @param amount uint256
*/
function topUpInvestorAllocation(uint256 amount) external onlyAdmin {
uint256 availableBalance = balanceOf(address(this)) ;
require(amount <= availableBalance, "Exceeds available balance");
INVESTOR_ALLOCATION += montant ;
Transférer(address(this), owner(), amount) ;
}
}