Contract Name:
UniswapV2Pair
Contract Source Code:
/*
█▀ █▀█ █▄░█ █ █▀▀ █▀▀ ▄▀█ █▀▀ ▀█▀ █▀█ █▀█ █▄█
▄█ █▄█ █░▀█ █ █▄▄ █▀░ █▀█ █▄▄ ░█░ █▄█ █▀▄ ░█░
Trade on SonicFactory and have fun!
Web: https://sonicfactory.fun/
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "./libraries/UQ112x112.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/IWETH.sol";
import "./interfaces/IIncubator.sol";
contract UniswapV2Pair {
using UQ112x112 for uint224;
address private immutable INCUBATOR;
address private _CREATOR;
address private _REFERRAL;
address private _WETH;
address private _TOKEN;
address public immutable token0;
address public immutable token1;
bool private _initialized;
bool private _AUTOESCAPE;
uint8 private locked;
uint24 private _FEE;
uint24 private _TAX;
uint24 private _REF;
uint32 private _LAUNCH;
uint32 private blockTimestampLast;
uint112 private _ETH_RESERVE_VIRTUAL_INITIAL;
uint112 private _ETH_RESERVE_ESCAPE_TARGET;
uint112 private _TOKEN_INITIAL_RESERVE;
uint112 private reserve0;
uint112 private reserve1;
uint256 public price0CumulativeLast;
uint256 public price1CumulativeLast;
event Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to);
event Sync(uint112 reserve0, uint112 reserve1);
error ErrorLocked();
error ErrorAutoEscape();
error ErrorNotLaunched();
error ErrorUnauthorized(address sender);
error ErrorAlreadyInitialized();
error ErrorInsufficientReserves();
error ErrorSwapInsufficientInput();
error ErrorSwapInsufficientOutput();
error ErrorSwapInsufficientLiquidity();
error ErrorSwapInvalidRecipient();
error ErrorTransfer(address to, uint256 amount);
modifier lock() {
if (locked >= 1) { revert ErrorLocked(); }
locked = 1;
_;
if (locked == 1) { locked = 0; }
}
modifier isLaunched() {
if (_LAUNCH > _timestamp()) { revert ErrorNotLaunched(); }
_;
}
constructor(address incubator, address _token0, address _token1) payable {
INCUBATOR = incubator;
token0 = _token0;
token1 = _token1;
}
function initializePair(address creator, uint24 fee, uint24 tax, uint24 ref, uint32 launch, uint112 tokenInitialReserve, uint112 ethInitialVirtualReserve, uint112 ethReserveEscapeTarget, address WETH, address referral, bool autoEscape) external {
if (msg.sender != INCUBATOR) { revert ErrorUnauthorized(msg.sender); }
if (_initialized) { revert ErrorAlreadyInitialized(); }
_CREATOR = creator;
_FEE = fee;
_TAX = tax;
_LAUNCH = launch;
_ETH_RESERVE_VIRTUAL_INITIAL = ethInitialVirtualReserve;
_ETH_RESERVE_ESCAPE_TARGET = ethReserveEscapeTarget;
_TOKEN_INITIAL_RESERVE = tokenInitialReserve;
_WETH = WETH;
_TOKEN = token0 == _WETH ? token1 : token0;
_AUTOESCAPE = autoEscape;
if (referral != address(0) && referral != creator) {
_REF = ref;
_REFERRAL = referral;
}
_initialized = true;
_update(token0 == _WETH ? uint256(ethInitialVirtualReserve) : uint256(_TOKEN_INITIAL_RESERVE), token0 == _WETH ? uint256(_TOKEN_INITIAL_RESERVE) : uint256(ethInitialVirtualReserve), 0, 0);
}
function FEE() external view returns (uint24) {
return _FEE;
}
function TAX() external view returns (uint24) {
return _TAX;
}
function ETH_INITIAL_VIRTUAL_RESERVE() external view returns (uint112) {
return _ETH_RESERVE_VIRTUAL_INITIAL;
}
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
return (reserve0, reserve1, blockTimestampLast);
}
function sync() external lock {
uint256 balance0;
uint256 balance1;
unchecked {
balance0 = IERC20(token0).balanceOf(address(this)) + (token0 == _WETH ? uint256(_ETH_RESERVE_VIRTUAL_INITIAL) : 0);
balance1 = IERC20(token1).balanceOf(address(this)) + (token1 == _WETH ? uint256(_ETH_RESERVE_VIRTUAL_INITIAL) : 0);
}
_update(balance0, balance1, reserve0, reserve1);
}
function swap(address to, address tokenIn) external payable isLaunched lock {
if (to == token0 || to == token1) { revert ErrorSwapInvalidRecipient(); }
bool buy = tokenIn == _WETH;
uint256 amountInEffective;
uint256 amountOutEffective;
uint256 amountIn;
uint256 amountOut;
uint256 ethAmount;
uint256 feeAmount;
uint256 taxAmount;
uint256 refAmount;
if (buy) {
amountIn = msg.value;
amountInEffective = _percentage(amountIn, 100_000 - uint256(_FEE + _TAX));
ethAmount = amountIn;
if (_FEE > 0 || _TAX > 0) {
if (_FEE > 0) {
unchecked {
feeAmount = _percentage(amountIn, uint256(_FEE));
if (_REF > 0) {
refAmount = _percentage(feeAmount, uint256(_REF));
feeAmount -= refAmount;
}
}
}
if (_TAX > 0) { taxAmount = _percentage(amountIn, uint256(_TAX)); }
}
IWETH(_WETH).deposit{ value: amountInEffective }();
IWETH(_WETH).transfer(address(this), amountInEffective);
} else {
unchecked {
amountIn = amountInEffective = IERC20(tokenIn).balanceOf(address(this)) - uint256(token0 == tokenIn ? reserve0 : reserve1);
}
}
if (amountIn == 0) { revert ErrorSwapInsufficientInput(); }
(uint112 reserveIn, uint112 reserveOut) = token0 == tokenIn ? (reserve0, reserve1) : (reserve1, reserve0);
unchecked {
amountOut = amountOutEffective = (amountInEffective * uint256(reserveOut)) / (uint256(reserveIn) + amountInEffective);
}
if (amountOut == 0) { revert ErrorSwapInsufficientOutput(); }
if (amountOut > uint256(reserveOut)) { revert ErrorSwapInsufficientLiquidity(); }
if (tokenIn == _TOKEN) {
unchecked {
amountOutEffective = _percentage(amountOut, 100_000 - uint256(_FEE + _TAX));
}
IWETH(_WETH).withdraw(amountOut);
ethAmount = amountOut;
if (_FEE > 0 || _TAX > 0) {
if (_FEE > 0) {
unchecked {
feeAmount = _percentage(amountOut, uint256(_FEE));
if (_REF > 0) {
refAmount = _percentage(feeAmount, uint256(_REF));
feeAmount -= refAmount;
}
}
}
if (_TAX > 0) { taxAmount = _percentage(amountOut, uint256(_TAX)); }
}
}
if (_FEE > 0 || _TAX > 0) {
if (feeAmount > 0) {
_withdrawETH(INCUBATOR, feeAmount);
if (refAmount > 0) { _withdrawETH(_REFERRAL, refAmount); }
}
if (taxAmount > 0) { _withdrawETH(_CREATOR, taxAmount); }
}
uint256 tokenAmount = buy ? amountOut : amountIn;
{ // scope to avoid "stack too deep" error
address _to = to;
bool _buy = buy;
uint256 _amountOutEffective = amountOutEffective;
uint256 _ethAmount = ethAmount;
uint256 _tokenAmount = tokenAmount;
uint256 _feeAmount = feeAmount;
uint256 _taxAmount = taxAmount;
uint256 _refAmount = refAmount;
if (_buy) {
IERC20(_TOKEN).transfer(_to, _amountOutEffective);
} else{
_withdrawETH(_to, _amountOutEffective);
}
(uint112 ethReserve, uint112 tokenReserve) = _getReserves();
IIncubator(INCUBATOR).pairSwap(_to, _buy, ethReserve, tokenReserve, _ethAmount, _tokenAmount, _feeAmount, _taxAmount, _refAmount);
}
uint256 balance0;
uint256 balance1;
unchecked {
balance0 = IERC20(token0).balanceOf(address(this)) + (token0 == _WETH ? uint256(_ETH_RESERVE_VIRTUAL_INITIAL) : 0);
balance1 = IERC20(token1).balanceOf(address(this)) + (token1 == _WETH ? uint256(_ETH_RESERVE_VIRTUAL_INITIAL) : 0);
}
_update(balance0, balance1, reserve0, reserve1);
if (tokenIn == token0) {
emit Swap(msg.sender, amountIn, 0, 0, amountOut, to);
} else {
emit Swap(msg.sender, 0, amountIn, amountOut, 0, to);
}
if (buy && _AUTOESCAPE) {
unchecked {
if ((token0 == _WETH ? balance0 : balance1) - uint256(_ETH_RESERVE_VIRTUAL_INITIAL) >= uint256(_ETH_RESERVE_ESCAPE_TARGET)) { _escape(); }
}
}
}
function escape() external isLaunched lock {
if (msg.sender != _CREATOR) { revert ErrorUnauthorized(msg.sender); }
if (_AUTOESCAPE) { revert ErrorAutoEscape(); }
unchecked {
if ((token0 == _WETH ? reserve0 : reserve1) - uint256(_ETH_RESERVE_VIRTUAL_INITIAL) < uint256(_ETH_RESERVE_ESCAPE_TARGET)) { revert ErrorInsufficientReserves(); }
}
_escape();
}
function _getReserves() private view returns (uint112 ethReserve, uint112 tokenReserve) {
ethReserve = token0 == _WETH ? reserve0 : reserve1;
tokenReserve = token0 == _WETH ? reserve1 : reserve0;
}
function _update(uint256 balance0, uint256 balance1, uint112 _reserve0, uint112 _reserve1) private {
uint32 blockTimestamp = _timestamp();
unchecked {
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
price0CumulativeLast += uint256(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
price1CumulativeLast += uint256(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
}
}
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
blockTimestampLast = _timestamp();
emit Sync(reserve0, reserve1);
}
function _withdrawETH(address to, uint256 value) private {
(bool success,) = payable(to).call{ value: value }("");
if (!success) { revert ErrorTransfer(to, value); }
}
function _escape() private {
locked = 2;
delete _FEE;
delete _TAX;
delete _REF;
uint256 balance0 = IERC20(token0).balanceOf(address(this));
uint256 balance1 = IERC20(token1).balanceOf(address(this));
(uint256 tokenAmount, uint256 ethAmount) = token0 == _WETH ? (balance1, balance0) : (balance0, balance1);
IERC20(_TOKEN).transfer(INCUBATOR, tokenAmount);
IWETH(_WETH).withdraw(ethAmount);
IIncubator(INCUBATOR).tokenEscape{ value: ethAmount }(tokenAmount);
_update(0, 0, reserve0, reserve1);
}
function _percentage(uint256 value, uint256 bps) private pure returns (uint256) {
unchecked {
return (value * bps) / 100_000;
}
}
function _timestamp() private view returns (uint32) {
unchecked {
return uint32(block.timestamp % 2**32);
}
}
receive() external payable {
require(msg.sender == _WETH);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IIncubator {
function pairSwap(address user, bool buy, uint112 ethReserve, uint112 tokenReserve, uint256 ethAmount, uint256 tokenAmount, uint256 feeAmount, uint256 taxAmount, uint256 refAmount) external;
function tokenEscape(uint256 tokenAmount) external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IWETH {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IERC20 {
function balanceOf(address owner) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
library UQ112x112 {
uint224 constant Q112 = 2**112;
function encode(uint112 y) internal pure returns (uint224 z) {
unchecked {
z = uint224(y) * Q112;
}
}
function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
unchecked {
z = x / uint224(y);
}
}
}