CommunityIDO
The Community IDO smart contract is the smart contract that will manage the launch of the LennyVerse project token. We want LennyVerse to be the first story-oriented token to be launched by its community. This will reduce the “Pump Dump” and "Whales" risks when launching the project as every participant becomes an owner of the project and has interest in the success of the project in the long run. To do so, the development team innovates by introducing this novel concept of Community Initial DEX Offering (CIDO).
The main goal of the smart contract is to create a pull of contribution that will be ultimately be used to deploy the first liquidity pool of the Token itself. There are many "launch pad" platforms that propose this kind of service, but the main difference here is that the participant becomes an owner of the project by owning the First Liquidity Pair Token and collecting the the transaction fees when contributing to the community IDO. Moreover, the community IDO manages the liquidity released through time to limit risks coming with overflowing liquidity. This implementation only works with Uniswap V2 DEX implementation.
Inheritances
This contract inherits from the Context.sol and ReentrancyGuard.sol implementations from Openzeppelin as well as the Democratic.sol[Abstracts/Democratic.md].
Libraries
This contract uses the SafeERC20.sol library implemented by Openzeppelin.
State Variables, Enums and Structs
bool public successIDO : boolean that indicates if the goal has been reached
bool public abortedIDO : boolean that indicates if the community IDO has been aborded
bool public pauseIDO : boolean that indicates if the participation process has been paused or not
uint256 public immutable tokenProvided : amount of token provided to the liquidity pool (non decimal value)
uint256 public immutable tokenSoldOnDate : amount of token retreivable directly when the liquidity pool is created (non decimal value)
uint256 public immutable tokenLocked : amount of token that is progressively redistributed through time (non decimal value)
uint256 public remainingOnDate : amount of token retreivable directly when the liquidity pool is created (non decimal value)
uint256 public remainingOnLocked : amount of token locked after liquidity pool is created (non decimal value)
uint256 public availableContribution : remaining available contribution the community IDO can accept (non decimal value)
uint256 public contributionBalance : current contribution in the community IDO (non decimal value)
uint256 public immutable maxPurchasePerAddress : maximum amount of Contribution Token accepted per address (non decimal value)
uint256 public immutable goalSuccess : amount of contribution to reach so that we can call a success IDO (non decimal value)
uint256 public initialLiquidityToken : amount of liquidity token received by the smart contract in decimal
uint256 public pairTokenToDistribute : amount of liquidity token to re-distribute
uint32 public contractClaimId : current number of claim that has been perform for the contract
uint32 public claimIdDev : current number of claim retreived by the dev team
bool public liquidityDeployed : boolean that indicates true if the liquidity has been deployed and false otherwise
bool public liquidityPairCreated : boolean that indicates true if the liquidity pair has been created and false otherwise
bool public liquidityPoolCreated : boolean that indicates true if the liquidity pool has been created and false otherwise
uint256 public timeBeforeClaim : timestamp at which the community will be able to create another claim
uint256 public immutable claimLatency : minimum umber of seconds before the community can create a new claim
address public tokenAdd : address of project Token
address public contTokenAdd : address of contribution Token that will be used to create the first liquidity.
address public swapRouterAdd : address of DEX router smart contract. It has to be a DEX that uses Uniswap V2 implementation.
address public swapFactoryAdd : address of DEX factory smart contract. It has to be a DEX that uses Uniswap V2 implementation.
address public pairAdd : address of the pair liquidity token generated the factory smart contract.
Contribution() (Struct)
address participant: address of the participant
uint256 contributionAmount: the contribution amount given by the participant (non decimal)
bool tokensWithdrawn: indicates true if the participant claimed the "on date" part of his/her tokens.
uint32 claimId: represents the last claim Id that the participant took.
Claim() (Struct)
uint32 claimId: id of the claim
uint256 amountToken: the total amount of token that are claimable for this claim id.
uint256 amountPairToken: the total amount of pair token that are claimable for this claim id.
uint256 date: the block timestamp at which the claim has been instantiated.
Mappings
private contributions that maps the calling address to the struct Contribution;
private claims that maps the claim id uint32 to the struct Claim;
Events
ClaimCreated(uint32 id, uint256 amountToken, uint256 amountPairToken)
Event that is emit when the participant call successfully the createClaim function. It emits the id of the claim, the token amount and the pair token amount withdrawnable.
ContributionConfirmed(address participant, uint256 contributionAmount)
Event that is emit when the participant call successfully the participate function. It emits the address of the participant and the contribution amount for the call.
RemovalConfirmed(address participant, uint256 removalAmount)
Event that is emit when the participant call successfully the retreiveContribution function. It emits the address of the participant and the removal amount for the call.
TokenClaimedConfirmed(address participant, uint256 tokenClaimed)
Event that is emit when the participant call successfully the withdrawLENNY and claimTokens and function. It emits the address of the participant and the token amount for the call.
PairClaimedConfirmed(address participant, uint256 pairTokenClaimed)
Event that is emit when the participant call successfully the claimTokensDev and claimTokens func- tion. It emits the address of the participant and the pair token amount for the call.
Modifiers
activeIDO()
This modifier is used to determine the active period of an IDO
successfulIDO()
This modifier is used to determine the period after a successful IDO
failedIDO()
This modifier is used to determine the period after a failed IDO
isPaused()
This modifier is used to determine is the active period has been paused
Functions
receive() (external)
CheckParticipantInfo(address account) → struct CommunityIDO.Contribution (external)
Return the contribution information that has been saved in the smart contract for a specific participant.
CheckClaimInfo(uint32 claimNumber) → struct CommunityIDO.Claim (external)
Return the claim information that has been saved in the smart contract for a specific id number.
getDeployment() → bool (external)
Return the boolean value of liquidityDeployed.
setContractAddresses(address tokenAddress, address contTokenAddress, address swapFactoryAddress, address swapRouterAddress) (external)
The authorized administrator can call this function to set the contract addresses needed for the community IDO.
function setContractAddresses(
address tokenAddress,
address contTokenAddress,
address swapFactoryAddress,
address swapRouterAddress
) external demokratia() {
require(tokenAddress != address(0), 'token address must be a none 0 address');
tokenAdd = tokenAddress;
require(contTokenAddress != address(0), 'contribution token address must be a none 0 address');
contTokenAdd = contTokenAddress;
require(swapFactoryAddress != address(0), 'swap factory address must be a none 0 address');
swapFactoryAdd = swapFactoryAddress;
require(swapRouterAddress != address(0), 'swap router address must be a none 0 address');
swapRouterAdd = swapRouterAddress;
}
participate(uint256 amount) (external)
emit ContributionConfirmed event can be called when IDO is active and not in pause function called to participate into the community IDO
function participate(uint256 amount) external activeIDO() isPaused(){
IERC20 contTokenSC = IERC20(contTokenAdd);
uint amountDecimals = amount * 10 ** contTokenSC.decimals();
uint balanceToken = contTokenSC.balanceOf(_msgSender());
require(balanceToken>=amountDecimals, "Not enough token");
Contribution storage currentStatus = contributions[_msgSender()];
uint remainingContribution = maxPurchasePerAddress - currentStatus.contributionAmount;
require(remainingContribution > 0, 'You exceed the maximum contribution');
uint tokenAmount = amount;
if(tokenAmount > remainingContribution){
tokenAmount = remainingContribution;
}
if(tokenAmount >= availableContribution){
tokenAmount = availableContribution;
successIDO = true;
timeBeforeClaim = block.timestamp + claimLatency;
}
uint tokenAmountDecimals = tokenAmount * 10 ** contTokenSC.decimals();
uint totalContribution = tokenAmount + currentStatus.contributionAmount;
contributions[_msgSender()] = Contribution(_msgSender(), totalContribution, false, 0);
contributionBalance += tokenAmount;
availableContribution -= tokenAmount;
contTokenSC.safeTransferFrom(_msgSender(), address(this), tokenAmountDecimals);
emit ContributionConfirmed(_msgSender(), tokenAmount);
}
removeParticipation(uint256 amount) (external)
emit RemovalConfirmed event can be called when IDO is active and not in pause function called to remove contribution from the community IDO
function removeParticipation (uint256 amount) external activeIDO() isPaused() nonReentrant() {
Contribution storage currentStatus = contributions[_msgSender()];
require(currentStatus.contributionAmount > 0, 'only Participants');
require(amount <= currentStatus.contributionAmount, 'you claim more than you participated');
IERC20 contTokenSC = IERC20(contTokenAdd);
uint amountDecimals = amount * 10 ** contTokenSC.decimals();
contributions[_msgSender()].contributionAmount -= amount;
contributionBalance -= amount;
availableContribution += amount;
contTokenSC.safeTransfer(currentStatus.participant, amountDecimals);
emit RemovalConfirmed(_msgSender(), amount);
}
createLiquidityPair() (external)
It can only be called if IDO is successful restricted function to create the lp token pair.
function createLiquidityPair () external successfulIDO() demokratia() {
require(!liquidityPairCreated, 'Liquidity Pair has already been created');
liquidityPairCreated = true;
ISwapFactory factorySC = ISwapFactory(swapFactoryAdd);
pairAdd = factorySC.createPair(tokenAdd, contTokenAdd);
}
createLiquidityPool() (external)
It can only be called if IDO is successful. this call will push the tokens from the cido smart contract to the AMM restricted function to create the liquidity pool.
function createLiquidityPool () external successfulIDO() demokratia() {
require(!liquidityPoolCreated, 'Liquidity Pool already created');
require(liquidityPairCreated, 'Liquidity Pair not yet created');
ISwapRouter routerSC = ISwapRouter(swapRouterAdd);
IERC20 contTokenSC = IERC20(contTokenAdd);
IERC20 tokenSC = IERC20(tokenAdd);
uint decimalsContribution = contributionBalance * 10 ** contTokenSC.decimals();
uint decimalsTokenProvided = tokenProvided * 10 ** tokenSC.decimals();
liquidityPoolCreated = true;
contTokenSC.safeIncreaseAllowance(swapRouterAdd, decimalsContribution*2);
tokenSC.safeIncreaseAllowance(swapRouterAdd, decimalsTokenProvided*2);
uint time = block.timestamp + 300;
routerSC.addLiquidity(
tokenAdd, contTokenAdd,
decimalsTokenProvided, decimalsContribution,
0, 0,
address(this),
time
);
}
initializeLPToken(uint256 toRoundUp) (external)
restricted function to initialize the number ok LP token to redistribute to the community
function initializeLPToken(uint toRoundUp) external successfulIDO() demokratia() {
require(!liquidityDeployed, 'Deployment already done');
require(liquidityPoolCreated, 'Liquidity Pool not yet created');
IPairToken pair = IPairToken(pairAdd);
uint moduloValue = 10 ** 19;
uint newBalance = pair.balanceOf(address(this)) - toRoundUp;
require (newBalance % moduloValue == 0, "newBalance modulo 100 has to be 0");
require (toRoundUp < moduloValue, "Round up has to be less than the modulo value");
initialLiquidityToken = newBalance;
timeBeforeClaim = block.timestamp + claimLatency;
liquidityDeployed = true;
IERC20 tokenSC = IERC20(tokenAdd);
remainingOnDate = tokenSoldOnDate * 10 ** tokenSC.decimals();
bool isTransfered = pair.transfer(_msgSender(), toRoundUp);
if(isTransfered){
emit PairClaimedConfirmed(_msgSender(), toRoundUp);
}
}
withdrawToken() (external)
this is callable only when the liquidity pool have been created this function is called to withdrawn the on date part of the token
function withdrawToken() external successfulIDO() nonReentrant() {
require(liquidityDeployed, 'liquidity pool is not yet deployed');
require(remainingOnDate > 0, 'All liquidity has been taken');
Contribution storage currentStatus = contributions[_msgSender()];
require(currentStatus.contributionAmount > 0, 'only participants');
require(!currentStatus.tokensWithdrawn, 'tokens already withdrawned');
IERC20 tokenSC = IERC20(tokenAdd);
uint decimalsToken = tokenSoldOnDate * 10 ** tokenSC.decimals();
uint256 withdrawingAmount = decimalsToken * currentStatus.contributionAmount / contributionBalance;
currentStatus.tokensWithdrawn = true;
remainingOnDate -= withdrawingAmount;
bool isTransfered = tokenSC.transfer(currentStatus.participant, withdrawingAmount);
if(isTransfered){
emit TokenClaimedConfirmed(_msgSender(), withdrawingAmount);
}
}
createClaim() (external)
a claim can be create once every claimLatency seconds
this function is called to create a claim
function createClaim () external successfulIDO() {
require(block.timestamp > timeBeforeClaim, 'You can not create claim yet');
uint pairTokenToRemove = initialLiquidityToken / 10;
IERC20 tokenSC = IERC20(tokenAdd);
uint tokenToRemove = tokenLocked * 10 ** tokenSC.decimals() / 10;
pairTokenToDistribute += pairTokenToRemove;
remainingOnLocked += tokenToRemove;
contractClaimId += 1;
claims[contractClaimId] = Claim(contractClaimId, tokenToRemove, pairTokenToRemove, block.timestamp);
timeBeforeClaim = block.timestamp + claimLatency;
emit ClaimCreated(contractClaimId, tokenToRemove, pairTokenToRemove);
}
claimTokensDev() (external)
this restricted function is called by the development team to claim its LP token
function claimTokensDev () external successfulIDO() demokratia() nonReentrant() {
require(pairTokenToDistribute>0, 'No Tokens to redistribute');
require(contractClaimId > claimIdDev, 'dev team already got the claim');
uint32 newClaim = claimIdDev + 1;
Claim storage claimStatus = claims[newClaim];
uint pairTokenToClaim = 2 * claimStatus.amountPairToken / 5 ;
IPairToken pair = IPairToken(pairAdd);
pairTokenToDistribute -= pairTokenToClaim;
claimIdDev = newClaim;
bool isTransfered = pair.transfer(_msgSender(), pairTokenToClaim);
if(isTransfered){
emit PairClaimedConfirmed(_msgSender(), pairTokenToClaim);
}
}
claimTokens() (external)
this restricted function is called by the development team to claim its LP token
function claimTokens () external successfulIDO() nonReentrant() {
require(pairTokenToDistribute>0, 'No Tokens to redistribute');
Contribution storage currentStatus = contributions[_msgSender()];
require(currentStatus.contributionAmount > 0, 'only Participants');
require(contractClaimId > currentStatus.claimId, 'You already got your claims');
uint32 newClaim = currentStatus.claimId + 1;
Claim storage claimInfo = claims[newClaim];
uint pairTokenToClaim = claimInfo.amountPairToken * (3 * currentStatus.contributionAmount) / (contributionBalance * 5);
uint tokenToClaim = claimInfo.amountToken * currentStatus.contributionAmount / contributionBalance;
IPairToken pair = IPairToken(pairAdd);
IERC20 tokenSC = IERC20(tokenAdd);
pairTokenToDistribute -= pairTokenToClaim;
contributions[_msgSender()].claimId = newClaim;
remainingOnLocked -= tokenToClaim;
bool isPairTransfered = pair.transfer(_msgSender(), pairTokenToClaim);
tokenSC.safeTransfer(_msgSender(), tokenToClaim);
if(isPairTransfered){
emit PairClaimedConfirmed(_msgSender(), pairTokenToClaim);
}
emit TokenClaimedConfirmed(_msgSender(), tokenToClaim);
}
exitCIDO() (external)
this function is used to remove all contribution token if the CIDO is aborded
function exitCIDO () external failedIDO() nonReentrant() {
Contribution storage currentStatus = contributions[_msgSender()];
require(currentStatus.contributionAmount > 0, 'only Participants');
require(!currentStatus.tokensWithdrawn, 'tokens already withdrawned');
IERC20 contTokenSC = IERC20(contTokenAdd);
uint amountDecimals = currentStatus.contributionAmount * 10 ** contTokenSC.decimals();
currentStatus.tokensWithdrawn = true;
contTokenSC.safeTransfer(currentStatus.participant, amountDecimals);
emit RemovalConfirmed(_msgSender(), amountDecimals);
}
sendBackCoin(address payable recipient, uint256 amount) (external)
this restrictive function is only used if a user send Coin by error into the contract
function sendBackCoin(address payable recipient, uint amount) external demokratia() {
require(recipient != address(0), 'None 0 Address');
payable(recipient).transfer(amount);
}
sendBackAnyToken(address wrongTokenAddress, address recipient, uint256 amount) (external)
this restrictive function is only used if a user send tokens by error into the contract
function sendBackAnyToken(address wrongTokenAddress, address recipient, uint amount) external demokratia() {
require(wrongTokenAddress != tokenAdd, 'Can not send Lenny Through this function');
require(wrongTokenAddress != contTokenAdd, 'Can not send BUSD Through this function');
require(recipient != address(0), 'None 0 Address');
IERC20 tokenSC = IERC20(wrongTokenAddress);
tokenSC.safeTransfer(recipient, amount);
}
stopIDO() (external)
this restrictive function is is used to stop the CIDO
function stopIDO() external demokratia() activeIDO() {
pauseIDO = !pauseIDO;
}
abortIDO() (external)
this restrictive function is is used to abord the CIDO
function abortIDO() external demokratia() activeIDO() {
abortedIDO = !abortedIDO;
}
constructor(uint256 _tokenProvided, uint256 _tokenSoldOnDate, uint256 _tokenLocked, uint256 _goalSuccess, uint256 _maxPurchasePerAddress, uint256 _claimLatency, address _doscAddress) (public)
constructor(uint _tokenProvided,
uint _tokenSoldOnDate,
uint _tokenLocked,
uint _goalSuccess,
uint _maxPurchasePerAddress,
uint _claimLatency,
address _doscAddress
) Democratic(_doscAddress) {
tokenProvided = _tokenProvided;
tokenSoldOnDate = _tokenSoldOnDate;
tokenLocked = _tokenLocked;
goalSuccess = _goalSuccess;
availableContribution = _goalSuccess;
maxPurchasePerAddress = _maxPurchasePerAddress;
claimLatency = _claimLatency;
}