RobinPadFactory
0x1461e52f235b614bd9cfb909383778521ad7d8bf
Verification
Verified
v0.8.28+commit.7893614a
Type
Contract
16,936 bytes
ABI entries
65
20 read · 9 write
License
none
Contract information
- Address
- 0x1461e52f235b614bd9cfb909383778521ad7d8bf
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.28+commit.7893614a
- Optimization
- Enabled
- Creator
- 0xEEdf57CAD0…9919f43D21
- Creation tx
- 0xdc4203589d…44cdf414eb
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (20)
FEE_TIER() → uint24
createTokenEnabled() → bool
deployedTokens(uint256) → address, string, string, address, address, address, uint256, string, uint256, uint256, uint256, uint256, uint256
feeRecipientForToken(address) → address
getDeploysPaginated(uint256, uint256) → tuple[]
getTokenMarketCap(address) → uint256
initialBuyAmount() → uint256
owner() → address
positionManager() → address
robinPadDeployer() → address
swapRouter() → address
tickSpacing() → int24
tokenCount() → uint256
tokenFeesGenerated(address) → uint256
tokenInfoByAddress(address) → address, string, string, address, address, address, uint256, string, uint256, uint256, uint256, uint256, uint256
tokenToNFTId(address) → uint256
uniV3ParameterSetCount() → uint256
uniV3Parameters(uint256) → uint160, int24, int24, uint256, uint256
uniswapV3Factory() → address
weth() → address
Events (8)
CreateTokenSwitchedERC20TokenCreatedInitialBuyAmountUpdatedOwnershipTransferredTokenFeeRecipientSetUniv3ParameterSetCreatedUniv3ParameterSetDeletedUniv3ParameterSetUpdated
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "uniswapV3Factory_",
"type": "address"
},
{
"internalType": "address",
"name": "positionManager_",
"type": "address"
},
{
"internalType": "address",
"name": "weth_",
"type": "address"
},
{
"internalType": "address",
"name": "swapRouter_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "CannotDeleteDefaultUniv3ParameterSet",
"type": "error"
},
{
"inputs": [],
"name": "DeployDisabled",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientInitialBuy",
"type": "error"
},
{
"inputs": [],
"name": "InvalidFeeRecipient",
"type": "error"
},
{
"inputs": [],
"name": "InvalidInitialBuyAmount",
"type": "error"
},
{
"inputs": [],
"name": "InvalidPositionManager",
"type": "error"
},
{
"inputs": [],
"name": "InvalidSqrtPrice",
"type": "error"
},
{
"inputs": [],
"name": "InvalidStoredTickLower",
"type": "error"
},
{
"inputs": [],
"name": "InvalidTickRange",
"type": "error"
},
{
"inputs": [],
"name": "InvalidTickSpacing",
"type": "error"
},
{
"inputs": [],
"name": "InvalidTokenAddressOrder",
"type": "error"
},
{
"inputs": [],
"name": "InvalidTokenPairOrder",
"type": "error"
},
{
"inputs": [],
"name": "InvalidUniv3ParameterSetId",
"type": "error"
},
{
"inputs": [],
"name": "InvalidWeth",
"type": "error"
},
{
"inputs": [],
"name": "NoTokensDeployed",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"inputs": [],
"name": "PageOutOfRange",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"inputs": [],
"name": "TickLowerOutOfRange",
"type": "error"
},
{
"inputs": [],
"name": "TickLowerSpacingInvalid",
"type": "error"
},
{
"inputs": [],
"name": "TickUpperOutOfRange",
"type": "error"
},
{
"inputs": [],
"name": "TickUpperSpacingInvalid",
"type": "error"
},
{
"inputs": [],
"name": "Token1MustBeWeth",
"type": "error"
},
{
"inputs": [],
"name": "TokenAllocationExceedsSupply",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "CreateTokenSwitched",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "tokenAddress",
"type": "address"
}
],
"name": "ERC20TokenCreated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "oldAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newAmount",
"type": "uint256"
}
],
"name": "InitialBuyAmountUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "tokenAddress",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "feeRecipient",
"type": "address"
}
],
"name": "TokenFeeRecipientSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "parameterSetId",
"type": "uint256"
}
],
"name": "Univ3ParameterSetCreated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "parameterSetId",
"type": "uint256"
}
],
"name": "Univ3ParameterSetDeleted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "parameterSetId",
"type": "uint256"
}
],
"name": "Univ3ParameterSetUpdated",
"type": "event"
},
{
"inputs": [],
"name": "FEE_TIER",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "collectFees",
"outputs": [
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadata",
"type": "string"
},
{
"internalType": "address",
"name": "feeRecipient",
"type": "address"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "split",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "uniV3ParameterSetId",
"type": "uint256"
}
],
"name": "createToken",
"outputs": [
{
"internalType": "uint256",
"name": "tokensReceived",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "createTokenEnabled",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint256",
"name": "amount0Desired",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1Desired",
"type": "uint256"
}
],
"name": "createUniv3ParameterSet",
"outputs": [
{
"internalType": "uint256",
"name": "parameterSetId",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "parameterSetId",
"type": "uint256"
}
],
"name": "deleteUniv3ParameterSet",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "deployedTokens",
"outputs": [
{
"internalType": "address",
"name": "tokenAddress",
"type": "address"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "address",
"name": "deployer",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "time",
"type": "uint256"
},
{
"internalType": "string",
"name": "metadata",
"type": "string"
},
{
"internalType": "uint256",
"name": "marketCapInETH",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "split",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "nftId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokenBalance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalFeesGenerated",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "feeRecipientForToken",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "page",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "order",
"type": "uint256"
}
],
"name": "getDeploysPaginated",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "tokenAddress",
"type": "address"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "address",
"name": "deployer",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "time",
"type": "uint256"
},
{
"internalType": "string",
"name": "metadata",
"type": "string"
},
{
"internalType": "uint256",
"name": "marketCapInETH",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "split",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "nftId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokenBalance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalFeesGenerated",
"type": "uint256"
}
],
"internalType": "struct TokenInfo[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "tokenAddress",
"type": "address"
}
],
"name": "getTokenMarketCap",
"outputs": [
{
"internalType": "uint256",
"name": "marketCap",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "initialBuyAmount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionManager",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "robinPadDeployer",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "setInitialBuyAmount",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "swapRouter",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "switchCreateToken",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "tickSpacing",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tokenCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "tokenFeesGenerated",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "tokenInfoByAddress",
"outputs": [
{
"internalType": "address",
"name": "tokenAddress",
"type": "address"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "address",
"name": "deployer",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "time",
"type": "uint256"
},
{
"internalType": "string",
"name": "metadata",
"type": "string"
},
{
"internalType": "uint256",
"name": "marketCapInETH",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "split",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "nftId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokenBalance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalFeesGenerated",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "tokenToNFTId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "uniV3ParameterSetCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "uniV3Parameters",
"outputs": [
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint256",
"name": "amount0Desired",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1Desired",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "uniswapV3Factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "parameterSetId",
"type": "uint256"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint256",
"name": "amount0Desired",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1Desired",
"type": "uint256"
}
],
"name": "updateUniv3ParameterSet",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "weth",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.9;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./Events.sol";
import "./Interfaces.sol";
import "./RobinPadToken.sol";
contract RobinPadFactory is IFactoryEvents, Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
error InvalidPositionManager();
error InvalidWeth();
error DeployDisabled();
error InvalidUniv3ParameterSetId();
error InsufficientInitialBuy();
error InvalidTokenAddressOrder();
error NoTokensDeployed();
error PageOutOfRange();
error CannotDeleteDefaultUniv3ParameterSet();
error InvalidTokenPairOrder();
error InvalidStoredTickLower();
error TickLowerOutOfRange();
error TickUpperOutOfRange();
error InvalidTickRange();
error TickLowerSpacingInvalid();
error TickUpperSpacingInvalid();
error InvalidTickSpacing();
error InvalidSqrtPrice();
error Token1MustBeWeth();
error TokenAllocationExceedsSupply();
error InvalidInitialBuyAmount();
error InvalidFeeRecipient();
mapping(uint256 => TokenInfo) public deployedTokens;
mapping(address => TokenInfo) public tokenInfoByAddress;
mapping(address => address) public feeRecipientForToken;
mapping(address => uint256) public tokenFeesGenerated;
mapping(address => uint256) public tokenToNFTId;
mapping(uint256 => Univ3ParameterSet) public uniV3Parameters;
uint256 public tokenCount = 0;
uint256 public uniV3ParameterSetCount = 0;
bool public createTokenEnabled = false;
address public immutable uniswapV3Factory;
address public immutable positionManager;
address public immutable weth;
address public immutable swapRouter;
address public immutable robinPadDeployer;
int24 public immutable tickSpacing;
uint24 public constant FEE_TIER = 10000;
int24 private constant MIN_TICK = -887272;
int24 private constant MAX_TICK = 887272;
uint256 private constant ITEMS_PER_PAGE = 50;
uint256 private constant TOKEN_TOTAL_SUPPLY = 1000000000 * 10 ** 18;
uint256 private constant BPS_DENOMINATOR = 10000;
uint256 private constant ROBINPAD_FEE_SHARE_BPS = 3000;
uint256 public initialBuyAmount = 0.00001 ether;
constructor(
address uniswapV3Factory_,
address positionManager_,
address weth_,
address swapRouter_
) Ownable(msg.sender) {
if (uniswapV3Factory_ == address(0)) revert InvalidPositionManager();
if (positionManager_ == address(0)) revert InvalidPositionManager();
if (weth_ == address(0)) revert InvalidWeth();
if (swapRouter_ == address(0)) revert InvalidPositionManager();
if (INonfungiblePositionManager(positionManager_).factory() != uniswapV3Factory_) revert InvalidPositionManager();
if (INonfungiblePositionManager(positionManager_).WETH9() != weth_) revert InvalidWeth();
int24 feeTickSpacing = IUniswapV3Factory(uniswapV3Factory_).feeAmountTickSpacing(FEE_TIER);
if (feeTickSpacing <= 0) revert InvalidTickSpacing();
uniswapV3Factory = uniswapV3Factory_;
positionManager = positionManager_;
weth = weth_;
swapRouter = swapRouter_;
robinPadDeployer = msg.sender;
tickSpacing = feeTickSpacing;
_validateUniv3ParameterSetTicks(200400, -87400);
uniV3Parameters[0] = Univ3ParameterSet({
sqrtPriceX96: 3527495286400061975389249,
tickLower: 200400,
tickUpper: -87400,
amount0Desired: TOKEN_TOTAL_SUPPLY,
amount1Desired: 0
});
uniV3ParameterSetCount = 1;
}
receive() external payable {}
function createToken(
string memory name,
string memory symbol,
string memory metadata,
address feeRecipient,
bytes32 salt,
uint256 split,
uint256 uniV3ParameterSetId
) public payable nonReentrant returns (uint256 tokensReceived) {
if (!createTokenEnabled) revert DeployDisabled();
if (uniV3ParameterSetId >= uniV3ParameterSetCount) revert InvalidUniv3ParameterSetId();
if (msg.value < initialBuyAmount) revert InsufficientInitialBuy();
if (feeRecipient == address(0)) revert InvalidFeeRecipient();
uint256 configuredTokenLiquidity = uniV3Parameters[uniV3ParameterSetId].amount0Desired;
_validateTokenLiquidityAmount(configuredTokenLiquidity);
bytes32 validSalt = _findUniswapSalt(name, symbol, metadata, salt);
address tokenAddress = _deployToken(validSalt, name, symbol, metadata);
if (tokenAddress >= weth) revert InvalidTokenAddressOrder();
_provideLiquidity(tokenAddress, weth, uniV3ParameterSetId);
_registerToken(tokenAddress, name, symbol, metadata, split, feeRecipient);
tokensReceived = _executeInitialBuy(tokenAddress);
tokensReceived += _handleExtraBuy(tokenAddress);
tokenCount++;
}
function switchCreateToken() external onlyOwner {
createTokenEnabled = !createTokenEnabled;
emit CreateTokenSwitched(createTokenEnabled);
}
function setInitialBuyAmount(uint256 amount) external onlyOwner {
if (amount == 0) revert InvalidInitialBuyAmount();
emit InitialBuyAmountUpdated(initialBuyAmount, amount);
initialBuyAmount = amount;
}
function createUniv3ParameterSet(
uint160 sqrtPriceX96,
int24 tickLower,
int24 tickUpper,
uint256 amount0Desired,
uint256 amount1Desired
) external onlyOwner returns (uint256 parameterSetId) {
if (sqrtPriceX96 == 0) revert InvalidSqrtPrice();
_validateTokenLiquidityAmount(amount0Desired);
_validateUniv3ParameterSetTicks(tickLower, tickUpper);
parameterSetId = uniV3ParameterSetCount;
uniV3Parameters[parameterSetId] = Univ3ParameterSet({
sqrtPriceX96: sqrtPriceX96,
tickLower: tickLower,
tickUpper: tickUpper,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired
});
uniV3ParameterSetCount++;
emit Univ3ParameterSetCreated(parameterSetId);
}
function updateUniv3ParameterSet(
uint256 parameterSetId,
uint160 sqrtPriceX96,
int24 tickLower,
int24 tickUpper,
uint256 amount0Desired,
uint256 amount1Desired
) external onlyOwner {
if (parameterSetId >= uniV3ParameterSetCount) revert InvalidUniv3ParameterSetId();
if (sqrtPriceX96 == 0) revert InvalidSqrtPrice();
_validateTokenLiquidityAmount(amount0Desired);
_validateUniv3ParameterSetTicks(tickLower, tickUpper);
uniV3Parameters[parameterSetId] = Univ3ParameterSet({
sqrtPriceX96: sqrtPriceX96,
tickLower: tickLower,
tickUpper: tickUpper,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired
});
emit Univ3ParameterSetUpdated(parameterSetId);
}
function deleteUniv3ParameterSet(uint256 parameterSetId) external onlyOwner {
if (parameterSetId >= uniV3ParameterSetCount) revert InvalidUniv3ParameterSetId();
if (parameterSetId == 0) revert CannotDeleteDefaultUniv3ParameterSet();
delete uniV3Parameters[parameterSetId];
emit Univ3ParameterSetDeleted(parameterSetId);
}
function getDeploysPaginated(uint256 page, uint256 order) public view returns (TokenInfo[] memory) {
if (tokenCount == 0) revert NoTokensDeployed();
uint256 totalPages = (tokenCount + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE;
if (page >= totalPages) revert PageOutOfRange();
uint256 start;
uint256 end;
uint256 j = 0;
if (order == 0) {
start = tokenCount > (page + 1) * ITEMS_PER_PAGE ? tokenCount - (page + 1) * ITEMS_PER_PAGE : 0;
end = tokenCount - page * ITEMS_PER_PAGE;
if (end > tokenCount) end = tokenCount;
} else {
start = page * ITEMS_PER_PAGE;
end = start + ITEMS_PER_PAGE;
if (end > tokenCount) end = tokenCount;
}
TokenInfo[] memory tokens = new TokenInfo[](end - start);
for (uint256 i = start; i < end; i++) {
uint256 index = order == 0 ? end - 1 - (i - start) : i;
TokenInfo memory info = deployedTokens[index];
tokens[j++] = TokenInfo({
tokenAddress: info.tokenAddress,
name: info.name,
symbol: info.symbol,
deployer: info.deployer,
creator: info.creator,
feeRecipient: info.feeRecipient,
time: info.time,
metadata: info.metadata,
marketCapInETH: getTokenMarketCap(info.tokenAddress),
split: info.split,
nftId: info.nftId,
tokenBalance: 0,
totalFeesGenerated: tokenFeesGenerated[info.tokenAddress]
});
}
return tokens;
}
function collectFees(uint256 tokenId) external nonReentrant returns (uint256 amount0, uint256 amount1) {
(, , address token0Raw, address token1Raw, , , , , , , , ) =
INonfungiblePositionManager(positionManager).positions(tokenId);
address token0 = token0Raw;
address token1 = token1Raw;
if (token0Raw == weth && token1Raw != weth) {
token0 = token1Raw;
token1 = token0Raw;
}
if (token1 != weth) revert Token1MustBeWeth();
uint256 beforeToken0 = IERC20(token0).balanceOf(address(this));
uint256 beforeToken1 = IERC20(token1).balanceOf(address(this));
INonfungiblePositionManager(positionManager).collect(
INonfungiblePositionManager.CollectParams({
tokenId: tokenId,
recipient: address(this),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
})
);
amount0 = IERC20(token0).balanceOf(address(this)) - beforeToken0;
amount1 = IERC20(token1).balanceOf(address(this)) - beforeToken1;
address launchFeeRecipient = feeRecipientForToken[token0];
if (launchFeeRecipient == address(0)) revert InvalidFeeRecipient();
_transferFeeSplit(token0, amount0, launchFeeRecipient);
_transferFeeSplit(token1, amount1, launchFeeRecipient);
tokenFeesGenerated[token0] += amount1;
}
function getTokenMarketCap(address tokenAddress) public view returns (uint256 marketCap) {
address pool = IUniswapV3Factory(uniswapV3Factory).getPool(tokenAddress, weth, FEE_TIER);
if (pool == address(0)) return 0;
uint256 totalSupply = IERC20(tokenAddress).totalSupply();
(uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(pool).slot0();
if (sqrtPriceX96 == 0 || totalSupply == 0) return 0;
bool tokenIsToken0 = tokenAddress < weth;
uint256 scaledSupply = totalSupply / 1e12;
if (tokenIsToken0) {
uint256 scaledPrice = (uint256(sqrtPriceX96) * sqrtPriceX96) >> 96;
marketCap = (scaledSupply * scaledPrice) >> 96;
} else {
uint256 denominator = (uint256(sqrtPriceX96) * sqrtPriceX96) >> 96;
if (denominator > 0) {
marketCap = (scaledSupply << 96) / denominator;
}
}
}
function _deployToken(
bytes32 salt,
string memory name,
string memory symbol,
string memory metadata
) internal returns (address tokenAddress) {
tokenAddress = address(new RobinPadToken{salt: salt}(name, symbol, metadata, positionManager, weth));
emit ERC20TokenCreated(tokenAddress);
}
function _registerToken(
address tokenAddress,
string memory name,
string memory symbol,
string memory metadata,
uint256 split,
address feeRecipient
) internal {
TokenInfo memory newTokenInfo = TokenInfo({
tokenAddress: tokenAddress,
name: name,
symbol: symbol,
deployer: msg.sender,
creator: msg.sender,
feeRecipient: feeRecipient,
time: block.timestamp,
metadata: metadata,
marketCapInETH: 0,
split: split,
nftId: tokenToNFTId[tokenAddress],
tokenBalance: 0,
totalFeesGenerated: 0
});
deployedTokens[tokenCount] = newTokenInfo;
tokenInfoByAddress[tokenAddress] = newTokenInfo;
feeRecipientForToken[tokenAddress] = feeRecipient;
emit TokenFeeRecipientSet(tokenAddress, feeRecipient);
}
function _executeInitialBuy(address tokenAddress) internal returns (uint256 tokensReceived) {
IWETH(weth).deposit{value: initialBuyAmount}();
IERC20(weth).approve(swapRouter, initialBuyAmount);
tokensReceived = ISwapRouter02(swapRouter).exactInputSingle(
ISwapRouter02.ExactInputSingleParams({
tokenIn: weth,
tokenOut: tokenAddress,
fee: FEE_TIER,
recipient: msg.sender,
amountIn: initialBuyAmount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
})
);
}
function _handleExtraBuy(address tokenAddress) internal returns (uint256 tokensReceived) {
uint256 extraEth = msg.value - initialBuyAmount;
if (extraEth == 0) return 0;
IWETH(weth).deposit{value: extraEth}();
IERC20(weth).approve(swapRouter, extraEth);
tokensReceived = ISwapRouter02(swapRouter).exactInputSingle(
ISwapRouter02.ExactInputSingleParams({
tokenIn: weth,
tokenOut: tokenAddress,
fee: FEE_TIER,
recipient: msg.sender,
amountIn: extraEth,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
})
);
}
function _provideLiquidity(address tokenA, address tokenB, uint256 parameterSetId) internal {
Univ3ParameterSet memory parameterSet = uniV3Parameters[parameterSetId];
_validateUniv3ParameterSetTicks(parameterSet.tickLower, parameterSet.tickUpper);
if (tokenA >= tokenB) revert InvalidTokenPairOrder();
IERC20(tokenA).approve(positionManager, type(uint256).max);
IERC20(tokenB).approve(positionManager, type(uint256).max);
INonfungiblePositionManager manager = INonfungiblePositionManager(positionManager);
manager.createAndInitializePoolIfNecessary(tokenA, tokenB, FEE_TIER, parameterSet.sqrtPriceX96);
(uint256 tokenId, , , ) = manager.mint(
INonfungiblePositionManager.MintParams({
token0: tokenA,
token1: tokenB,
fee: FEE_TIER,
tickLower: -parameterSet.tickLower,
tickUpper: parameterSet.tickUpper,
amount0Desired: parameterSet.amount0Desired,
amount1Desired: parameterSet.amount1Desired,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp
})
);
tokenToNFTId[tokenA] = tokenId;
}
function _transferFeeSplit(address token, uint256 amount, address launchFeeRecipient) internal {
if (amount == 0) return;
uint256 robinPadAmount = (amount * ROBINPAD_FEE_SHARE_BPS) / BPS_DENOMINATOR;
uint256 launchAmount = amount - robinPadAmount;
if (launchAmount > 0) {
IERC20(token).safeTransfer(launchFeeRecipient, launchAmount);
}
if (robinPadAmount > 0) {
IERC20(token).safeTransfer(robinPadDeployer, robinPadAmount);
}
}
function _getTokenBytecode(
string memory name,
string memory symbol,
string memory metadata
) internal view returns (bytes memory bytecode) {
bytecode = abi.encodePacked(
type(RobinPadToken).creationCode,
abi.encode(name, symbol, metadata, positionManager, weth)
);
}
function _predictTokenAddress(
string memory name,
string memory symbol,
string memory metadata,
bytes32 salt
) internal view returns (address) {
bytes memory bytecode = _getTokenBytecode(name, symbol, metadata);
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(bytecode)
)
);
return address(uint160(uint256(hash)));
}
function _findUniswapSalt(
string memory name,
string memory symbol,
string memory metadata,
bytes32 baseSalt
) internal view returns (bytes32) {
bytes32 salt = baseSalt;
uint256 nonce = 0;
while (nonce < 1000) {
address predictedAddress = _predictTokenAddress(name, symbol, metadata, salt);
if (predictedAddress < weth) return salt;
salt = keccak256(abi.encodePacked(baseSalt, nonce));
nonce++;
}
revert("Could not find v3-compatible salt");
}
function _validateUniv3ParameterSetTicks(int24 storedTickLower, int24 storedTickUpper) internal view {
if (storedTickLower <= 0) revert InvalidStoredTickLower();
int24 actualTickLower = -storedTickLower;
int24 minUsableTick = (MIN_TICK / tickSpacing) * tickSpacing;
int24 maxUsableTick = (MAX_TICK / tickSpacing) * tickSpacing;
if (actualTickLower < minUsableTick) revert TickLowerOutOfRange();
if (storedTickUpper < minUsableTick || storedTickUpper > maxUsableTick) revert TickUpperOutOfRange();
if (actualTickLower >= storedTickUpper) revert InvalidTickRange();
if (actualTickLower % tickSpacing != 0) revert TickLowerSpacingInvalid();
if (storedTickUpper % tickSpacing != 0) revert TickUpperSpacingInvalid();
}
function _validateTokenLiquidityAmount(uint256 amount0Desired) internal pure {
if (amount0Desired > TOKEN_TOTAL_SUPPLY) revert TokenAllocationExceedsSupply();
}
}
Chain explorer5338msChain node116ms