Contract Name:
SDLGECreator
Contract Source Code:
File 1 of 1 : SDLGECreator
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
// optimize 100
/// Standard IERC20 interface
interface IERC20 {
function totalSupply() external view returns (uint256);
function decimals() external view returns (uint8);
function balanceOf(address user) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
interface Reader {
function isAdmin(address addy) external view returns (bool);
function superAdmin(address addy) external view returns (bool);
function isSetter(address addy) external view returns (bool);
function fegAddress() external view returns(address);
function uniswapV2Pair() external view returns(address);
function sdDepAddy() external view returns (address);
function breaker() external view returns (bool); //circuit breaked in case of an exploit to handle
function setWhitelistContract(address addy, bool _bool) external;
function currentRouter() external view returns(address);
function wETH() external view returns(address);
function LEAPDepAddy() external view returns(address);
function isProtocol(address addy) external view returns(bool);
}
interface sdep{
function sdOwner() external view returns(address);
function backingLogicAddress() external view returns(address);
function backingAsset() external view returns(address);
function setLGE(address addy) external;
function endLGE() external;
function endLGE(address addy) external;
function setWLOff(address token, bool _bool) external;
function liquifyForBacking(uint256 amt) external returns(uint tokenAmountOut);
function liquifyForAllBacking(address user, uint256 amt) external returns(uint tokenAmountOut);
function oneTokentoBacking(uint amt) external view returns(uint256);
function frontRun(address who, uint256 range, uint256 slip) external view returns(bool yes);
function tokenBackingPair(address token) external view returns(address);
function setTick(address who) external;
function range() external view returns(uint256,uint256);
}
interface spre {
function setInitial(address _sd, uint256 _supplyForLP, uint256 _devShare, uint256 _rate, uint256 _maxBuy, uint256 _startTime, uint256 _durationDays, uint256 _backingShare) external;
function setVesting(uint256 _vestingDelay, uint256 _vestingRate) external;
function dev() external view returns(address);
function fee(address user) external view returns(uint256);
}
interface Dataport {
function DATA_READ() external view returns(address);
}
contract SDLGECreator {
address public dataport = 0xcCeD1a96321B2B2a06E8F3F4B0B883dDD059968c;
function createLGE(address owner, address backing, address token) external returns(address SDP) {
require(sdep(token).sdOwner() == owner && Reader(Dataport(dataport).DATA_READ()).LEAPDepAddy() == msg.sender, "not owner");
SDP = address(new SDLGE(msg.sender, owner, backing, block.timestamp, token));
return SDP;
}
}
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insuff");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
/// Transfer Helper to ensure the correct transfer of the tokens or ETH
library SafeTransfer {
using Address for address;
/** Safe Transfer asset from one wallet with approval of the wallet
* @param erc20: the contract address of the erc20 token
* @param from: the wallet to take from
* @param amount: the amount to take from the wallet
**/
function _pullUnderlying(IERC20 erc20, address from, uint amount) internal
{
safeTransferFrom(erc20,from,address(this),amount);
}
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/** Safe Transfer asset to one wallet from within the contract
* @param erc20: the contract address of the erc20 token
* @param to: the wallet to send to
* @param amount: the amount to send from the contract
**/
function _pushUnderlying(IERC20 erc20, address to, uint amount) internal
{
safeTransfer(erc20,to,amount);
}
/** Safe Transfer ETH to one wallet from within the contract
* @param to: the wallet to send to
* @param value: the amount to send from the contract
**/
function safeTransferETH(address to, uint256 value) internal {
(bool success,) = to.call{value : value}(new bytes(0));
require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 op");
}
}
function safeApprove(IERC20 token, address spender, uint256 value)
internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0)
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
}
interface SD2 {
function sdOwner() external view returns(address);
function balanceOf(address account) external view returns(uint256);
function BackingLogicAddress() external view returns(address);
function uniswapV2Pair() external view returns(address);
function setLGE() external;
function LGE() external view returns(bool);
}
interface Swap {
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external;
function deposit() external payable;
function withdraw(uint wad) external;
function getPair(address token0, address token1) external view returns(address);
function factory() external view returns(address);
function createPair(address tokenA, address tokenB) external returns (address pair);
}
contract SDLGE {
mapping(address => LGErs) public buyer; // user entry data
mapping(address => LGErsLP) public buyerLP; // user LP data
address public SD; // Token which is staked
address public uniswapV2Pair;
address public uniswapV2PairFEG;
address private constant dead = 0x000000000000000000000000000000000000dEaD;
address public deployer; // LGE deployer
address public owner; // LGE creator
address public backingAsset; // Backing asset of the SmartDeFi
address[] public entered; // All LGE Entrants
address[] public whitelist; // All whitelisted addresses
uint256 public startTime; // LGE Starttime
uint256 public endTime; // LGE Endtime
uint256 public duration; // duration of LGE in days
uint256 public raised; // Total raised
uint256 public sold; // Tokens allocated for Liquidity
uint256 public rateCurrent; // LP price rate
uint256 public totalLP0; // Total Main LP
uint256 public totalLP1; // Total FEG LP
uint256 public totalClaimedLP0; // Total claimed Main LP
uint256 public totalClaimedLP1; // Total claimed FEG LP
uint256 public whiteListEntered; // Total whitelist spots that have entered LGE
uint256 public devShare; // Share of vested LP for dev
uint256 public backingShare; // Percentage of LGE raised towards backing. 100 raised with 20 backingShare = 20 to backing 80 to LP
uint256 public totalTokensVested; // Total vested tokens by dev
uint256 private _status = 1;
uint256 public soldOutTime;
bool private setup; // LGE base setup
bool public vestSetup; // LP Vesting setup
bool public completeSetup; // All set up complete
bool public started; // LGE Started
bool public ended; // LGE ended
bool public aborted; // LGE aborted, if endtime and softcap not met can abort.
bool public soldOut; // LGE is soldout
bool public SAFU; // LGE has lowest risk of loss, 100% total supply given to LP and 10% devShare or under.
bool eu;
LGE public pre; // Main LGE base info
tokenVesting[] public vest; // Vested tokens of dev
event EnterLGE(address buyer, uint256 amount);
event ExitLGE(address buyer, uint256 amount);
event EndLGE();
event Claim0(address addy, uint256 LP0);
event Claim1(address addy, uint256 LP1);
event vestedToken(uint256 amount, uint256 unlockTime, uint256 vestID);
event WithdrawVestedToken(uint256 amount, uint256 vestID);
event LockLP(address user, uint256 unlocktime);
event EditLGE(uint256 choice, uint256 input);
constructor(address dep, address own, address _backing, uint256 time, address sd) {
deployer = dep;
owner = own;
backingAsset = _backing;
SD = sd; time;
}
receive() external payable {
if(started && !soldOut && !ended) {
if(!eu) {
enterLGE(msg.sender);
}
}
}
struct LGE {
uint256 supplyForLP;
uint256 softCap;
uint256 hardCap;
uint256 maxBuy;
uint256 vestingDelay;
uint256 vestingRounds;
uint256 vestingStartTime;
bool whiteListEnabled;
uint256 whiteListCount;
}
struct tokenVesting {
uint256 vestingAmount;
uint256 vestingEnd;
uint256 vestingClaimed;
}
struct LGErs {
uint256 totAmtIn;
uint256 tok;
bool returned;
bool aborted;
bool whiteListed;
}
struct LGErsLP {
uint256 lpInitial0;
uint256 lpInitial1;
uint256 lpClaimed0;
uint256 lpClaimed1;
uint256 lp0Rate;
uint256 lp1Rate;
uint256 lastClaimedLP;
}
modifier nonReentrant() {
require(_status == 1, "reentrant");
_status = 0;
_;
_status = 1;
}
function DATA_READ() public view returns(address) {
return Dataport(0xcCeD1a96321B2B2a06E8F3F4B0B883dDD059968c).DATA_READ();
}
function FEG() public view returns(address) {
return Reader(DATA_READ()).fegAddress();
}
function fegPair() internal view returns(address) {
return Reader(FEG()).uniswapV2Pair();
}
function UNISWAP_V2_ROUTER() public view returns(address) {
return Reader(DATA_READ()).currentRouter();
}
function wETH() public view returns(address) {
return Reader(DATA_READ()).wETH();
}
// ********************** Start LGE Base Section
// function to end LGE on hardcap
function close(uint256 minFEGOut, uint256 minBackOut) internal {
address feg = FEG();
(uint256 range, uint256 slip) = sdep(deployer).range();
if(range > 0) {
require(!sdep(Reader(DATA_READ()).sdDepAddy()).frontRun(feg, range, slip), "Front run");
}
if(backingShare > 0) {
settleBacking(minBackOut);
}
address weth = wETH();
uint256 FEGfee = IERC20(weth).balanceOf(address(this)) * spre(deployer).fee(address(this)) / 1000;
uint256 tokensForFEGPair = pre.supplyForLP * spre(deployer).fee(address(this)) / 1000;
uint256 tokensForPair = pre.supplyForLP - tokensForFEGPair;
SafeTransfer.safeApprove(IERC20(weth), UNISWAP_V2_ROUTER(), FEGfee);
address[] memory path = new address[](2);
path[0] = weth;
path[1] = feg;
Swap(UNISWAP_V2_ROUTER()).swapExactTokensForTokensSupportingFeeOnTransferTokens(
FEGfee,
minFEGOut,
path,
address(this),
block.timestamp
);
SafeTransfer._pushUnderlying(IERC20(SD), uniswapV2Pair, tokensForPair);
SafeTransfer._pushUnderlying(IERC20(weth), uniswapV2Pair, IERC20(weth).balanceOf(address(this)));
totalLP0 = Swap(uniswapV2Pair).mint(address(this));
totalLP0 = totalLP0 * 98 / 100;
buyerLP[owner].lpInitial0 = devShare > 0 ? totalLP0 * devShare / 100 : 0;
buyerLP[owner].lp0Rate = buyerLP[owner].lpInitial0 / pre.vestingRounds;
SafeTransfer._pushUnderlying(IERC20(feg), uniswapV2PairFEG, IERC20(feg).balanceOf(address(this)));
SafeTransfer._pushUnderlying(IERC20(SD), uniswapV2PairFEG, tokensForFEGPair);
totalLP1 = Swap(uniswapV2PairFEG).mint(address(this));
totalLP1 = totalLP1 * 98 / 100;
buyerLP[owner].lpInitial1 = devShare > 0 ? totalLP1 * devShare / 100 : 0;
buyerLP[owner].lp1Rate = buyerLP[owner].lpInitial1 / pre.vestingRounds;
pre.vestingStartTime = block.timestamp;
emit EndLGE();
}
// function called when LGE reached hardCap or endTime passed and above softCap
function endLGE(uint256 minFEGOut, uint256 minBackOut) external {
require(!aborted && !ended && started, "already");
require(msg.sender == owner || Reader(DATA_READ()).isAdmin(msg.sender));
if(block.timestamp > endTime && raised < pre.softCap) {
endTime = block.timestamp + duration;
}
else{
require(soldOut || (block.timestamp > endTime && raised >= pre.softCap));
close(minFEGOut, minBackOut);
sdep(deployer).endLGE(SD);
ended = true;
endTime = block.timestamp;
}
}
// function to abortLGE must not be already aborted, not ended and raised is below softCap.
function abortLGE() external {
require(!aborted && !ended, "already");
require(raised < pre.softCap, "scap");
aborted = true;
saveTokens(SD);
sdep(deployer).setWLOff(SD, false);
sdep(deployer).endLGE(SD);
SAFU = false;
emit EndLGE();
}
// editLGE enter choice of option below with input data to change, only LGE creator can call. Must not be started or aborted
function editLGE(uint256 choice, uint256 input) external {
require(msg.sender == owner);
require((!started && !aborted), "started");
if(choice == 0) {
require(input >= block.timestamp + 15 minutes, "past");
startTime = input;
endTime = startTime + duration;
}
if(choice == 1) {
require(input <= 90 && input >= 1, " 90/1");
duration = input * 1 days;
endTime = startTime + duration;
}
if(choice == 2) {
require(input <= 50, "50");
devShare = input;
}
if(choice == 3) {
require(input <= 50, "50/1");
backingShare = input;
}
if(choice == 4) {
pre.hardCap = input;
rateCurrent = pre.supplyForLP / input;
pre.softCap = input / 3;
pre.maxBuy = pre.maxBuy > input ? input : pre.maxBuy;
}
if(choice == 5) {
require(input <= IERC20(SD).totalSupply() && input <= (type(uint128).max - 1), "total supply");
uint256 a = pre.supplyForLP;
if(input > a) {
SafeTransfer._pullUnderlying(IERC20(SD), msg.sender, input - pre.supplyForLP);
}
if(input <= a) {
SafeTransfer._pushUnderlying(IERC20(SD), msg.sender, pre.supplyForLP - input);
}
pre.supplyForLP = input;
require(pre.supplyForLP > 0, "zero");
pre.hardCap = pre.supplyForLP / rateCurrent;
pre.softCap = pre.hardCap / 3;
SAFU = (input >= (IERC20(SD).totalSupply() * 98 / 100) && devShare < 11) ? true : false;
}
if(choice == 6) {
require(input <= 90 && input >= 7, "90/7");
pre.vestingDelay = input * 1 days;
}
if(choice == 7) {
require(input <= 20 && input >= 5, "20/5");
pre.vestingRounds = input;
}
if(choice == 8) {
require(input >= 1e16, "1e16");
pre.maxBuy = input > pre.hardCap ? pre.hardCap : input;
}
emit EditLGE(choice, input);
}
// initial set up function
function setInitial(uint256 _supplyForLP, uint256 _devShare, uint256 _hardcap, uint256 _maxbuy, uint256 _startTime, uint256 _backingShare) external {
require(!setup && msg.sender == deployer);
owner = SD2(SD).sdOwner();
uniswapV2Pair = SD2(SD).uniswapV2Pair();
setup = true;
pre.supplyForLP = _supplyForLP;
pre.hardCap = _hardcap;
rateCurrent = _supplyForLP / _hardcap;
pre.softCap = _hardcap / 3;
pre.maxBuy = _maxbuy;
startTime = _startTime;
duration = 90 * 1 days;
endTime = _startTime + duration;
devShare = _devShare;
backingShare = _backingShare;
}
// function to auto start LGE after starttime
function Started() public {
if(!started) {
if(block.timestamp > startTime){
started = true;
}
}
}
// ********************** End LGE Base Section
// ********************** Token Vesting Section
// LGE owner can vest the SD token here.
function vestToken(uint256[] memory amount, uint256[] memory _vestingEnd) external nonReentrant{
require(msg.sender == owner);
require(amount.length == _vestingEnd.length, "length");
uint256 a;
for(uint256 i = 0; i < amount.length; i++) {
a += amount[i];
vest.push(tokenVesting(amount[i],_vestingEnd[i],0));
emit vestedToken(amount[i], _vestingEnd[i], i);
}
totalTokensVested += a;
SafeTransfer._pullUnderlying(IERC20(SD), msg.sender, a);
}
// LGE owner can withdraw expired vested tokens
function withdrawVestedToken(uint256[] memory vestID) external nonReentrant{
require(msg.sender == owner);
uint256 totalOut;
uint256 a;
for(uint256 i = 0; i < vestID.length; i++) {
require((block.timestamp > vest[vestID[i]].vestingEnd || aborted) && vest[vestID[i]].vestingClaimed == 0, "already");
a = vest[vestID[i]].vestingAmount;
totalOut += a;
vest[vestID[i]].vestingClaimed = a;
emit WithdrawVestedToken(a, vestID[i]);
}
totalTokensVested -= totalOut;
SafeTransfer._pushUnderlying(IERC20(SD), owner, totalOut);
}
// Initial setup function
function setVesting(uint256 _vestingDelay, uint256 _vestingRate, address fp) external {
require(!vestSetup && msg.sender == deployer);
require(_vestingDelay <= 90 && _vestingDelay >= 7, "90/7");
vestSetup = true;
pre.vestingDelay = _vestingDelay * 1 days; // enter in days
pre.vestingRounds = _vestingRate;
completeSetup = true;
uniswapV2PairFEG = fp;
if(pre.supplyForLP >= (IERC20(SD).totalSupply() * 98 / 100) && devShare < 11){
SAFU = true;
}
}
// function to check vesting round
function vestingRound() public view returns(uint256) {
uint256 a = pre.vestingDelay;
uint256 round = 1;
for(uint256 i = 0; i < pre.vestingRounds; i++) {
if(block.timestamp > pre.vestingStartTime + (round * a)){
round += 1;
}
if(block.timestamp <= pre.vestingStartTime + (round * a) || round == pre.vestingRounds){
break;
}
}
return round;
}
// ********************** End Token Vesting Section
// ********************** Start LGE Whitelist Section
function whitelistLength() public view returns(uint256) {
return whitelist.length;
}
// function to enable whitelist LGE must be done before LGE has started
function setWhitelist(bool _bool) external {
require(msg.sender == owner);
require(!started || (started && pre.whiteListEnabled && !_bool));
pre.whiteListEnabled = _bool;
}
// function for adding bulk initial whitelist data
function addBulkWhitelist(address[] memory _list) external {
require(msg.sender == owner);
require(!started);
pre.whiteListEnabled = true;
uint256 length = _list.length;
for(uint256 i = 0; i < length; i++) {
require(!buyer[_list[i]].whiteListed && _list[i] != owner, "already");
buyer[_list[i]].whiteListed = true;
pre.whiteListCount += 1;
whitelist.push(_list[i]);
}
}
// function to edit whitelist spot
function editWhitelist(address user, bool _live) external {
require(msg.sender == owner && user != owner);
if(!buyer[user].whiteListed) { // if not already whitelisted require _live = true and LGE not started
require(_live);
require(!started);
pre.whiteListEnabled = true;
whitelist.push(user);
}
require(pre.whiteListEnabled);
if(buyer[user].whiteListed) { // if already whitelisted require _live = false and LGE started with user not have entered yet.
require(!_live);
require(buyer[user].totAmtIn == 0);
}
buyer[user].whiteListed = _live;
pre.whiteListCount = buyer[user].whiteListed ? pre.whiteListCount + 1 : pre.whiteListCount - 1;
pre.whiteListEnabled = whiteListEntered == pre.whiteListCount ? false : true;
}
// ********************** End LGE Whitelist Section
// ********************** Enter LGE Section
// check if LGE has whitelist enabled, if true then require user is whitelisted. After whiteListCount has reached whiteListEntered allow public to enter LGE
function preCheck(address user) internal {
if(pre.whiteListEnabled){
require(buyer[user].whiteListed, "not wl");
whiteListEntered = buyer[user].totAmtIn == 0 ? whiteListEntered + 1 : whiteListEntered;
if(whiteListEntered == pre.whiteListCount){pre.whiteListEnabled = false;}
}
}
// LGE entry quote
function quoteBuy(uint256 amount) external view returns(uint256 quote) {
quote = amount * rateCurrent;
}
// enter LGE with ETH
function enterLGE(address user) public payable nonReentrant {
Started();
if(!Reader(DATA_READ()).isProtocol(msg.sender)) {
user = msg.sender;
}
require(!soldOut && !aborted && started, "sout");
require(!buyer[user].returned, "exited");
require(msg.value > 0, "no amount");
if(block.timestamp < endTime && endTime < block.timestamp + 5 minutes) {
endTime += 5 minutes;
}
require(block.timestamp < endTime && user != owner, "time up");
preCheck(user);
Swap(wETH()).deposit{value:msg.value}();
raised += msg.value;
if(buyer[user].totAmtIn == 0) {
entered.push(user);
}
buyer[user].totAmtIn += msg.value;
require(buyer[user].totAmtIn <= pre.maxBuy, "MB");
uint256 amtBought = msg.value * rateCurrent;
buyer[user].tok += amtBought;
sold += amtBought;
require(raised <= pre.hardCap, "oversold");
if(raised == pre.hardCap){
soldOut = true;
soldOutTime = block.timestamp;
}
emit EnterLGE(user, msg.value);
}
// enter LGE with token, input token to sell, amount to sell and minAmountOut of ETH for selling token for entering LGE with ETH.
function enterLGEWithToken(address user, address token, uint256 amount, uint256 minAmountOut) external nonReentrant returns(uint256) {
Started();
if(!Reader(DATA_READ()).isProtocol(msg.sender)) {
user = msg.sender;
}
require(token != SD, "invalid token");
require(amount > 0, "no amount");
require(!buyer[user].returned, "exited");
address weth = wETH();
require(!soldOut && !aborted && started, "sout");
if(block.timestamp < endTime && endTime < block.timestamp + 5 minutes) {
endTime += 5 minutes;
}
require(block.timestamp < endTime && user != owner, "time up");
preCheck(user);
if(buyer[user].totAmtIn == 0) {
entered.push(user);
}
uint256 beforeToken = IERC20(token).balanceOf(address(this));
SafeTransfer.safeTransferFrom(IERC20(token), msg.sender, address(this), amount);
uint256 amtToken = IERC20(token).balanceOf(address(this)) - beforeToken;
uint256 amtE = IERC20(weth).balanceOf(address(this));
convert(token, amtToken, minAmountOut);
uint256 amtIn = IERC20(weth).balanceOf(address(this)) - amtE;
require(amtIn > 0, "amtIn 0");
raised += amtIn;
buyer[user].totAmtIn += amtIn;
require(buyer[user].totAmtIn <= pre.maxBuy, "MB");
uint256 amtBought = amtIn * rateCurrent;
buyer[user].tok += amtBought;
sold += amtBought;
require(raised <= pre.hardCap, "oversold");
if(raised == pre.hardCap){
soldOut = true;
soldOutTime = block.timestamp;
}
emit EnterLGE(user, amtIn);
return amtIn;
}
// convert token from enterLGEWithToken
function convert(address token, uint256 amtToken, uint256 minAmountOut) internal {
SafeTransfer.safeApprove(IERC20(token), UNISWAP_V2_ROUTER(), amtToken);
address[] memory path = new address[](2);
path[0] = token;
path[1] = wETH();
Swap(UNISWAP_V2_ROUTER()).swapExactTokensForTokensSupportingFeeOnTransferTokens(
amtToken,
minAmountOut,
path,
address(this),
block.timestamp
);
}
// function for LGE participant to exit LGE with requirements listed below
function emergencyExit() external nonReentrant {
require(buyer[msg.sender].totAmtIn > 0, "no balance");
require(totalLP0 == 0, "sout");
if(soldOut) {
require(block.timestamp > soldOutTime + 72 hours);
}
require(!buyer[msg.sender].returned && !buyer[msg.sender].aborted, "ret/abt");
if(aborted) {
buyer[msg.sender].aborted = true;
}
buyer[msg.sender].returned = true;
uint256 ta = buyer[msg.sender].totAmtIn;
raised -= ta;
uint256 s = buyer[msg.sender].tok;
sold -= s;
buyer[msg.sender].totAmtIn = 0;
buyer[msg.sender].tok = 0;
if(buyer[msg.sender].whiteListed) {
whiteListEntered -= 1;
}
eu = true;
Swap(wETH()).withdraw(ta);
eu = false;
SafeTransfer.safeTransferETH(msg.sender, ta);
emit ExitLGE(msg.sender, ta);
}
// ********************** End Enter LGE Section
// ********************** Shares Section
// after LGE completes user enableShares to activate vesting
function enableShares() external nonReentrant {
require(totalLP0 > 0 && msg.sender != owner, "no lp");
require(buyerLP[msg.sender].lpInitial0 == 0, "claimed");
buyerLP[msg.sender].lpInitial0 = (totalLP0 * (100 - devShare) / 100) * buyer[msg.sender].totAmtIn / raised;
buyerLP[msg.sender].lpInitial1 = (totalLP1 * (100 - devShare) / 100) * buyer[msg.sender].totAmtIn / raised;
buyerLP[msg.sender].lp0Rate = buyerLP[msg.sender].lpInitial0 / pre.vestingRounds;
buyerLP[msg.sender].lp1Rate = buyerLP[msg.sender].lpInitial1 / pre.vestingRounds;
}
// check total vested LP for withdraw
function checkShare(address user) public view returns(uint256 val0, uint256 val1) {
val0 = 0; val1 = 0;
uint256 vr = vestingRound();
if(buyerLP[user].lpInitial0 > 0) {
if(block.timestamp > buyerLP[msg.sender].lastClaimedLP + 1 days) {
if((buyerLP[user].lp0Rate * vr > buyerLP[user].lpClaimed0)) {
uint256 val00 = (buyerLP[user].lp0Rate * vr) - buyerLP[user].lpClaimed0 > buyerLP[user].lp0Rate ? buyerLP[user].lp0Rate : (buyerLP[user].lp0Rate * vr) - buyerLP[user].lpClaimed0;
val0 = val00 + buyerLP[user].lpClaimed0 > buyerLP[user].lpInitial0 ? buyerLP[user].lpInitial0 - buyerLP[user].lpClaimed0 : val00;
uint256 val11 = (buyerLP[user].lp1Rate * vr) - buyerLP[user].lpClaimed1 > buyerLP[user].lp1Rate ? buyerLP[user].lp1Rate : (buyerLP[user].lp1Rate * vr) - buyerLP[user].lpClaimed1;
val1 = val11 + buyerLP[user].lpClaimed1 > buyerLP[user].lpInitial1 ? buyerLP[user].lpInitial1 - buyerLP[user].lpClaimed1 : val11;
}
}
}
}
/*
* @param val0: ETH value of main LP
* @param val1: FEG of fegPair LP
* @param back0: backing value of SD side of LP from main pair
* @param back1: backing value of FEG side of LP from fegPair
*/
function claimableShareValue(address user) public view returns(uint256 val0, uint256 val1, uint256 back0, uint256 back1) {
uint256 max0 = buyerLP[user].lp0Rate * vestingRound();
uint256 max1 = buyerLP[user].lp1Rate * vestingRound();
if(max0 > buyerLP[user].lpClaimed0) {
address weth = wETH();
uint256 totLP0 = IERC20(uniswapV2Pair).totalSupply();
uint256 totLP1 = IERC20(uniswapV2PairFEG).totalSupply();
(max0, max1) = checkShare(user);
back0 = sdep(sdep(SD).backingLogicAddress()).oneTokentoBacking(IERC20(SD).balanceOf(uniswapV2Pair) * max0 / totLP0);
back1 = sdep(sdep(SD).backingLogicAddress()).oneTokentoBacking(IERC20(SD).balanceOf(uniswapV2PairFEG) * max1 / totLP1);
val0 = IERC20(weth).balanceOf(uniswapV2Pair) * max0 / totLP0;
val1 = IERC20(FEG()).balanceOf(uniswapV2PairFEG) * max1 / totLP1;
}
}
// total of all unclaimed LP
function totalUnclaimedLP() external view returns(uint256 max0, uint256 max1) {
max0 = totalLP0 > totalClaimedLP0 ? totalLP0 - totalClaimedLP0 : 0;
max1 = totalLP1 > totalClaimedLP1 ? totalLP1 - totalClaimedLP1 : 0;
}
// claim share with amount input as % of claimable share, 100 = 100% of claimable share, if share is over vestingRate then max claim sets to _vestingRate
function claimShare(uint256 amount) public nonReentrant {
require(ended && !aborted, "not ended");
require(amount <= 100, "100% max");
uint256 amt0;
uint256 amt01;
address weth = wETH();
bool h;
require(block.timestamp > buyerLP[msg.sender].lastClaimedLP + 1 days); // 1 day cool down, also used for ensuring of locking LP
(amt0,amt01) = checkShare(msg.sender);
if(msg.sender == owner) {
if(buyerLP[msg.sender].lpClaimed0 == 0) {
amt0 = amt0 * (51 - devShare) / 10;
h = true;
}
}
uint256 amt0a = amt0 * amount / 100;
buyerLP[msg.sender].lpClaimed0 += amt0a;
require((buyerLP[msg.sender].lpClaimed0 <= buyerLP[msg.sender].lpInitial0), "LPO");
SafeTransfer._pushUnderlying(IERC20(uniswapV2Pair), uniswapV2Pair, amt0a);
Swap(uniswapV2Pair).burn(address(this));
totalClaimedLP0 += amt0a;
emit Claim0(msg.sender, amt0a);
if(msg.sender == owner) {
if(buyerLP[msg.sender].lpClaimed1 == 0) {
amt01 = amt01 * (51 - devShare) / 10;
h = true;
}
}
uint256 amt01a = amt01 * amount / 100;
require((amt0a <= buyerLP[msg.sender].lp0Rate && amt01a <= buyerLP[msg.sender].lp1Rate) || (msg.sender == owner && h), "AM");
buyerLP[msg.sender].lpClaimed1 += amt01a;
require(buyerLP[msg.sender].lpClaimed1 <= buyerLP[msg.sender].lpInitial1, "LP1");
SafeTransfer._pushUnderlying(IERC20(uniswapV2PairFEG), uniswapV2PairFEG, amt01a);
Swap(uniswapV2PairFEG).burn(address(this));
preback(msg.sender);
Swap(weth).withdraw(IERC20(weth).balanceOf(address(this)));
SafeTransfer.safeTransferETH(msg.sender, address(this).balance);
address f = FEG();
SafeTransfer._pushUnderlying(IERC20(f), msg.sender, IERC20(f).balanceOf(address(this)));
totalClaimedLP1 += amt01a;
emit Claim1(msg.sender, amt01a);
buyerLP[msg.sender].lastClaimedLP = block.timestamp;
}
// ********************** End Shares Section
// ********************** LP section
// allow any user or dev to lock their LP, lastClaimedLP will add days which will trigger the cooldown on claim function blocking claiming
function lockLP(uint256 _days) external {
require(buyerLP[msg.sender].lpInitial0 > 0 && _days > 0, "no hold or days");
require(block.timestamp + (_days * 1 days) > buyerLP[msg.sender].lastClaimedLP, "must be in future");
buyerLP[msg.sender].lastClaimedLP = block.timestamp + (_days * 1 days);
emit LockLP(msg.sender, buyerLP[msg.sender].lastClaimedLP);
}
// check LP lock
function getLPLock(address user) external view returns(bool, uint256) {
bool a = buyerLP[user].lastClaimedLP > block.timestamp ? true : false;
uint256 b = a ? buyerLP[user].lastClaimedLP : 0;
return (a, b);
}
// ********************** End LP Section
// ********************** Start Backing Section
// after LGE has reached hardCap settle backing share
function settleBacking(uint256 minBackOut) private {
uint256 backing = raised * backingShare / 100;
address backingPool = sdep(SD).backingLogicAddress();
address weth = wETH();
// since we cannot track backing pool balance history before SD ticks enable we cannot check for front run.
if(backingAsset != weth) {
SafeTransfer.safeApprove(IERC20(weth), UNISWAP_V2_ROUTER(), backing);
address[] memory path;
path = new address[](2);
path[0] = weth;
path[1] = backingAsset;
// BUY and SELL fee may apply
Swap(UNISWAP_V2_ROUTER()).swapExactTokensForTokensSupportingFeeOnTransferTokens(
backing,
minBackOut,
path,
address(this),
block.timestamp
);
SafeTransfer._pushUnderlying(IERC20(backingAsset), backingPool, IERC20(backingAsset).balanceOf(address(this)));
}
if(backingAsset == weth) {
SafeTransfer._pushUnderlying(IERC20(weth), backingPool, backing);
}
}
// call to burn SD from LP on claim function
function preback(address user) internal {
address backingPool = sdep(SD).backingLogicAddress();
uint256 b = IERC20(SD).balanceOf(address(this)) - totalTokensVested;
uint256 c = IERC20(backingAsset).balanceOf(backingPool);
if(c < 1e15) {
SafeTransfer._pushUnderlying(IERC20(SD), dead, b);
}
if(c >= 1e15) {
SafeTransfer.safeApprove(IERC20(SD), backingPool, b);
sdep(backingPool).liquifyForAllBacking(user, b);
}
}
// ********************** End Backing Section
// LGE owner can save lost tokens, cannot remove FEG, LP or backing asset. If LGE has started and token == sd require LGE aborted.
function saveTokens(address token) public nonReentrant {
require(msg.sender == owner);
require(token != FEG() && token != uniswapV2Pair && token != uniswapV2PairFEG);
if(token == backingAsset || token == wETH()) {
require(raised == 0, "raised != 0");
}
uint256 a = IERC20(token).balanceOf(address(this));
if(token == SD) {
if(started) {
require(aborted, "rax");
}
a = a - totalTokensVested;
pre.supplyForLP = 0;
pre.hardCap = 0;
pre.softCap = 0;
}
if(a > 0) {
SafeTransfer._pushUnderlying(IERC20(token), owner, a);
}
if(address(this).balance > 0) {
SafeTransfer.safeTransferETH(owner, address(this).balance);
}
}
}