LaunchFeeSplitter

0x2b3d54c04362a8c1e2c63c5a2d3d6217a7694544

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
4,472 bytes
ABI entries
27
9 read · 5 write
License
none

Contract information

Address
0x2b3d54c04362a8c1e2c63c5a2d3d6217a7694544
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x6c3adc1ca9…3e0f05c52f

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (9)

creatorRewards(address)uint256
creatorShareBps()uint256
deployer()address
hdxPoolKey()address, address, uint24, int24, address
hdxPoolSet()bool
launchHook()address
poolManager()address
totalCreatorRewardsAccrued()uint256
treasury()address

Events (2)

CreatorRewardsClaimedLaunchFeeAttributed

ABI

[
  {
    "inputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "_poolManager",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_treasury",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "_creatorShareBps",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AlreadySet",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ForwardFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotPoolManager",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NothingToClaim",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyCreator",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyDeployer",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyLaunchHook",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "received",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minOut",
        "type": "uint256"
      }
    ],
    "name": "SlippageExceeded",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "coin",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "hdxOut",
        "type": "uint256"
      }
    ],
    "name": "CreatorRewardsClaimed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "coin",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "creatorCut",
        "type": "uint256"
      }
    ],
    "name": "LaunchFeeAttributed",
    "type": "event"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "coin",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "minHdxOut",
        "type": "uint256"
      }
    ],
    "name": "claimCreatorRewards",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "coin",
        "type": "address"
      }
    ],
    "name": "creatorRewards",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "creatorShareBps",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "deployer",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "hdxPoolKey",
    "outputs": [
      {
        "internalType": "Currency",
        "name": "currency0",
        "type": "address"
      },
      {
        "internalType": "Currency",
        "name": "currency1",
        "type": "address"
      },
      {
        "internalType": "uint24",
        "name": "fee",
        "type": "uint24"
      },
      {
        "internalType": "int24",
        "name": "tickSpacing",
        "type": "int24"
      },
      {
        "internalType": "contract IHooks",
        "name": "hooks",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "hdxPoolSet",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchHook",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "coin",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "onLaunchFee",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolManager",
    "outputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      }
    ],
    "name": "setHdxPool",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "hook",
        "type": "address"
      }
    ],
    "name": "setLaunchHook",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalCreatorRewardsAccrued",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "treasury",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "unlockCallback",
    "outputs": [
      {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {IUnlockCallback} from "v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {Currency} from "v4-core/src/types/Currency.sol";
import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol";
import {SwapParams} from "v4-core/src/types/PoolOperation.sol";
import {TickMath} from "v4-core/src/libraries/TickMath.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

import {LaunchCoin} from "./LaunchCoin.sol";

/// @title LaunchFeeSplitter — CREATOR REWARDS for launchpad-coins
/// @notice Fee-mottaker for alle launchpad-pools. Av launchpadens 4 %-fee:
///           - 1 %-poeng (25 % av feen) oeremerkes COINENS CREATOR
///           - resten (3 %-poeng) videresendes HDX-treasury umiddelbart
///         Creator claimer naar som helst — utbetalingen MARKEDSKJOEPER HDX
///         i HDX-poolen og sender tokens til creator. Hver claim er altsaa
///         kjoepepress paa HDX. "Launch hos oss, tjen HDX."
///
/// @dev Attribusjon: LaunchFeeHook kaller onLaunchFee(coin, amount) rett
///      etter at ETH-feen er tatt hit via poolManager.take.
/// @dev Foer HDX-poolen er satt (setHdxPool, én gang) utbetales claims i
///      ETH som fallback — relevant kun for testnet.
contract LaunchFeeSplitter is IUnlockCallback, ReentrancyGuard {
    address public immutable treasury;
    IPoolManager public immutable poolManager;
    address public immutable deployer;

    /// @notice Andel av feen som gaar til creator. 2500 bps av 4 %-feen
    ///         = 1 %-poeng av traden.
    uint256 public immutable creatorShareBps;
    uint256 internal constant BPS = 10_000;

    address public launchHook;     // settes én gang etter hook-deploy
    PoolKey public hdxPoolKey;     // settes én gang naar HDX-poolen finnes
    bool public hdxPoolSet;

    /// @notice Claimbar creator-pott per coin, i ETH (veksles til HDX ved claim).
    mapping(address coin => uint256) public creatorRewards;
    uint256 public totalCreatorRewardsAccrued; // livstid, for statistikk

    event LaunchFeeAttributed(address indexed coin, uint256 fee, uint256 creatorCut);
    event CreatorRewardsClaimed(address indexed coin, address indexed creator, uint256 ethIn, uint256 hdxOut);

    error OnlyDeployer();
    error OnlyLaunchHook();
    error OnlyCreator();
    error AlreadySet();
    error NothingToClaim();
    error ForwardFailed();
    error NotPoolManager();
    error SlippageExceeded(uint256 received, uint256 minOut);

    constructor(IPoolManager _poolManager, address _treasury, uint256 _creatorShareBps) {
        require(_creatorShareBps <= BPS, "share too high");
        poolManager = _poolManager;
        treasury = _treasury;
        creatorShareBps = _creatorShareBps;
        deployer = msg.sender;
    }

    receive() external payable {} // ETH fra poolManager.take (hook-fees)

    // ---- Engangs-oppsett (deployer, foer bruk) ----

    function setLaunchHook(address hook) external {
        if (msg.sender != deployer) revert OnlyDeployer();
        if (launchHook != address(0)) revert AlreadySet();
        launchHook = hook;
    }

    function setHdxPool(PoolKey calldata key) external {
        if (msg.sender != deployer) revert OnlyDeployer();
        if (hdxPoolSet) revert AlreadySet();
        hdxPoolKey = key;
        hdxPoolSet = true;
    }

    // ---- Fee-attribusjon (kalles av hooken per swap) ----

    function onLaunchFee(address coin, uint256 amount) external {
        if (msg.sender != launchHook) revert OnlyLaunchHook();
        uint256 creatorCut = (amount * creatorShareBps) / BPS;
        creatorRewards[coin] += creatorCut;
        totalCreatorRewardsAccrued += creatorCut;

        uint256 forward = amount - creatorCut;
        if (forward > 0) {
            (bool ok,) = treasury.call{value: forward}("");
            if (!ok) revert ForwardFailed();
        }
        emit LaunchFeeAttributed(coin, amount, creatorCut);
    }

    // ---- Creator claim: ETH-potten markedskjoeper HDX til creator ----

    /// @param minHdxOut AUDIT-fix M-3: minste HDX creator godtar for potten —
    ///        uten dette kunne claim-kjoepet sandwiches uten grense. Settes av
    ///        frontenden fra gjeldende pris med slippage-toleranse. Ignoreres
    ///        i ETH-fallbacken (ingen swap skjer da).
    function claimCreatorRewards(address coin, uint256 minHdxOut) external nonReentrant {
        address creator = LaunchCoin(coin).creator();
        if (msg.sender != creator) revert OnlyCreator();
        uint256 amount = creatorRewards[coin];
        if (amount == 0) revert NothingToClaim();
        creatorRewards[coin] = 0;

        if (hdxPoolSet) {
            poolManager.unlock(abi.encode(coin, creator, amount, minHdxOut));
        } else {
            // Testnet-fallback foer HDX-poolen finnes: utbetal ETH.
            (bool ok,) = creator.call{value: amount}("");
            if (!ok) revert ForwardFailed();
            emit CreatorRewardsClaimed(coin, creator, amount, 0);
        }
    }

    /// @dev Kjoep HDX for creator-potten: exactInput ETH -> HDX i HDX-poolen,
    ///      HDX tas direkte ut til creator. (Kjøpet betaler selv 4 %-hooken
    ///      — creator-claims mater altsaa ogsaa treasury.)
    function unlockCallback(bytes calldata data) external returns (bytes memory) {
        if (msg.sender != address(poolManager)) revert NotPoolManager();
        (address coin, address creator, uint256 amount, uint256 minHdxOut) =
            abi.decode(data, (address, address, uint256, uint256));

        BalanceDelta delta = poolManager.swap(
            hdxPoolKey,
            SwapParams({
                zeroForOne: true, // ETH (currency0) -> HDX (currency1)
                amountSpecified: -int256(amount),
                sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1
            }),
            ""
        );

        // Betal ETH-siden, ta HDX-siden rett til creator.
        if (delta.amount0() < 0) {
            poolManager.settle{value: uint256(uint128(-delta.amount0()))}();
        }
        uint256 hdxOut;
        if (delta.amount1() > 0) {
            hdxOut = uint256(uint128(delta.amount1()));
            poolManager.take(hdxPoolKey.currency1, creator, hdxOut);
        }
        if (hdxOut < minHdxOut) revert SlippageExceeded(hdxOut, minHdxOut);
        emit CreatorRewardsClaimed(coin, creator, amount, hdxOut);
        return "";
    }
}
Chain explorer3903msChain node81ms