FlaunchZap
0x65d673f25b5878df2a2e8a5203fe2b2846c5cbba
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
7,342 bytes
ABI entries
11
4 read · 3 write
License
none
Contract information
- Address
- 0x65d673f25b5878df2a2e8a5203fe2b2846c5cbba
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x89f52C8B63…845cd47973
- Creation tx
- 0x4d12026215…a4e4c63f06
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (4)
calculateFee(tuple, uint256) → uint256
flaunchContract() → address
positionManager() → address
treasuryManagerFactory() → address
ABI
[
{
"inputs": [
{
"internalType": "contract PositionManager",
"name": "_positionManager",
"type": "address"
},
{
"internalType": "contract Flaunch",
"name": "_flaunchContract",
"type": "address"
},
{
"internalType": "contract ITreasuryManagerFactory",
"name": "_treasuryManagerFactory",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "CreatorCannotBeZero",
"type": "error"
},
{
"inputs": [],
"name": "TreasuryManagerFactoryNotSet",
"type": "error"
},
{
"inputs": [
{
"components": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "tokenUri",
"type": "string"
},
{
"internalType": "uint256",
"name": "premineAmount",
"type": "uint256"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint24",
"name": "creatorFeeAllocation",
"type": "uint24"
},
{
"internalType": "uint256",
"name": "flaunchAt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "initialPriceParams",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "feeCalculatorParams",
"type": "bytes"
}
],
"internalType": "struct IPositionManager.FlaunchParams",
"name": "_flaunchParams",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "_slippage",
"type": "uint256"
}
],
"name": "calculateFee",
"outputs": [
{
"internalType": "uint256",
"name": "ethRequired_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_managerImplementation",
"type": "address"
},
{
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"internalType": "bytes",
"name": "_data",
"type": "bytes"
},
{
"internalType": "address",
"name": "_permissions",
"type": "address"
}
],
"name": "deployAndInitializeManager",
"outputs": [
{
"internalType": "address payable",
"name": "manager_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "tokenUri",
"type": "string"
},
{
"internalType": "uint256",
"name": "premineAmount",
"type": "uint256"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint24",
"name": "creatorFeeAllocation",
"type": "uint24"
},
{
"internalType": "uint256",
"name": "flaunchAt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "initialPriceParams",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "feeCalculatorParams",
"type": "bytes"
}
],
"internalType": "struct IPositionManager.FlaunchParams",
"name": "_flaunchParams",
"type": "tuple"
},
{
"internalType": "address",
"name": "_trustedFeeSigner",
"type": "address"
}
],
"name": "flaunch",
"outputs": [
{
"internalType": "address",
"name": "memecoin_",
"type": "address"
},
{
"internalType": "uint256",
"name": "ethSpent_",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "tokenUri",
"type": "string"
},
{
"internalType": "uint256",
"name": "premineAmount",
"type": "uint256"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint24",
"name": "creatorFeeAllocation",
"type": "uint24"
},
{
"internalType": "uint256",
"name": "flaunchAt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "initialPriceParams",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "feeCalculatorParams",
"type": "bytes"
}
],
"internalType": "struct IPositionManager.FlaunchParams",
"name": "_flaunchParams",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "manager",
"type": "address"
},
{
"internalType": "address",
"name": "permissions",
"type": "address"
},
{
"internalType": "bytes",
"name": "initializeData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "depositData",
"type": "bytes"
}
],
"internalType": "struct IFlaunchZap.TreasuryManagerParams",
"name": "_treasuryManagerParams",
"type": "tuple"
},
{
"internalType": "address",
"name": "_trustedFeeSigner",
"type": "address"
}
],
"name": "flaunch",
"outputs": [
{
"internalType": "address",
"name": "memecoin_",
"type": "address"
},
{
"internalType": "uint256",
"name": "ethSpent_",
"type": "uint256"
},
{
"internalType": "address",
"name": "deployedManager_",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "flaunchContract",
"outputs": [
{
"internalType": "contract Flaunch",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionManager",
"outputs": [
{
"internalType": "contract PositionManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "treasuryManagerFactory",
"outputs": [
{
"internalType": "contract ITreasuryManagerFactory",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {SafeTransferLib} from '@solady/utils/SafeTransferLib.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {PoolIdLibrary} from '@uniswap/v4-core/src/types/PoolId.sol';
import {PoolKey} from '@uniswap/v4-core/src/types/PoolKey.sol';
import {Flaunch} from '@flaunch/Flaunch.sol';
import {PositionManager} from '@flaunch/PositionManager.sol';
import {TokenSupply} from '@flaunch/libraries/TokenSupply.sol';
import {IFlaunchZap} from '@flaunch-interfaces/IFlaunchZap.sol';
import {IPositionManager} from '@flaunch-interfaces/IPositionManager.sol';
import {ITreasuryManager} from '@flaunch-interfaces/ITreasuryManager.sol';
import {ITreasuryManagerFactory} from '@flaunch-interfaces/ITreasuryManagerFactory.sol';
import {ITrustedSignerFeeCalculator} from '@flaunch-interfaces/ITrustedSignerFeeCalculator.sol';
/**
* Single-transaction entrypoint for flaunching a memecoin with optional extras that the
* {PositionManager} alone cannot provide: registering a trusted fee signer against the new pool
* and escrowing the Flaunch ERC721 into a treasury manager.
*
* The zap holds no funds between transactions: any ETH or memecoin it handles during a launch is
* forwarded or refunded before the call returns.
*/
contract FlaunchZap is IFlaunchZap {
using PoolIdLibrary for PoolKey;
/// The Flaunch {PositionManager} that launches are forwarded to
PositionManager public immutable positionManager;
/// The {Flaunch} ERC721 that represents ownership of a flaunched pool
Flaunch public immutable flaunchContract;
/// The factory used to deploy / recognise treasury managers. May be `address(0)` on chains
/// where no factory has been deployed yet: manager flaunches then fall through to the
/// unknown-manager path (approve + best-effort deposit + transfer), and
/// {deployAndInitializeManager} reverts.
ITreasuryManagerFactory public immutable treasuryManagerFactory;
/**
* References the contract addresses for the Flaunch protocol.
*
* @param _positionManager The Flaunch {PositionManager} contract
* @param _flaunchContract The {Flaunch} ERC721 contract
* @param _treasuryManagerFactory The {ITreasuryManagerFactory}, or `address(0)` if none exists
*/
constructor(
PositionManager _positionManager,
Flaunch _flaunchContract,
ITreasuryManagerFactory _treasuryManagerFactory
) {
positionManager = _positionManager;
flaunchContract = _flaunchContract;
treasuryManagerFactory = _treasuryManagerFactory;
}
/**
* Flaunches a memecoin without any additional logic.
*
* @param _flaunchParams The base flaunch parameters
* @param _trustedFeeSigner Optional trusted fee signer for this memecoin
*
* @return memecoin_ The created ERC20 token address
* @return ethSpent_ The amount of ETH spent during the premine
*/
function flaunch(
IPositionManager.FlaunchParams memory _flaunchParams,
address _trustedFeeSigner
) external payable refundsEth returns (address memecoin_, uint ethSpent_) {
return _flaunch(_flaunchParams, _trustedFeeSigner);
}
/**
* Flaunches a memecoin and escrows it into a treasury manager.
*
* The zap flaunches with itself as the temporary creator so that it receives the Flaunch
* ERC721, then wires the manager (see {_createWithManagerZap}) and deposits the token into it
* on behalf of the original creator. Any premined memecoin is swept to the original creator.
*
* @param _flaunchParams The base flaunch parameters
* @param _treasuryManagerParams Optional manager wiring for the new pool
* @param _trustedFeeSigner Optional trusted fee signer for this memecoin
*
* @return memecoin_ The created ERC20 token address
* @return ethSpent_ The amount of ETH spent during the premine
* @return deployedManager_ The manager the flaunch token was deposited into
*/
function flaunch(
IPositionManager.FlaunchParams memory _flaunchParams,
TreasuryManagerParams calldata _treasuryManagerParams,
address _trustedFeeSigner
) external payable refundsEth returns (address memecoin_, uint ethSpent_, address deployedManager_) {
return _flaunch(_flaunchParams, _treasuryManagerParams, _trustedFeeSigner);
}
/**
* Deploys and initializes a manager instance from an approved implementation, setting its
* permissions before handing ownership to `_owner`.
*
* @param _managerImplementation The approved manager implementation to clone
* @param _owner The final owner of the deployed manager
* @param _data Initialization data passed to the manager
* @param _permissions The permissions contract set against the manager
*
* @return manager_ The deployed manager instance
*/
function deployAndInitializeManager(
address _managerImplementation,
address _owner,
bytes calldata _data,
address _permissions
) public returns (address payable manager_) {
if (address(treasuryManagerFactory) == address(0)) {
revert TreasuryManagerFactoryNotSet();
}
manager_ = treasuryManagerFactory.deployAndInitializeManager({
_managerImplementation: _managerImplementation,
_owner: address(this),
_data: _data
});
ITreasuryManager(manager_).setPermissions(_permissions);
ITreasuryManager(manager_).transferManagerOwnership(_owner);
}
/**
* Quotes the ETH a caller needs to supply for a launch: the flaunch fee plus the cost of any
* premine, with an optional slippage buffer applied on top.
*
* @param _flaunchParams The base flaunch parameters
* @param _slippage Optional slippage buffer, in basis points (100_00 == 100%)
*
* @return ethRequired_ The ETH the caller must send with the launch
*/
function calculateFee(
IPositionManager.FlaunchParams memory _flaunchParams,
uint _slippage
) public view returns (uint ethRequired_) {
uint premineCost = positionManager.getFlaunchingMarketCap(_flaunchParams.initialPriceParams) * _flaunchParams.premineAmount
/ TokenSupply.INITIAL_SUPPLY;
ethRequired_ = positionManager.getFlaunchingFee(_flaunchParams.initialPriceParams) + premineCost;
if (_slippage != 0) {
ethRequired_ += ethRequired_ * _slippage / 100_00;
}
}
/**
* Convenience wrapper for launches without a treasury manager.
*
* @param _flaunchParams The base flaunch parameters
* @param _trustedFeeSigner Optional trusted fee signer for this memecoin
*
* @return memecoin_ The created ERC20 token address
* @return ethSpent_ The amount of ETH spent during the premine
*/
function _flaunch(
IPositionManager.FlaunchParams memory _flaunchParams,
address _trustedFeeSigner
) internal returns (address memecoin_, uint ethSpent_) {
// A zero-value manager struct means no manager is wired for this launch
TreasuryManagerParams memory noManagerParams;
(memecoin_, ethSpent_,) = _flaunch(_flaunchParams, noManagerParams, _trustedFeeSigner);
}
/**
* The core launch routine that all entrypoints funnel into: flaunches the memecoin, reports
* the ETH spent and, if requested, escrows the flaunched token into a treasury manager.
*
* @param _flaunchParams The base flaunch parameters
* @param _treasuryManagerParams Optional manager wiring for the new pool
* @param _trustedFeeSigner Optional trusted fee signer for this memecoin
*
* @return memecoin_ The created ERC20 token address
* @return ethSpent_ The amount of ETH spent during the premine
* @return deployedManager_ The manager the flaunch token was deposited into
*/
function _flaunch(
IPositionManager.FlaunchParams memory _flaunchParams,
TreasuryManagerParams memory _treasuryManagerParams,
address _trustedFeeSigner
) internal returns (address memecoin_, uint ethSpent_, address deployedManager_) {
address originalCreator = _flaunchParams.creator;
if (originalCreator == address(0)) {
revert CreatorCannotBeZero();
}
// When a manager is requested, the zap becomes the temporary creator so that it receives
// the Flaunch ERC721 (and any premine) and can deposit the token into the manager. The
// trusted-signer path inside {_flaunchMemecoin} sees `creator == address(this)` and skips
// its own NFT return / premine sweep, so both are handled exclusively below.
bool hasManager = _treasuryManagerParams.manager != address(0);
if (hasManager) {
_flaunchParams.creator = address(this);
}
memecoin_ = _flaunchMemecoin(_flaunchParams, _trustedFeeSigner);
// The ETH spent is the flaunch fee plus the ETH-funded premine cost
ethSpent_ = positionManager.getFlaunchingFee(_flaunchParams.initialPriceParams)
+ (positionManager.getFlaunchingMarketCap(_flaunchParams.initialPriceParams)
* _flaunchParams.premineAmount
/ TokenSupply.INITIAL_SUPPLY);
if (hasManager) {
deployedManager_ = _createWithManagerZap(memecoin_, originalCreator, _treasuryManagerParams);
// The manager takes only the ERC721; any premine was delivered to the zap as the
// temporary creator, so sweep it to the original creator so it is not stranded.
uint memecoinBalance = IERC20(memecoin_).balanceOf(address(this));
if (memecoinBalance != 0) {
SafeTransferLib.safeTransfer(memecoin_, originalCreator, memecoinBalance);
}
}
}
/**
* Performs the {PositionManager} flaunch call, registering a trusted fee signer against the
* created pool if one was supplied.
*
* Registering a signer can only be done by the pool creator, so the zap makes itself the
* temporary creator for the launch and afterwards forwards the ERC721 and any premined
* memecoin to the original creator. If the creator was already set to this zap by the manager
* flow, that forwarding is skipped and ownership is handled by the caller instead.
*
* @param _flaunchParams The base flaunch parameters
* @param _trustedFeeSigner Optional trusted fee signer for this memecoin
*
* @return memecoin_ The created ERC20 token address
*/
function _flaunchMemecoin(
IPositionManager.FlaunchParams memory _flaunchParams,
address _trustedFeeSigner
) internal returns (address memecoin_) {
// Without a signer there is nothing for the zap to do post-launch; forward the call as-is
if (_trustedFeeSigner == address(0)) {
memecoin_ = positionManager.flaunch{value: msg.value}(_flaunchParams);
return memecoin_;
}
// Become the temporary creator so that the zap is allowed to register the signer
address originalCreator = _flaunchParams.creator;
if (originalCreator != address(this)) {
_flaunchParams.creator = address(this);
}
memecoin_ = positionManager.flaunch{value: msg.value}(_flaunchParams);
// Register the trusted signer against the pool that was just created
PoolKey memory createdPoolKey = positionManager.poolKey(memecoin_);
ITrustedSignerFeeCalculator(address(positionManager.feeCalculator()))
.setTrustedPoolKeySigner({_poolKey: createdPoolKey, _signer: _trustedFeeSigner});
// Forward the Flaunch ERC721 to the original creator
if (originalCreator != address(this)) {
flaunchContract.transferFrom(address(this), originalCreator, flaunchContract.tokenId(memecoin_));
}
// The premine is delivered to the flaunch `creator`, which is temporarily set to this
// zap so it can register the trusted signer. Sweep any premined memecoin back to the
// original creator so it is not stranded in the zap.
uint memecoinBalance = IERC20(memecoin_).balanceOf(address(this));
if (memecoinBalance != 0 && originalCreator != address(this)) {
SafeTransferLib.safeTransfer(memecoin_, originalCreator, memecoinBalance);
}
}
/**
* Escrows a freshly flaunched token (held by this zap as the temporary creator) into a
* treasury manager, dispatching on what the manager address is:
*
* 1. An approved manager implementation -> deploy + initialize a fresh instance through the
* factory and deposit into it, optionally applying permissions before handing the manager
* ownership to the creator.
* 2. An instance previously deployed by the factory -> deposit into it directly.
* 3. An unknown address -> best-effort `deposit` and, if the zap still holds the ERC721
* afterwards, transfer it to the address directly.
*
* If no factory is bound to this zap (`address(0)`), every manager routes through (3).
*
* @param _memecoin The flaunched memecoin whose ERC721 is being escrowed
* @param _creator The original creator that the deposit is made on behalf of
* @param _treasuryManagerParams The manager wiring requested by the caller
*
* @return deployedManager_ The manager the flaunch token was deposited into
*/
function _createWithManagerZap(
address _memecoin,
address _creator,
TreasuryManagerParams memory _treasuryManagerParams
) internal returns (address deployedManager_) {
uint tokenId = flaunchContract.tokenId(_memecoin);
ITreasuryManager.FlaunchToken memory flaunchToken =
ITreasuryManager.FlaunchToken({flaunch: flaunchContract, tokenId: tokenId});
bool hasFactory = address(treasuryManagerFactory) != address(0);
if (hasFactory && treasuryManagerFactory.approvedManagerImplementation(_treasuryManagerParams.manager)) {
// If permissions will be applied, the zap must own the manager while it sets them, and
// only hands ownership to the creator afterwards.
address initialOwner = _treasuryManagerParams.permissions == address(0) ? _creator : address(this);
deployedManager_ = treasuryManagerFactory.deployAndInitializeManager({
_managerImplementation: _treasuryManagerParams.manager,
_owner: initialOwner,
_data: _treasuryManagerParams.initializeData
});
flaunchContract.approve(deployedManager_, tokenId);
ITreasuryManager(deployedManager_).deposit({
_flaunchToken: flaunchToken,
_creator: _creator,
_data: _treasuryManagerParams.depositData
});
if (_treasuryManagerParams.permissions != address(0)) {
ITreasuryManager(deployedManager_).setPermissions(_treasuryManagerParams.permissions);
ITreasuryManager(deployedManager_).transferManagerOwnership(_creator);
}
} else if (hasFactory && treasuryManagerFactory.managerImplementation(_treasuryManagerParams.manager) != address(0)) {
flaunchContract.approve(_treasuryManagerParams.manager, tokenId);
deployedManager_ = _treasuryManagerParams.manager;
ITreasuryManager(deployedManager_).deposit({
_flaunchToken: flaunchToken,
_creator: _creator,
_data: _treasuryManagerParams.depositData
});
} else {
flaunchContract.approve(_treasuryManagerParams.manager, tokenId);
try ITreasuryManager(_treasuryManagerParams.manager).deposit(
flaunchToken, _creator, _treasuryManagerParams.depositData
) {} catch {}
if (flaunchContract.ownerOf(tokenId) == address(this)) {
flaunchContract.transferFrom(address(this), _treasuryManagerParams.manager, tokenId);
}
deployedManager_ = _treasuryManagerParams.manager;
}
}
/**
* Refunds any ETH left in the zap after the call to the caller, so nothing is stranded.
*/
modifier refundsEth() {
_;
uint remainingBalance = payable(address(this)).balance;
if (remainingBalance != 0) {
SafeTransferLib.safeTransferETH(msg.sender, remainingBalance);
}
}
/// Allows the {PositionManager} to refund unspent ETH during a launch
receive() external payable {}
}
Chain explorer1854msChain node91ms