Clanker
0xd3f2cc1731b7fd17f28798835c2e02f0a1839a94
Verification
Verified
v0.8.28+commit.7893614a
Type
Contract
12,070 bytes
ABI entries
55
11 read · 11 write
License
none
Contract information
- Address
- 0xd3f2cc1731b7fd17f28798835c2e02f0a1839a94
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.28+commit.7893614a
- Optimization
- Enabled
- Creator
- 0x4e59b44847…26c0B4956C
- Creation tx
- 0x28b5e185a9…433ae9892f
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (11)
BPS() → uint256
MAX_EXTENSIONS() → uint256
MAX_EXTENSION_BPS() → uint16
TOKEN_SUPPLY() → uint256
admins(address) → bool
deploymentInfoForToken(address) → address, address, address
deprecated() → bool
enabledLockers(address, address) → bool
owner() → address
teamFeeRecipient() → address
tokenDeploymentInfo(address) → tuple
Events (11)
ClaimTeamFeesExtensionTriggeredOwnershipTransferredSetAdminSetDeprecatedSetExtensionSetHookSetLockerSetMevModuleSetTeamFeeRecipientTokenCreated
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "owner_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "Deprecated",
"type": "error"
},
{
"inputs": [],
"name": "ExtensionMsgValueMismatch",
"type": "error"
},
{
"inputs": [],
"name": "ExtensionNotEnabled",
"type": "error"
},
{
"inputs": [],
"name": "HookNotEnabled",
"type": "error"
},
{
"inputs": [],
"name": "InvalidExtension",
"type": "error"
},
{
"inputs": [],
"name": "InvalidHook",
"type": "error"
},
{
"inputs": [],
"name": "InvalidLocker",
"type": "error"
},
{
"inputs": [],
"name": "InvalidMevModule",
"type": "error"
},
{
"inputs": [],
"name": "LockerNotEnabled",
"type": "error"
},
{
"inputs": [],
"name": "MaxExtensionBpsExceeded",
"type": "error"
},
{
"inputs": [],
"name": "MaxExtensionsExceeded",
"type": "error"
},
{
"inputs": [],
"name": "MevModuleNotEnabled",
"type": "error"
},
{
"inputs": [],
"name": "NotFound",
"type": "error"
},
{
"inputs": [],
"name": "OnlyNonOriginatingChains",
"type": "error"
},
{
"inputs": [],
"name": "OnlyOriginatingChain",
"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": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"inputs": [],
"name": "TeamFeeRecipientNotSet",
"type": "error"
},
{
"inputs": [],
"name": "Unauthorized",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "ClaimTeamFees",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "extension",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "extensionSupply",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "msgValue",
"type": "uint256"
}
],
"name": "ExtensionTriggered",
"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": "admin",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "SetAdmin",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "deprecated",
"type": "bool"
}
],
"name": "SetDeprecated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "extension",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "SetExtension",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "hook",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "SetHook",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "locker",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "hook",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "SetLocker",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "mevModule",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "SetMevModule",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "oldTeamFeeRecipient",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "newTeamFeeRecipient",
"type": "address"
}
],
"name": "SetTeamFeeRecipient",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "msgSender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "tokenAddress",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "tokenAdmin",
"type": "address"
},
{
"indexed": false,
"internalType": "string",
"name": "tokenImage",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "tokenName",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "tokenSymbol",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "tokenMetadata",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "tokenContext",
"type": "string"
},
{
"indexed": false,
"internalType": "int24",
"name": "startingTick",
"type": "int24"
},
{
"indexed": false,
"internalType": "address",
"name": "poolHook",
"type": "address"
},
{
"indexed": false,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "address",
"name": "pairedToken",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "locker",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "mevModule",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "extensionsSupply",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address[]",
"name": "extensions",
"type": "address[]"
}
],
"name": "TokenCreated",
"type": "event"
},
{
"inputs": [],
"name": "BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_EXTENSIONS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_EXTENSION_BPS",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TOKEN_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "admins",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "claimTeamFees",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"components": [
{
"internalType": "address",
"name": "tokenAdmin",
"type": "address"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "string",
"name": "image",
"type": "string"
},
{
"internalType": "string",
"name": "metadata",
"type": "string"
},
{
"internalType": "string",
"name": "context",
"type": "string"
},
{
"internalType": "uint256",
"name": "originatingChainId",
"type": "uint256"
}
],
"internalType": "struct IClanker.TokenConfig",
"name": "tokenConfig",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "hook",
"type": "address"
},
{
"internalType": "address",
"name": "pairedToken",
"type": "address"
},
{
"internalType": "int24",
"name": "tickIfToken0IsClanker",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "bytes",
"name": "poolData",
"type": "bytes"
}
],
"internalType": "struct IClanker.PoolConfig",
"name": "poolConfig",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "locker",
"type": "address"
},
{
"internalType": "address[]",
"name": "rewardAdmins",
"type": "address[]"
},
{
"internalType": "address[]",
"name": "rewardRecipients",
"type": "address[]"
},
{
"internalType": "uint16[]",
"name": "rewardBps",
"type": "uint16[]"
},
{
"internalType": "int24[]",
"name": "tickLower",
"type": "int24[]"
},
{
"internalType": "int24[]",
"name": "tickUpper",
"type": "int24[]"
},
{
"internalType": "uint16[]",
"name": "positionBps",
"type": "uint16[]"
},
{
"internalType": "bytes",
"name": "lockerData",
"type": "bytes"
}
],
"internalType": "struct IClanker.LockerConfig",
"name": "lockerConfig",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "mevModule",
"type": "address"
},
{
"internalType": "bytes",
"name": "mevModuleData",
"type": "bytes"
}
],
"internalType": "struct IClanker.MevModuleConfig",
"name": "mevModuleConfig",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "extension",
"type": "address"
},
{
"internalType": "uint256",
"name": "msgValue",
"type": "uint256"
},
{
"internalType": "uint16",
"name": "extensionBps",
"type": "uint16"
},
{
"internalType": "bytes",
"name": "extensionData",
"type": "bytes"
}
],
"internalType": "struct IClanker.ExtensionConfig[]",
"name": "extensionConfigs",
"type": "tuple[]"
}
],
"internalType": "struct IClanker.DeploymentConfig",
"name": "deploymentConfig",
"type": "tuple"
}
],
"name": "deployToken",
"outputs": [
{
"internalType": "address",
"name": "tokenAddress",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "deploymentInfoForToken",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "hook",
"type": "address"
},
{
"internalType": "address",
"name": "locker",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "deprecated",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "locker",
"type": "address"
},
{
"internalType": "address",
"name": "hook",
"type": "address"
}
],
"name": "enabledLockers",
"outputs": [
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "admin",
"type": "address"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "setAdmin",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "deprecated_",
"type": "bool"
}
],
"name": "setDeprecated",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "extension",
"type": "address"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "setExtension",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "hook",
"type": "address"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "setHook",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "locker",
"type": "address"
},
{
"internalType": "address",
"name": "hook",
"type": "address"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "setLocker",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "mevModule",
"type": "address"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "setMevModule",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "teamFeeRecipient_",
"type": "address"
}
],
"name": "setTeamFeeRecipient",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "teamFeeRecipient",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "tokenDeploymentInfo",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "hook",
"type": "address"
},
{
"internalType": "address",
"name": "locker",
"type": "address"
},
{
"internalType": "address[]",
"name": "extensions",
"type": "address[]"
}
],
"internalType": "struct IClanker.DeploymentInfo",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {ClankerDeployer} from "./utils/ClankerDeployer.sol";
import {OwnerAdmins} from "./utils/OwnerAdmins.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IClanker} from "./interfaces/IClanker.sol";
import {IClankerExtension} from "./interfaces/IClankerExtension.sol";
import {IClankerHook} from "./interfaces/IClankerHook.sol";
import {IClankerLpLocker} from "./interfaces/IClankerLpLocker.sol";
import {IClankerMevModule} from "./interfaces/IClankerMevModule.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
/*
.--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--.
/ .. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \
\ \/\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ \/ /
\/ /`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'\/ /
/ /\ ```````````````````````````````````````````````````````````````````````````````````` / /\
/ /\ \ ```````````````````````````````````````````````````````````````````````````````````` / /\ \
\ \/ / ```````::::::::``:::````````````:::`````::::````:::`:::````:::`::::::::::`:::::::::` \ \/ /
\/ / `````:+:````:+:`:+:``````````:+:`:+:```:+:+:```:+:`:+:```:+:``:+:````````:+:````:+:` \/ /
/ /\ ````+:+````````+:+`````````+:+```+:+``:+:+:+``+:+`+:+``+:+```+:+````````+:+````+:+`` / /\
/ /\ \ ```+#+````````+#+````````+#++:++#++:`+#+`+:+`+#+`+#++:++````+#++:++#```+#++:++#:```` / /\ \
\ \/ / ``+#+````````+#+````````+#+`````+#+`+#+``+#+#+#`+#+``+#+```+#+````````+#+````+#+```` \ \/ /
\/ / `#+#````#+#`#+#````````#+#`````#+#`#+#```#+#+#`#+#```#+#``#+#````````#+#````#+#````` \/ /
/ /\ `########``##########`###`````###`###````####`###````###`##########`###````###`````` / /\
/ /\ \ ```````````````````````````````````````````````````````````````````````````````````` / /\ \
\ \/ / ```````````````````````````````````````````````````````````````````````````````````` \ \/ /
\/ / ```````````````````````````````````````````````````````````````````````````````````` \/ /
/ /\.--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--..--./ /\
/ /\ \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \.. \/\ \
\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `'\ `' /
`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'`--'
*/
/// @notice Clanker Token Launcher
contract Clanker is OwnerAdmins, ReentrancyGuard, IClanker {
string constant version = "4";
uint256 public constant TOKEN_SUPPLY = 100_000_000_000e18; // 100b with 18 decimals
uint256 public constant BPS = 10_000;
uint256 public constant MAX_EXTENSIONS = 10;
uint16 public constant MAX_EXTENSION_BPS = 9000;
// if true, the factory will not allow supplied token deployments
bool public deprecated;
// receiver of the fees from the factory
address public teamFeeRecipient;
mapping(address token => DeploymentInfo deploymentInfo) public deploymentInfoForToken;
// enabled factory modules
mapping(address hook => bool enabled) enabledHooks;
mapping(address locker => mapping(address hook => bool enabled)) public enabledLockers;
mapping(address extension => bool enabled) enabledExtensions;
mapping(address mevModule => bool enabled) enabledMevModules;
constructor(address owner_) OwnerAdmins(owner_) {
// only non-originating tokens deployments are enabled
// before initialization
deprecated = true;
}
function setDeprecated(bool deprecated_) external onlyOwner {
deprecated = deprecated_;
emit SetDeprecated(deprecated_);
}
function setTeamFeeRecipient(address teamFeeRecipient_) external onlyOwner {
address oldTeamFeeRecipient = teamFeeRecipient;
teamFeeRecipient = teamFeeRecipient_;
emit SetTeamFeeRecipient(oldTeamFeeRecipient, teamFeeRecipient_);
}
function claimTeamFees(address token) external onlyOwnerOrAdmin {
if (teamFeeRecipient == address(0)) revert TeamFeeRecipientNotSet();
uint256 balance = IERC20(token).balanceOf(address(this));
SafeERC20.safeTransfer(IERC20(token), teamFeeRecipient, balance);
emit ClaimTeamFees(token, teamFeeRecipient, balance);
}
function tokenDeploymentInfo(address token) external view returns (DeploymentInfo memory) {
return deploymentInfoForToken[token];
}
function setHook(address hook, bool enabled) external onlyOwnerOrAdmin {
// check that the hook supports the IClankerHook interface
if (!IClankerHook(hook).supportsInterface(type(IClankerHook).interfaceId)) {
revert InvalidHook();
}
enabledHooks[hook] = enabled;
emit SetHook(hook, enabled);
}
function setLocker(address locker, address hook, bool enabled) external onlyOwnerOrAdmin {
// check that the locker supports the IClankerLpLocker interface
if (!IClankerLpLocker(locker).supportsInterface(type(IClankerLpLocker).interfaceId)) {
revert InvalidLocker();
}
enabledLockers[locker][hook] = enabled;
emit SetLocker(locker, hook, enabled);
}
function setMevModule(address mevModule, bool enabled) external onlyOwnerOrAdmin {
// check that the mev module supports the IClankerMevModule interface
if (!IClankerMevModule(mevModule).supportsInterface(type(IClankerMevModule).interfaceId)) {
revert InvalidMevModule();
}
enabledMevModules[mevModule] = enabled;
emit SetMevModule(mevModule, enabled);
}
// enable a extension contract for use, note the extension may implement its own access control
function setExtension(address extension, bool enabled) external onlyOwnerOrAdmin {
// check that the extension contract supports the IClankerExtension interface
if (!IClankerExtension(extension).supportsInterface(type(IClankerExtension).interfaceId)) {
revert InvalidExtension();
}
enabledExtensions[extension] = enabled;
emit SetExtension(extension, enabled);
}
// Deploy a token and pool with the option to vault the token and buy an initial amount
function deployToken(DeploymentConfig memory deploymentConfig)
public
payable
nonReentrant
returns (address tokenAddress)
{
if (deprecated) revert Deprecated();
if (block.chainid != deploymentConfig.tokenConfig.originatingChainId) {
revert OnlyOriginatingChain();
}
// deploy the token
tokenAddress = ClankerDeployer.deployToken(deploymentConfig.tokenConfig, TOKEN_SUPPLY);
// figure out the supply split
uint256 extensionsSupply = _prepareExtensions(deploymentConfig.extensionConfigs);
uint256 poolSupply = TOKEN_SUPPLY - extensionsSupply;
// configure the pool
PoolKey memory poolKey = _initializePool({
poolConfig: deploymentConfig.poolConfig,
locker: deploymentConfig.lockerConfig.locker,
mevModule: deploymentConfig.mevModuleConfig.mevModule,
newToken: tokenAddress
});
// have locker mint liquidity and inform the hook of the position
_initializeLiquidity(
deploymentConfig.lockerConfig,
deploymentConfig.poolConfig,
poolKey,
poolSupply,
tokenAddress
);
// trigger the extensions
_triggerExtensions(deploymentConfig, poolKey, tokenAddress);
// initialize the mev module
_initializeMevModule(deploymentConfig, poolKey);
// add the deployment info to the deployment info for token
address[] memory extensions = new address[](deploymentConfig.extensionConfigs.length);
for (uint256 i = 0; i < deploymentConfig.extensionConfigs.length; i++) {
extensions[i] = deploymentConfig.extensionConfigs[i].extension;
}
deploymentInfoForToken[tokenAddress] = DeploymentInfo({
locker: deploymentConfig.lockerConfig.locker,
token: tokenAddress,
hook: deploymentConfig.poolConfig.hook,
extensions: extensions
});
emit TokenCreated({
msgSender: msg.sender,
tokenAddress: tokenAddress,
tokenAdmin: deploymentConfig.tokenConfig.tokenAdmin,
tokenMetadata: deploymentConfig.tokenConfig.metadata,
tokenImage: deploymentConfig.tokenConfig.image,
tokenName: deploymentConfig.tokenConfig.name,
tokenSymbol: deploymentConfig.tokenConfig.symbol,
tokenContext: deploymentConfig.tokenConfig.context,
poolHook: deploymentConfig.poolConfig.hook,
poolId: poolKey.toId(),
startingTick: deploymentConfig.poolConfig.tickIfToken0IsClanker,
pairedToken: deploymentConfig.poolConfig.pairedToken,
locker: deploymentConfig.lockerConfig.locker,
mevModule: deploymentConfig.mevModuleConfig.mevModule,
extensionsSupply: extensionsSupply,
extensions: extensions
});
}
function _initializeMevModule(DeploymentConfig memory deploymentConfig, PoolKey memory poolKey)
internal
{
if (!enabledMevModules[deploymentConfig.mevModuleConfig.mevModule]) {
revert MevModuleNotEnabled();
}
// initialize the mev module
IClankerHook(deploymentConfig.poolConfig.hook).initializeMevModule(
poolKey, deploymentConfig.mevModuleConfig.mevModuleData
);
}
function _initializePool(
PoolConfig memory poolConfig,
address locker,
address mevModule,
address newToken
) internal returns (PoolKey memory poolKey) {
// check that the pool hook is enabled
if (!enabledHooks[poolConfig.hook]) {
revert HookNotEnabled();
}
// call into the hook to initialize the pool
poolKey = IClankerHook(poolConfig.hook).initializePool(
newToken,
poolConfig.pairedToken,
poolConfig.tickIfToken0IsClanker,
poolConfig.tickSpacing,
locker,
mevModule,
poolConfig.poolData
);
}
function _initializeLiquidity(
LockerConfig memory lockerConfig,
IClanker.PoolConfig memory poolConfig,
PoolKey memory poolKey,
uint256 poolSupply,
address token
) internal {
// check that the locker is enabled
if (!enabledLockers[lockerConfig.locker][poolConfig.hook]) {
revert LockerNotEnabled();
}
// approve the liquidity locker to take the pool's token supply
IERC20(token).approve(address(lockerConfig.locker), poolSupply);
// have the locker mint liquidity
IClankerLpLocker(lockerConfig.locker).placeLiquidity(
lockerConfig, poolConfig, poolKey, poolSupply, token
);
}
function _prepareExtensions(ExtensionConfig[] memory extensions)
internal
view
returns (uint256 extensionSupply)
{
if (extensions.length == 0) {
return 0;
}
// check for max number of extensions
if (extensions.length > MAX_EXTENSIONS) {
revert MaxExtensionsExceeded();
}
// determine total supply percentage earmarked for extensions
uint256 extensionSupplyPercentage = 0;
for (uint256 i = 0; i < extensions.length; i++) {
extensionSupplyPercentage += extensions[i].extensionBps;
}
// check that the extension supply percentage is less than the max extension bps
if (extensionSupplyPercentage > MAX_EXTENSION_BPS) {
revert MaxExtensionBpsExceeded();
}
// determine expected extension eth
uint256 expectedExtensionEth = 0;
for (uint256 i = 0; i < extensions.length; i++) {
expectedExtensionEth += extensions[i].msgValue;
}
// ensure the extension expected eth is equal to the msg.value
if (expectedExtensionEth != msg.value) {
revert ExtensionMsgValueMismatch();
}
// check that the extensions are enabled
for (uint256 i = 0; i < extensions.length; i++) {
if (!enabledExtensions[extensions[i].extension]) {
revert ExtensionNotEnabled();
}
}
// figure out the extension supply
extensionSupply = extensionSupplyPercentage * TOKEN_SUPPLY / BPS;
}
// send the tokens to the extension contract
function _triggerExtensions(
DeploymentConfig memory deploymentConfig,
PoolKey memory poolKey,
address token
) internal {
// iterate over the extensions and trigger each one
for (uint256 i = 0; i < deploymentConfig.extensionConfigs.length; i++) {
// determine the supply for the extension
uint256 extensionSupply =
deploymentConfig.extensionConfigs[i].extensionBps * TOKEN_SUPPLY / BPS;
// approve the extension contract to spend the token
IERC20(token).approve(deploymentConfig.extensionConfigs[i].extension, extensionSupply);
// trigger the extension
IClankerExtension(deploymentConfig.extensionConfigs[i].extension).receiveTokens{
value: deploymentConfig.extensionConfigs[i].msgValue
}(deploymentConfig, poolKey, token, extensionSupply, i);
emit ExtensionTriggered(
deploymentConfig.extensionConfigs[i].extension,
extensionSupply,
deploymentConfig.extensionConfigs[i].msgValue
);
}
}
}
Chain explorer2780msChain node92ms