Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/Clones.sol)
pragma solidity ^0.8.20;
import {Create2} from "../utils/Create2.sol";
import {Errors} from "../utils/Errors.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*/
library Clones {
error CloneArgumentsTooLong();
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
return clone(implementation, 0);
}
/**
* @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
* to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function clone(address implementation, uint256 value) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
assembly ("memory-safe") {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(value, 0x09, 0x37)
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple times will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
return cloneDeterministic(implementation, salt, 0);
}
/**
* @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
* a `value` parameter to send native currency to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneDeterministic(
address implementation,
bytes32 salt,
uint256 value
) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
assembly ("memory-safe") {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(value, 0x09, 0x37, salt)
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
/**
* @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
* immutable arguments. These are provided through `args` and cannot be changed after deployment. To
* access the arguments within the implementation, use {fetchCloneArgs}.
*
* This function uses the create opcode, which should never revert.
*/
function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) {
return cloneWithImmutableArgs(implementation, args, 0);
}
/**
* @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
* parameter to send native currency to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneWithImmutableArgs(
address implementation,
bytes memory args,
uint256 value
) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
assembly ("memory-safe") {
instance := create(value, add(bytecode, 0x20), mload(bytecode))
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation` with custom
* immutable arguments. These are provided through `args` and cannot be changed after deployment. To
* access the arguments within the implementation, use {fetchCloneArgs}.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
* `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice
* at the same address.
*/
function cloneDeterministicWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0);
}
/**
* @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
* but with a `value` parameter to send native currency to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneDeterministicWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt,
uint256 value
) internal returns (address instance) {
bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
return Create2.deploy(value, salt, bytecode);
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
*/
function predictDeterministicAddressWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
return Create2.computeAddress(salt, keccak256(bytecode), deployer);
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
*/
function predictDeterministicAddressWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this));
}
/**
* @dev Get the immutable args attached to a clone.
*
* - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
* function will return an empty array.
* - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
* `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
* creation.
* - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
* function should only be used to check addresses that are known to be clones.
*/
function fetchCloneArgs(address instance) internal view returns (bytes memory) {
bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
assembly ("memory-safe") {
extcodecopy(instance, add(result, 32), 45, mload(result))
}
return result;
}
/**
* @dev Helper that prepares the initcode of the proxy with immutable args.
*
* An assembly variant of this function requires copying the `args` array, which can be efficiently done using
* `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
* abi.encodePacked is more expensive but also more portable and easier to review.
*
* NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
* With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
*/
function _cloneCodeWithImmutableArgs(
address implementation,
bytes memory args
) private pure returns (bytes memory) {
if (args.length > 24531) revert CloneArgumentsTooLong();
return
abi.encodePacked(
hex"61",
uint16(args.length + 45),
hex"3d81600a3d39f3363d3d373d3d3d363d73",
implementation,
hex"5af43d82803e903d91602b57fd5bf3",
args
);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev There's no code to deploy.
*/
error Create2EmptyBytecode();
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
if (bytecode.length == 0) {
revert Create2EmptyBytecode();
}
assembly ("memory-safe") {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
// if no address was created, and returndata is not empty, bubble revert
if and(iszero(addr), not(iszero(returndatasize()))) {
let p := mload(0x40)
returndatacopy(p, 0, returndatasize())
revert(p, returndatasize())
}
}
if (addr == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
assembly ("memory-safe") {
let ptr := mload(0x40) // Get free memory pointer
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
// |-------------------|---------------------------------------------------------------------------|
// | bytecodeHash | CCCCCCCCCCCCC...CC |
// | salt | BBBBBBBBBBBBB...BB |
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
// | 0xFF | FF |
// |-------------------|---------------------------------------------------------------------------|
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
mstore8(start, 0xff)
addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff)
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// @title Sonicx.fun - Decentralized Launchpad
// @notice Powered by SonicXSwap.com, enabling fast and secure transactions
// @author SuryaprakashMalgo
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; // Protects against reentrancy attacks
import "@openzeppelin/contracts/access/Ownable.sol"; // Provides ownership functionality
import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(
address recipient,
uint256 amount
) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(
address owner,
address spender
) external returns (uint256);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
function decimals() external returns (uint8);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
interface SonicxswapRouter {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
function addLiquidityETH(
address token,
uint256 amountTokenDesired,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
)
external
payable
returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);
function swapExactETHForTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
function getAmountsOut(
uint256 amountIn,
address[] calldata path
) external view returns (uint256[] memory amounts);
function getAmountsIn(
uint256 amountOut,
address[] calldata path
) external view returns (uint256[] memory amounts);
}
interface SonicxswapFactory {
event PairCreated(
address indexed token0,
address indexed token1,
address pair,
uint256
);
function getPair(
address tokenA,
address tokenB
) external view returns (address pair);
function createPair(
address tokenA,
address tokenB
) external returns (address pair);
}
interface LPToken {
function sync() external;
}
interface IFunDeployerInterface {
function getOwnerPer() external view returns (uint256);
function emitRoyal(
address funContract,
address tokenAddress,
address router,
address baseAddress,
uint256 liquidityAmount,
uint256 tokenAmount,
uint256 _time,
uint256 totalVolume
) external;
}
interface IFunToken {
function initialize(
uint256 initialSupply,
string memory _name,
string memory _symbol,
address _midDeployer,
address _deployer
) external;
function initiateDex() external;
}
contract SonicxPool is Ownable, ReentrancyGuard {
address public constant DEAD = 0x000000000000000000000000000000000000dEaD;
uint256 public constant HUNDRED = 100;
uint256 public constant BASIS_POINTS = 10000;
AggregatorV3Interface internal dataFeed;
struct FunTokenPoolData {
uint256 reserveTokens;
uint256 reserveETH;
uint256 volume;
uint256 listThreshold;
uint256 initialReserveEth;
uint8 nativePer;
bool tradeActive;
bool lpBurn;
bool royalemitted;
}
struct FunTokenPool {
address creator;
address token;
address baseToken;
address router;
address lockerAddress;
address storedLPAddress;
address deployer;
FunTokenPoolData pool;
}
// deployer allowed to create fun tokens
mapping(address => bool) public allowedDeployers;
// user => array of fun tokens
mapping(address => address[]) public userFunTokens;
// fun token => fun token details
mapping(address => FunTokenPool) public tokenPools;
address public implementation;
uint16 public feePer;
uint16 public lpfee = 3;
event LiquidityAdded(
address indexed provider,
uint256 tokenAmount,
uint256 ethAmount
);
event sold(
address indexed user,
uint256 amountIn,
uint256 amountOut,
uint256 _time,
uint256 reserveEth,
uint256 reserveTokens,
uint256 totalVolume
);
event bought(
address indexed user,
uint256 amountIn,
uint256 amountOut,
uint256 _time,
uint256 reserveEth,
uint256 reserveTokens,
uint256 totalVolume
);
event funTradeCall(
address indexed user,
uint256 amountIn,
uint256 amountOut,
uint256 _time,
uint256 reserveEth,
uint256 reserveTokens,
string tradeType,
uint256 totalVolume
);
event listed(
address indexed user,
address indexed tokenAddress,
address indexed router,
uint256 liquidityAmount,
uint256 tokenAmount,
uint256 _time,
uint256 totalVolume
);
constructor(
address _implementation,
uint16 _feePer
) payable Ownable(msg.sender) {
implementation = _implementation;
feePer = _feePer;
dataFeed = AggregatorV3Interface(
0xc76dFb89fF298145b417d221B2c747d84952e01d
);
}
function getChainlinkDataFeedLatestAnswer() public view returns (int256) {
(
,
/* uint80 roundID */ int answer /*uint startedAt*/ /*uint timeStamp*/ /*uint80 answeredInRound*/,
,
,
) = dataFeed.latestRoundData();
return answer;
}
function createFun(
string[2] memory _name_symbol,
uint256 _totalSupply,
address _creator,
address _baseToken,
address _router,
uint256[2] memory listThreshold_initReserveEth,
bool lpBurn
) public payable returns (address funtoken) {
require(allowedDeployers[msg.sender], "not deployer");
address funToken = Clones.clone(implementation);
IFunToken(funToken).initialize(
_totalSupply,
_name_symbol[0],
_name_symbol[1],
address(this),
msg.sender
);
userFunTokens[_creator].push(funToken);
FunTokenPool memory pool;
pool.creator = _creator;
pool.token = funToken;
pool.baseToken = _baseToken;
pool.router = _router;
pool.deployer = msg.sender;
if (_baseToken == SonicxswapRouter(_router).WETH()) {
pool.pool.nativePer = 100;
} else {
pool.pool.nativePer = 50;
}
pool.pool.tradeActive = true;
pool.pool.lpBurn = lpBurn;
pool.pool.reserveTokens += _totalSupply;
pool.pool.reserveETH += (listThreshold_initReserveEth[1] + msg.value);
pool.pool.listThreshold = listThreshold_initReserveEth[0];
pool.pool.initialReserveEth = listThreshold_initReserveEth[1];
tokenPools[funToken] = pool;
emit LiquidityAdded(address(this), _totalSupply, msg.value);
return address(funToken);
}
function getAmountOutTokens(
address funToken,
uint256 amountIn
) public view returns (uint256 amountOut) {
require(amountIn > 0, "Invalid input amount");
FunTokenPool storage token = tokenPools[funToken];
require(
token.pool.reserveTokens > 0 && token.pool.reserveETH > 0,
"Invalid reserves"
);
uint256 numerator = amountIn * token.pool.reserveTokens;
uint256 denominator = (token.pool.reserveETH) + amountIn;
amountOut = numerator / denominator;
}
function getAmountOutETH(
address funToken,
uint256 amountIn
) public view returns (uint256 amountOut) {
require(amountIn > 0, "Invalid input amount");
FunTokenPool storage token = tokenPools[funToken];
require(
token.pool.reserveTokens > 0 && token.pool.reserveETH > 0,
"Invalid reserves"
);
uint256 numerator = amountIn * token.pool.reserveETH;
uint256 denominator = (token.pool.reserveTokens) + amountIn;
amountOut = numerator / denominator;
}
function getBaseToken(address funToken) public view returns (address) {
FunTokenPool storage token = tokenPools[funToken];
return address(token.baseToken);
}
function getWrapAddr(address funToken) public view returns (address) {
return SonicxswapRouter(tokenPools[funToken].router).WETH();
}
function getLatestPrice() public view returns (uint256) {
(, int256 price, , , ) = dataFeed.latestRoundData();
require(price >= 0, "Price cannot be negative");
return uint256(price * 10 ** 10);
}
function getCurrentCap(address funToken) public view returns (uint256) {
FunTokenPool storage token = tokenPools[funToken];
return
(((getLatestPrice() * token.pool.reserveETH) / 10 ** 18) *
IERC20(funToken).totalSupply()) / token.pool.reserveTokens;
}
function getFuntokenPool(
address funToken
) public view returns (FunTokenPool memory) {
return tokenPools[funToken];
}
function getFuntokenPools(
address[] memory funTokens
) public view returns (FunTokenPool[] memory) {
uint256 length = funTokens.length;
FunTokenPool[] memory pools = new FunTokenPool[](length);
for (uint256 i = 0; i < length; ) {
pools[i] = tokenPools[funTokens[i]];
unchecked {
i++;
}
}
return pools;
}
function getUserFuntokens(
address user
) public view returns (address[] memory) {
return userFunTokens[user];
}
function sellTokens(
address funToken,
uint256 tokenAmount,
uint256 minEth
) public nonReentrant returns (bool, bool) {
FunTokenPool storage token = tokenPools[funToken];
require(token.pool.tradeActive, "Trading not active");
uint256 tokenToSell = tokenAmount;
uint256 ethAmount = getAmountOutETH(funToken, tokenToSell);
uint256 ethAmountFee = (ethAmount * feePer) / 10000;
require(ethAmount > 0 && ethAmount >= minEth, "Slippage too high");
token.pool.reserveTokens += tokenAmount;
token.pool.reserveETH -= ethAmount;
token.pool.volume += ethAmount;
IERC20(funToken).transferFrom(msg.sender, address(this), tokenToSell);
(bool success, ) = owner().call{value: ethAmountFee}("");
require(success, "ownr ETH transfer failed");
(success, ) = msg.sender.call{value: ethAmount - ethAmountFee}("");
require(success, "seller ETH transfer failed");
emit sold(
msg.sender,
tokenAmount,
ethAmount,
block.timestamp,
token.pool.reserveETH,
token.pool.reserveTokens,
token.pool.volume
);
emit funTradeCall(
msg.sender,
tokenAmount,
ethAmount,
block.timestamp,
token.pool.reserveETH,
token.pool.reserveTokens,
"sell",
token.pool.volume
);
return (true, true);
}
function buyTokens(
address funToken,
uint256 minTokens
) public payable nonReentrant {
require(msg.value > 0, "Invalid buy value");
FunTokenPool storage token = tokenPools[funToken];
require(token.pool.tradeActive, "Trading not active");
{
uint256 ethAmount = msg.value;
uint256 ethAmountFee = (ethAmount * feePer) / 10000;
uint256 tokenAmount = getAmountOutTokens(
funToken,
ethAmount - ethAmountFee
);
require(tokenAmount >= minTokens, "Slippage too high");
token.pool.reserveETH += (ethAmount - ethAmountFee);
token.pool.reserveTokens -= tokenAmount;
token.pool.volume += ethAmount;
(bool success, ) = owner().call{value: ethAmountFee}("");
require(success, "fee ETH transfer failed");
IERC20(funToken).transfer(msg.sender, tokenAmount);
emit bought(
msg.sender,
msg.value,
tokenAmount,
block.timestamp,
token.pool.reserveETH,
token.pool.reserveTokens,
token.pool.volume
);
emit funTradeCall(
msg.sender,
msg.value,
tokenAmount,
block.timestamp,
token.pool.reserveETH,
token.pool.reserveTokens,
"buy",
token.pool.volume
);
}
uint256 currentMarketCap = getCurrentCap(funToken);
uint256 listThresholdCap = token.pool.listThreshold * 10 ** 18;
if (
currentMarketCap >= (listThresholdCap / 2) &&
!token.pool.royalemitted
) {
IFunDeployerInterface(token.deployer).emitRoyal(
funToken,
funToken,
token.router,
token.baseToken,
token.pool.reserveETH,
token.pool.reserveTokens,
block.timestamp,
token.pool.volume
);
token.pool.royalemitted = true;
}
if (currentMarketCap >= listThresholdCap) {
token.pool.tradeActive = false;
IFunToken(funToken).initiateDex();
token.pool.reserveETH -= token.pool.initialReserveEth;
_addLiquidityETH(
funToken,
IERC20(funToken).balanceOf(address(this)),
token.pool.reserveETH
);
token.pool.reserveETH -= token.pool.reserveETH;
}
}
function changeNativePer(address funToken, uint8 _newNativePer) public {
require(_isUserFunToken(funToken), "Unauthorized");
FunTokenPool storage token = tokenPools[funToken];
require(
token.baseToken != getWrapAddr(funToken),
"no custom base selected"
);
require(_newNativePer >= 0 && _newNativePer <= 100, "invalid per");
token.pool.nativePer = _newNativePer;
}
function _addLiquidityETH(
address funToken,
uint256 amountTokenDesired,
uint256 nativeForDex // bool lpBurn
) internal {
uint256 amountETH = nativeForDex;
uint256 amountETHMin = (amountETH * 90) / HUNDRED;
uint256 amountTokenToAddLiq = amountTokenDesired;
uint256 amountTokenMin = (amountTokenToAddLiq * 90) / HUNDRED;
FunTokenPool storage token = tokenPools[funToken];
address wrapperAddress = getWrapAddr(funToken);
token.storedLPAddress = _getpair(funToken, funToken, wrapperAddress);
address storedLPAddress = token.storedLPAddress;
_approve(funToken, false);
SonicxswapRouter(token.router).addLiquidityETH{value: amountETH}(
funToken,
amountTokenToAddLiq,
amountTokenMin,
amountETHMin,
address(this),
block.timestamp + (300)
);
uint256 lpBalance = IERC20(storedLPAddress).balanceOf(address(this));
// Calculate LP fee distribution
uint256 ownerLPFee = (lpBalance * lpfee) / 100; // Owner's LP fee
uint256 deadLPAmount = lpBalance - ownerLPFee; // Remaining sent to DEAD address
//3% Transfer LP tokens
IERC20(storedLPAddress).transfer(owner(), ownerLPFee); // Send LP fee to owner
//97%
IERC20(storedLPAddress).transfer(DEAD, deadLPAmount); // Send the rest to DEAD address
emit listed(
msg.sender,
funToken,
token.router,
amountETH,
amountTokenToAddLiq,
block.timestamp,
token.pool.volume
);
}
function _approve(
address funToken,
bool isBaseToken
) internal returns (bool) {
FunTokenPool storage token = tokenPools[funToken];
IERC20 token_ = IERC20(funToken);
if (isBaseToken) {
token_ = IERC20(token.baseToken);
}
if (token_.allowance(address(this), token.router) == 0) {
token_.approve(token.router, type(uint256).max);
}
return true;
}
function _approveLock(
address _lp,
address _lockDeployer
) internal returns (bool) {
IERC20 lp_ = IERC20(_lp);
if (lp_.allowance(address(this), _lockDeployer) == 0) {
lp_.approve(_lockDeployer, type(uint256).max);
}
return true;
}
function _getpair(
address funToken,
address _token1,
address _token2
) internal returns (address) {
address router = tokenPools[funToken].router;
address factory = SonicxswapRouter(router).factory();
address pair = SonicxswapFactory(factory).getPair(_token1, _token2);
if (pair != address(0)) {
return pair;
} else {
return SonicxswapFactory(factory).createPair(_token1, _token2);
}
}
function _isUserFunToken(address funToken) internal view returns (bool) {
for (uint256 i = 0; i < userFunTokens[msg.sender].length; ) {
if (funToken == userFunTokens[msg.sender][i]) {
return true;
}
unchecked {
i++;
}
}
return false;
}
function addDeployer(address _deployer) public onlyOwner {
allowedDeployers[_deployer] = true;
}
function removeDeployer(address _deployer) public onlyOwner {
allowedDeployers[_deployer] = false;
}
function updateImplementation(address _implementation) public onlyOwner {
require(_implementation != address(0));
implementation = _implementation;
}
function updateteamFeeper(uint16 _newFeePer) public onlyOwner {
feePer = _newFeePer;
}
function updateLpfee(uint16 _lpfee) public onlyOwner {
lpfee = _lpfee;
}
}