Skip to content

Commit

Permalink
ADD: aime power fair launch v2
Browse files Browse the repository at this point in the history
  • Loading branch information
cctv2206 committed Apr 11, 2024
1 parent 16a7ca6 commit 3223b67
Show file tree
Hide file tree
Showing 8 changed files with 402 additions and 66 deletions.
236 changes: 236 additions & 0 deletions contracts/aime/AIMePowerFairLaunchV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol";
import "@uniswap/v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@uniswap/v3-periphery/contracts/interfaces/external/IWETH9.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../utils/ReentrancyGuard.sol";
import "./MathUtil.sol";

contract AIMePowerFairLaunchV2 is ERC20, ReentrancyGuard, IERC721Receiver {
uint256 public price;
uint256 public amountPerUnits;
uint256 public reservedAmount;
uint256 public deadline;
uint24 public poolFee;
address public pool;

uint256 public mintLimit;
uint256 public minted;

bool public started;
address public launcher;

address public immutable WETH;
ISwapRouter public immutable swapRouter;
INonfungiblePositionManager public immutable nonfungiblePositionManager;

uint256 public positionTokenId;

event AIMePowerFairMinted(address indexed to, uint256 amount, uint256 ethAmount);
event AIMePowerFairRefund(address indexed to, uint256 amount, uint256 ethAmount);

constructor(
uint256 _price,
uint256 _amountPerUnits,
uint256 _totalSupply,
uint256 _reservedAmount,
address _launcher,
uint256 _deadline,
uint24 _poolFee,
INonfungiblePositionManager _nonfungiblePositionManager,
ISwapRouter _swapRouter,
string memory _name,
string memory _symbol
) ERC20(_name, _symbol) {
price = _price;
amountPerUnits = _amountPerUnits;
deadline = _deadline;
launcher = _launcher;
reservedAmount = _reservedAmount;
poolFee = _poolFee;
started = false;

_mint(address(this), _totalSupply);

// 50% for mint
mintLimit = (_totalSupply - reservedAmount) / 2;

// uniswap
nonfungiblePositionManager = _nonfungiblePositionManager;
swapRouter = _swapRouter;
WETH = IPeripheryImmutableState(address(_swapRouter)).WETH9();
}

/// @notice Collects the fees associated with provided liquidity
function collectFees() external {
// set amount0Max and amount1Max to uint256.max to collect all fees
INonfungiblePositionManager.CollectParams memory params =
INonfungiblePositionManager.CollectParams({
tokenId: positionTokenId,
recipient: address(this),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
});

(uint256 amount0, uint256 amount1) = nonfungiblePositionManager.collect(params);

// todo: send collected fee to receiver or just buy token
}

receive() external payable {
// todo: require value != 0?
if (msg.sender != launcher) {
if (block.timestamp > deadline || minted == mintLimit) {
start();
} else {
mint();
}
} else {
start();
}
}

function onERC721Received(
address,
address,
uint256,
bytes calldata
) external override returns (bytes4) {
return this.onERC721Received.selector;
}

function mint() internal virtual nonReentrant {
require(!started, "AIMEPowerFairLaunch: already started");
require(msg.value >= price, "AIMEPowerFairLaunch: value not match");
require(!_isContract(msg.sender), "AIMEPowerFairLaunch: can not mint to contract");
require(msg.sender == tx.origin, "AIMEPowerFairLaunch: can not mint to contract.");

uint256 units = msg.value / price;
uint256 amountDesired = units * amountPerUnits;
uint256 amount = amountDesired;
if (minted + amountDesired > mintLimit) {
amount = mintLimit - minted;
units = amount / amountPerUnits + 1;
}

uint256 realCost = units * price;
uint256 refund = msg.value - realCost;

minted += amount;
_transfer(address(this), msg.sender, amount);

emit AIMePowerFairMinted(msg.sender, amount, realCost);

if (refund > 0) {
(bool success, ) = payable(msg.sender).call{value: refund}("");
require(success, "Failed to refund");
}
}

function start() internal {
require(!started, "AIMEPowerFairLaunch: already started");
started = true;

// eth -> weth
IWETH9(WETH).deposit{value: address(this).balance}();

if (address(this) < WETH) {
_createPool(address(this), WETH, minted, IWETH9(WETH).balanceOf(address(this)));
} else {
_createPool(WETH, address(this), IWETH9(WETH).balanceOf(address(this)), minted);
}
// todo: launch event
}

function _createPool(address token0, address token1, uint256 token0Amount, uint256 token1Amount) internal {
uint160 sqrtPriceX96 = uint160((2 ** 96) * MathUtil.SquareRoot(token0Amount) / MathUtil.SquareRoot(token1Amount));

// init pool
pool = nonfungiblePositionManager
.createAndInitializePoolIfNecessary(
token0,
token1,
poolFee,
sqrtPriceX96
);

// Approve the position manager
TransferHelper.safeApprove(
token0,
address(nonfungiblePositionManager),
token0Amount
);
TransferHelper.safeApprove(
token1,
address(nonfungiblePositionManager),
token1Amount
);

int24 tickSpacing = IUniswapV3Pool(pool).tickSpacing();
(, int24 tick, , , , , ) = IUniswapV3Pool(pool).slot0();

// todo: calculate tick lower and upper
INonfungiblePositionManager.MintParams
memory params = INonfungiblePositionManager.MintParams({
token0: token0,
token1: token1,
fee: poolFee,
tickLower: tick - 2 * tickSpacing,
tickUpper: tick - 2 * tickSpacing,
amount0Desired: token0Amount,
amount1Desired: token1Amount,
amount0Min: token0Amount,
amount1Min: token1Amount,
recipient: address(this),
deadline: block.timestamp
});

(uint256 tokenId, uint256 liquidity, uint256 amount0, uint256 amount1) = nonfungiblePositionManager
.mint(params);

positionTokenId = tokenId;

// todo: refund tokens
}

function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal override {
if (!started && to == address(this) && from != address(0)) {
_refund(from, amount);
}
}

function _refund(address from, uint256 value) internal nonReentrant {
require(!started, "AIMEPowerFairLaunch: already started");
require(!_isContract(from), "AIMEPowerFairLaunch: can not refund to contract");
require(from == tx.origin, "AIMEPowerFairLaunch: can not refund to contract.");
require(value >= amountPerUnits, "AIMEPowerFairLaunch: value not match");
require(value % amountPerUnits == 0, "AIMEPowerFairLaunch: value not match");

uint256 ethAmount = (value / amountPerUnits) * price;
require(ethAmount > 0, "AIMEPowerFairLaunch: no refund");

minted -= value;
(bool success, ) = payable(from).call{value: ethAmount}("");
require(success, "Failed to refund");

emit AIMePowerFairRefund(from, value, ethAmount);
}

function _isContract(address _addr) internal view returns (bool) {
uint32 size;
assembly {
size := extcodesize(_addr)
}
return (size > 0);
}
}
17 changes: 17 additions & 0 deletions contracts/aime/MathUtil.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library MathUtil {
function SquareRoot(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
Loading

0 comments on commit 3223b67

Please sign in to comment.