// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Library for converting between addresses and bytes32 values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Bytes32AddressLib.sol)
library Bytes32AddressLib {
function fromLast20Bytes(
bytes32 bytesValue
) internal pure returns (address) {
return address(uint160(uint256(bytesValue)));
}
function fillLast12Bytes(
address addressValue
) internal pure returns (bytes32) {
return bytes32(bytes20(addressValue));
}
}
// 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.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// 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
pragma solidity ^0.8.26;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {TickMath} from "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import {INonfungiblePositionManager, IUniswapV3Factory, ILockerFactory, ILocker, ExactInputSingleParams, ISwapRouter} from "./interface.sol";
import {Bytes32AddressLib} from "./Bytes32AddressLib.sol";
contract Token is ERC20 {
constructor(
string memory name_,
string memory symbol_,
uint256 maxSupply_
) ERC20(name_, symbol_) {
_mint(msg.sender, maxSupply_);
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
}
contract SocialDexDeployer is Ownable {
using TickMath for int24;
using Bytes32AddressLib for bytes32;
address public taxCollector;
uint64 public defaultLockingPeriod = 33275115461;
uint8 public taxRate = 25; // 25 / 1000 -> 2.5 %
uint8 public lpFeesCut = 50; // 5 / 100 -> 5%
uint8 public protocolCut = 30; // 3 / 100 -> 3%
ILockerFactory public liquidityLocker;
address public weth;
IUniswapV3Factory public uniswapV3Factory;
INonfungiblePositionManager public positionManager;
address public swapRouter;
event TokenCreated(
address tokenAddress,
uint256 lpNftId,
address deployer,
string name,
string symbol,
uint256 supply,
uint256 _supply,
address lockerAddress
);
constructor(
address taxCollector_,
address weth_,
address locker_,
address uniswapV3Factory_,
address positionManager_,
uint64 defaultLockingPeriod_,
address swapRouter_
) Ownable(msg.sender) {
taxCollector = taxCollector_;
weth = weth_;
liquidityLocker = ILockerFactory(locker_);
uniswapV3Factory = IUniswapV3Factory(uniswapV3Factory_);
positionManager = INonfungiblePositionManager(positionManager_);
defaultLockingPeriod = defaultLockingPeriod_;
swapRouter = swapRouter_;
}
function deployToken(
string calldata _name,
string calldata _symbol,
uint256 _supply,
int24 _initialTick,
uint24 _fee,
bytes32 _salt,
address _deployer
) external payable returns (Token token, uint256 tokenId) {
int24 tickSpacing = uniswapV3Factory.feeAmountTickSpacing(_fee);
require(
tickSpacing != 0 && _initialTick % tickSpacing == 0,
"Invalid tick"
);
token = new Token{salt: keccak256(abi.encode(msg.sender, _salt))}(
_name,
_symbol,
_supply
);
require(address(token) < weth, "Invalid salt");
require(_supply >= _supply, "Invalid supply amount");
uint160 sqrtPriceX96 = _initialTick.getSqrtRatioAtTick();
address pool = uniswapV3Factory.createPool(address(token), weth, _fee);
IUniswapV3Factory(pool).initialize(sqrtPriceX96);
INonfungiblePositionManager.MintParams
memory params = INonfungiblePositionManager.MintParams(
address(token),
weth,
_fee,
_initialTick,
maxUsableTick(tickSpacing),
_supply,
0,
0,
0,
address(this),
block.timestamp
);
token.approve(address(positionManager), _supply);
(tokenId, , , ) = positionManager.mint(params);
address lockerAddress = liquidityLocker.deploy(
address(positionManager),
_deployer,
defaultLockingPeriod,
tokenId,
lpFeesCut
);
positionManager.safeTransferFrom(address(this), lockerAddress, tokenId);
ILocker(lockerAddress).initializer(tokenId);
uint256 protocolFees = (msg.value * protocolCut) / 1000;
uint256 remainingFundsToBuyTokens = msg.value - protocolFees;
// send to 0x04F6ef12a8B6c2346C8505eE4Cff71C43D2dd825
if (msg.value > 0) {
ExactInputSingleParams memory swapParams = ExactInputSingleParams({
tokenIn: weth, // The token we are exchanging from (ETH wrapped as WETH)
tokenOut: address(token), // The token we are exchanging to
fee: _fee, // The pool fee
recipient: msg.sender, // The recipient address
amountIn: remainingFundsToBuyTokens, // The amount of ETH (WETH) to be swapped
amountOutMinimum: 0, // Minimum amount of DAI to receive
sqrtPriceLimitX96: 0 // No price limit
});
// The call to `exactInputSingle` executes the swap.
ISwapRouter(swapRouter).exactInputSingle{
value: remainingFundsToBuyTokens
}(swapParams);
}
(bool success, ) = payable(taxCollector).call{value: protocolFees}("");
if (!success) {
revert("Failed to send protocol fees");
}
emit TokenCreated(
address(token),
tokenId,
msg.sender,
_name,
_symbol,
_supply,
_supply,
lockerAddress
);
}
function initialSwapTokens(address token, uint24 _fee) public payable {
ExactInputSingleParams memory swapParams = ExactInputSingleParams({
tokenIn: weth, // The token we are exchanging from (ETH wrapped as WETH)
tokenOut: address(token), // The token we are exchanging to
fee: _fee, // The pool fee
recipient: msg.sender, // The recipient address
amountIn: msg.value, // The amount of ETH (WETH) to be swapped
amountOutMinimum: 0, // Minimum amount of DAI to receive
sqrtPriceLimitX96: 0 // No price limit
});
// The call to `exactInputSingle` executes the swap.
ISwapRouter(swapRouter).exactInputSingle{value: msg.value}(swapParams);
}
function predictToken(
address deployer,
string calldata name,
string calldata symbol,
uint256 supply,
bytes32 salt
) public view returns (address) {
bytes32 create2Salt = keccak256(abi.encode(deployer, salt));
return
keccak256(
abi.encodePacked(
bytes1(0xFF),
address(this),
create2Salt,
keccak256(
abi.encodePacked(
type(Token).creationCode,
abi.encode(name, symbol, supply)
)
)
)
).fromLast20Bytes();
}
function generateSalt(
address deployer,
string calldata name,
string calldata symbol,
uint256 supply
) external view returns (bytes32 salt, address token) {
for (uint256 i; ; i++) {
salt = bytes32(i);
token = predictToken(deployer, name, symbol, supply, salt);
if (token < weth && token.code.length == 0) {
break;
}
}
}
function updateTaxCollector(address newCollector) external onlyOwner {
taxCollector = newCollector;
}
function updateLiquidityLocker(address newLocker) external onlyOwner {
liquidityLocker = ILockerFactory(newLocker);
}
function updateDefaultLockingPeriod(uint64 newPeriod) external onlyOwner {
defaultLockingPeriod = newPeriod;
}
function updateProtocolFees(uint8 newFee) external onlyOwner {
lpFeesCut = newFee;
}
function updateTaxRate(uint8 newRate) external onlyOwner {
taxRate = newRate;
}
}
/// @notice Given a tickSpacing, compute the maximum usable tick
function maxUsableTick(int24 tickSpacing) pure returns (int24) {
unchecked {
return (TickMath.MAX_TICK / tickSpacing) * tickSpacing;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
interface INonfungiblePositionManager {
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
function mint(
MintParams calldata params
)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) external payable returns (address pool);
function collect(
CollectParams calldata params
) external payable returns (uint256 amount0, uint256 amount1);
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
}
interface IUniswapV3Factory {
function initialize(uint160 sqrtPriceX96) external;
function createPool(
address tokenA,
address tokenB,
uint24 fee
) external returns (address pool);
function feeAmountTickSpacing(uint24 fee) external view returns (int24);
}
interface ILockerFactory {
function deploy(
address token,
address beneficiary,
uint64 durationSeconds,
uint256 tokenId,
uint256 fees
) external payable returns (address);
}
interface ILocker {
function initializer(uint256 tokenId) external;
}
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
interface ISwapRouter {
function exactInputSingle(
ExactInputSingleParams calldata params
) external payable returns (uint256 amountOut);
}
{
"compilationTarget": {
"src/SocialDexDeployer.sol": "Token"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@uniswap/v3-core/=lib/v3-core/",
":@uniswap/v3-periphery/=lib/v3-periphery/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
":v3-core/=lib/v3-core/contracts/"
],
"viaIR": true
}
[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint256","name":"maxSupply_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]