{"version":3,"file":"index.cjs","sources":["../src/deploy.ts","../src/read.ts","../src/create2.ts"],"sourcesContent":["/**\n * @escapehub/token-creator - Token Deployment Functions\n *\n * Framework-agnostic token deployment using ethers.js v6.\n * No Angular dependencies - works with any JavaScript/TypeScript environment.\n *\n * @example React usage with wagmi\n * ```tsx\n * import { deployToken, normalizeWallet, createDefaultConfig } from '@escapehub/token-creator';\n * import { useWalletClient } from 'wagmi';\n *\n * function DeployButton() {\n *   const { data: walletClient } = useWalletClient();\n *\n *   const handleDeploy = async () => {\n *     const signer = await normalizeWallet(walletClient);\n *     const config = createDefaultConfig('My Token', 'MTK', '1000000', '0x...');\n *     const result = await deployToken(signer, config, { chainId: 1, factoryAddress: '0x...' });\n *     console.log('Deployed at:', result.tokenAddress);\n *   };\n *\n *   return <button onClick={handleDeploy}>Deploy Token</button>;\n * }\n * ```\n *\n * @example Vue usage with ethers\n * ```vue\n * <script setup>\n * import { deployToken, normalizeWallet, createDefaultConfig } from '@escapehub/token-creator';\n * import { BrowserProvider } from 'ethers';\n *\n * const deploy = async () => {\n *   const provider = new BrowserProvider(window.ethereum);\n *   const signer = await normalizeWallet({ provider: window.ethereum, account: '0x...' });\n *   const config = createDefaultConfig('My Token', 'MTK', '1000000', '0x...');\n *   const result = await deployToken(signer, config, { chainId: 1, factoryAddress: '0x...' });\n *   console.log('Deployed at:', result.tokenAddress);\n * };\n * </script>\n * ```\n *\n * @example Vanilla JS usage\n * ```javascript\n * import { deployToken, normalizeWallet, createDefaultConfig } from '@escapehub/token-creator';\n *\n * async function deploy() {\n *   const signer = await normalizeWallet(window.ethereum);\n *   const config = createDefaultConfig('My Token', 'MTK', '1000000', '0x...');\n *   const result = await deployToken(signer, config, { chainId: 1, factoryAddress: '0x...' });\n *   console.log('Deployed at:', result.tokenAddress);\n * }\n * ```\n */\n\nimport {\n  Contract,\n  Signer,\n  BrowserProvider,\n  JsonRpcProvider,\n  Provider,\n  keccak256,\n  getBytes,\n  getCreate2Address,\n  Interface,\n  TransactionReceipt,\n} from 'ethers';\nimport type {\n  TokenConfig,\n  Address,\n} from './types/deploy-config';\nimport { ZERO_ADDRESS } from './types/deploy-config';\nimport type { ChainConfig } from './types/token-data';\n\n/**\n * Result of a successful token deployment.\n */\nexport interface DeploymentResult {\n  /** Transaction hash of the deployment */\n  readonly txHash: `0x${string}`;\n  /** Deployed token contract address */\n  readonly tokenAddress: Address;\n}\n\n/**\n * Gas estimation for a token deployment.\n */\nexport interface GasEstimation {\n  /** Estimated gas limit */\n  readonly gasLimit: bigint;\n  /** Factory creation fee in wei */\n  readonly creationFee: bigint;\n}\n\n// ============================================================================\n// Factory ABI (Human-Readable Format)\n// ============================================================================\n\n/**\n * Human-readable ABI for LaunchERC20Factory contract.\n * Contains only the functions needed for token deployment.\n */\nexport const FACTORY_ABI = [\n  // createToken function\n  'function createToken((string name, string symbol, uint8 decimals, uint256 initialSupply, address finalOwner, bool mintingEnabled, uint256 hardCap, bool pausable, bool rescueEnabled, bool burnEnabled, bool metadataMutable, bool socialsMutable, bool feesModuleEnabled, bool limitsModuleEnabled, bool cooldownModuleEnabled, bool blacklistModuleEnabled, string website, string telegram, string xHandle, string discord, bool feesEnabled, uint16 maxBuyFeeBps, uint16 maxSellFeeBps, uint16 maxTransferFeeBps, uint16 buyFeeBps, uint16 sellFeeBps, uint16 transferFeeBps, uint16 feeBurnShareBps, address feeRecipient, uint256 maxWalletAmount, uint256 maxTxAmount, uint64 limitsEndTime, bool exemptRecipientsFromLimits, address[] additionalLimitExempt, uint32 maxCooldownSeconds, uint32 cooldownSeconds, address[] initialRecipients, uint256[] initialAmounts, string[] initialLabels, uint64 tradingSchedule, bool enableTradingImmediately, address router, bool routerProtectionEnabled, address[] initialBlacklist, bool freezeBlacklistAfterDeploy, bool renounceOwnership, bool useSalt, bytes32 salt, bool deadblocksEnabled, uint8 deadBlocks, uint8 deadblockMode, bool deadblockExemptLimitExempt, bool whitelistModuleEnabled, uint8 whitelistMode, bool whitelistAutoDisable, bool whitelistBypassesRouter, bool whitelistMutable, address[] initialWhitelist, bool freezeWhitelistAfterDeploy, bool antiDumpModuleEnabled, uint8 antiDumpType, uint256 maxAntiDumpLimit, uint64 maxAntiDumpPeriod, uint256 antiDumpLimit, uint64 antiDumpPeriod, uint64 antiDumpEndTime, uint8 batchTransferAccess, uint8 batchTransferFees, uint16 maxBatchSize, bool permitEnabled, bool freezeFeesAfterDeploy, bool freezeLimitsAfterDeploy, bool freezeCooldownAfterDeploy, bool freezeAntiDumpAfterDeploy, bool blockOwnershipTransferAfterDeploy) p) payable returns (address)',\n  // getCreationFee function\n  'function getCreationFee() view returns (uint256 fee)',\n  // predictAddress function\n  'function predictAddress(bytes32 salt) view returns (address)',\n  // implementation function\n  'function implementation() view returns (address)',\n  // TokenCreated event\n  'event TokenCreated(address indexed token, address indexed owner, bytes32 salt)',\n] as const;\n\n/**\n * Full JSON ABI for LaunchERC20Factory contract.\n * Use this if you need the full ABI structure for contract interactions.\n */\nexport const FACTORY_ABI_JSON = [\n  {\n    type: 'function',\n    name: 'createToken',\n    inputs: [\n      {\n        name: 'p',\n        type: 'tuple',\n        components: [\n          { name: 'name', type: 'string' },\n          { name: 'symbol', type: 'string' },\n          { name: 'decimals', type: 'uint8' },\n          { name: 'initialSupply', type: 'uint256' },\n          { name: 'finalOwner', type: 'address' },\n          { name: 'mintingEnabled', type: 'bool' },\n          { name: 'hardCap', type: 'uint256' },\n          { name: 'pausable', type: 'bool' },\n          { name: 'rescueEnabled', type: 'bool' },\n          { name: 'burnEnabled', type: 'bool' },\n          { name: 'metadataMutable', type: 'bool' },\n          { name: 'socialsMutable', type: 'bool' },\n          { name: 'feesModuleEnabled', type: 'bool' },\n          { name: 'limitsModuleEnabled', type: 'bool' },\n          { name: 'cooldownModuleEnabled', type: 'bool' },\n          { name: 'blacklistModuleEnabled', type: 'bool' },\n          { name: 'website', type: 'string' },\n          { name: 'telegram', type: 'string' },\n          { name: 'xHandle', type: 'string' },\n          { name: 'discord', type: 'string' },\n          { name: 'feesEnabled', type: 'bool' },\n          { name: 'maxBuyFeeBps', type: 'uint16' },\n          { name: 'maxSellFeeBps', type: 'uint16' },\n          { name: 'maxTransferFeeBps', type: 'uint16' },\n          { name: 'buyFeeBps', type: 'uint16' },\n          { name: 'sellFeeBps', type: 'uint16' },\n          { name: 'transferFeeBps', type: 'uint16' },\n          { name: 'feeBurnShareBps', type: 'uint16' },\n          { name: 'feeRecipient', type: 'address' },\n          { name: 'maxWalletAmount', type: 'uint256' },\n          { name: 'maxTxAmount', type: 'uint256' },\n          { name: 'limitsEndTime', type: 'uint64' },\n          { name: 'exemptRecipientsFromLimits', type: 'bool' },\n          { name: 'additionalLimitExempt', type: 'address[]' },\n          { name: 'maxCooldownSeconds', type: 'uint32' },\n          { name: 'cooldownSeconds', type: 'uint32' },\n          { name: 'initialRecipients', type: 'address[]' },\n          { name: 'initialAmounts', type: 'uint256[]' },\n          { name: 'initialLabels', type: 'string[]' },\n          { name: 'tradingSchedule', type: 'uint64' },\n          { name: 'enableTradingImmediately', type: 'bool' },\n          { name: 'router', type: 'address' },\n          { name: 'routerProtectionEnabled', type: 'bool' },\n          { name: 'initialBlacklist', type: 'address[]' },\n          { name: 'freezeBlacklistAfterDeploy', type: 'bool' },\n          { name: 'renounceOwnership', type: 'bool' },\n          { name: 'useSalt', type: 'bool' },\n          { name: 'salt', type: 'bytes32' },\n          { name: 'deadblocksEnabled', type: 'bool' },\n          { name: 'deadBlocks', type: 'uint8' },\n          { name: 'deadblockMode', type: 'uint8' },\n          { name: 'deadblockExemptLimitExempt', type: 'bool' },\n          { name: 'whitelistModuleEnabled', type: 'bool' },\n          { name: 'whitelistMode', type: 'uint8' },\n          { name: 'whitelistAutoDisable', type: 'bool' },\n          { name: 'whitelistBypassesRouter', type: 'bool' },\n          { name: 'whitelistMutable', type: 'bool' },\n          { name: 'initialWhitelist', type: 'address[]' },\n          { name: 'freezeWhitelistAfterDeploy', type: 'bool' },\n          { name: 'antiDumpModuleEnabled', type: 'bool' },\n          { name: 'antiDumpType', type: 'uint8' },\n          { name: 'maxAntiDumpLimit', type: 'uint256' },\n          { name: 'maxAntiDumpPeriod', type: 'uint64' },\n          { name: 'antiDumpLimit', type: 'uint256' },\n          { name: 'antiDumpPeriod', type: 'uint64' },\n          { name: 'antiDumpEndTime', type: 'uint64' },\n          { name: 'batchTransferAccess', type: 'uint8' },\n          { name: 'batchTransferFees', type: 'uint8' },\n          { name: 'maxBatchSize', type: 'uint16' },\n          { name: 'permitEnabled', type: 'bool' },\n          { name: 'freezeFeesAfterDeploy', type: 'bool' },\n          { name: 'freezeLimitsAfterDeploy', type: 'bool' },\n          { name: 'freezeCooldownAfterDeploy', type: 'bool' },\n          { name: 'freezeAntiDumpAfterDeploy', type: 'bool' },\n          { name: 'blockOwnershipTransferAfterDeploy', type: 'bool' },\n        ],\n      },\n    ],\n    outputs: [{ name: '', type: 'address' }],\n    stateMutability: 'payable',\n  },\n  {\n    type: 'function',\n    name: 'getCreationFee',\n    inputs: [],\n    outputs: [{ name: 'fee', type: 'uint256' }],\n    stateMutability: 'view',\n  },\n  {\n    type: 'function',\n    name: 'predictAddress',\n    inputs: [{ name: 'salt', type: 'bytes32' }],\n    outputs: [{ name: '', type: 'address' }],\n    stateMutability: 'view',\n  },\n  {\n    type: 'function',\n    name: 'implementation',\n    inputs: [],\n    outputs: [{ name: '', type: 'address' }],\n    stateMutability: 'view',\n  },\n  {\n    type: 'event',\n    name: 'TokenCreated',\n    inputs: [\n      { name: 'token', type: 'address', indexed: true },\n      { name: 'owner', type: 'address', indexed: true },\n      { name: 'salt', type: 'bytes32', indexed: false },\n    ],\n    anonymous: false,\n  },\n] as const;\n\n// ============================================================================\n// Wallet Input Types\n// ============================================================================\n\n/**\n * EIP-1193 Provider interface (window.ethereum, WalletConnect, etc.)\n */\nexport interface EIP1193Provider {\n  request: (args: { method: string; params?: unknown[] }) => Promise<unknown>;\n}\n\n/**\n * Wallet input for provider + account (used by Reown, wagmi, etc.)\n */\nexport interface WalletClientInput {\n  /** EIP-1193 compatible provider */\n  provider: EIP1193Provider;\n  /** Connected account address */\n  account: Address;\n}\n\n/**\n * Union type for all supported wallet inputs.\n *\n * Supported inputs:\n * - EIP1193Provider: window.ethereum, WalletConnect provider, etc.\n * - ethers.Signer: Already resolved ethers Signer\n * - WalletClientInput: Provider + account object (wagmi/viem style)\n *\n * @example Different wallet sources\n * ```typescript\n * // MetaMask/browser extension\n * const signer = await normalizeWallet(window.ethereum);\n *\n * // Already have an ethers Signer\n * const signer = await normalizeWallet(existingSigner);\n *\n * // wagmi/viem style wallet client\n * const signer = await normalizeWallet({ provider: walletClient, account: '0x...' });\n * ```\n */\nexport type WalletInput = EIP1193Provider | Signer | WalletClientInput;\n\n// ============================================================================\n// Legacy Chains (No EIP-1559)\n// ============================================================================\n\n/**\n * Chain IDs that don't support EIP-1559 (use legacy gas pricing).\n * These chains require { type: 0 } in transaction options.\n */\nexport const LEGACY_CHAIN_IDS = new Set([\n  56, // BSC Mainnet\n  97, // BSC Testnet\n  5000, // Mantle Mainnet\n  5003, // Mantle Sepolia\n  41455, // Monad Testnet\n  10143, // Monad Devnet\n  1088, // Metis Mainnet\n  59902, // Metis Sepolia\n  2222, // Kava Mainnet\n  2221, // Kava Testnet\n  40, // Telos Mainnet\n  41, // Telos Testnet\n]);\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Check if a chain uses legacy gas pricing (no EIP-1559).\n *\n * @param chainId - Chain ID to check\n * @returns True if chain requires legacy transactions\n */\nexport function isLegacyChain(chainId: number): boolean {\n  return LEGACY_CHAIN_IDS.has(chainId);\n}\n\n/**\n * Safely parse a string to BigInt, validating that it only contains digits.\n * Allows formatting characters (commas, underscores, spaces).\n *\n * @param value - The string to parse\n * @param fieldName - Name of the field (for error messages)\n * @returns BigInt value\n * @throws Error if the string contains invalid characters\n *\n * @example\n * ```typescript\n * safeParseBigInt('1,000,000', 'supply') // Returns 1000000n\n * safeParseBigInt('1_000_000', 'supply') // Returns 1000000n\n * safeParseBigInt('1 000 000', 'supply') // Returns 1000000n\n * ```\n */\nfunction safeParseBigInt(value: string, fieldName: string): bigint {\n  // Remove allowed formatting characters (commas, underscores, spaces)\n  const cleaned = value.replace(/[,_\\s]/g, '');\n\n  // Validate that the cleaned string only contains digits\n  if (!/^\\d+$/.test(cleaned)) {\n    throw new Error(\n      `Invalid ${fieldName}: \"${value}\" contains non-numeric characters. ` +\n        `Only digits and formatting characters (commas, spaces, underscores) are allowed.`,\n    );\n  }\n\n  try {\n    return BigInt(cleaned);\n  } catch (error) {\n    throw new Error(`Failed to parse ${fieldName}: ${error}`);\n  }\n}\n\n// ============================================================================\n// Wallet Normalization\n// ============================================================================\n\n/**\n * Type guard to check if input is an ethers Signer.\n */\nfunction isSigner(input: WalletInput): input is Signer {\n  return (\n    typeof input === 'object' &&\n    input !== null &&\n    'getAddress' in input &&\n    typeof (input as Signer).getAddress === 'function' &&\n    'signMessage' in input &&\n    typeof (input as Signer).signMessage === 'function'\n  );\n}\n\n/**\n * Type guard to check if input is an EIP-1193 Provider.\n */\nfunction isEIP1193Provider(input: WalletInput): input is EIP1193Provider {\n  return (\n    typeof input === 'object' &&\n    input !== null &&\n    'request' in input &&\n    typeof (input as EIP1193Provider).request === 'function'\n  );\n}\n\n/**\n * Type guard to check if input is a WalletClientInput.\n */\nfunction isWalletClientInput(input: WalletInput): input is WalletClientInput {\n  return (\n    typeof input === 'object' &&\n    input !== null &&\n    'provider' in input &&\n    'account' in input &&\n    typeof (input as WalletClientInput).account === 'string'\n  );\n}\n\n/**\n * Normalize any wallet input to an ethers Signer.\n *\n * Handles:\n * - EIP-1193 providers (window.ethereum, WalletConnect)\n * - ethers.Signer (pass-through)\n * - WalletClientInput (provider + account from wagmi/viem)\n *\n * @param wallet - Wallet input in any supported format\n * @returns ethers Signer ready for transactions\n * @throws Error if wallet type is not supported or connection fails\n *\n * @example React with wagmi\n * ```typescript\n * import { useWalletClient } from 'wagmi';\n * import { normalizeWallet } from '@escapehub/token-creator';\n *\n * const { data: walletClient } = useWalletClient();\n * const signer = await normalizeWallet(walletClient);\n * ```\n *\n * @example Vue with ethers\n * ```typescript\n * import { normalizeWallet } from '@escapehub/token-creator';\n *\n * const signer = await normalizeWallet(window.ethereum);\n * ```\n *\n * @example Vanilla JS\n * ```typescript\n * import { normalizeWallet } from '@escapehub/token-creator';\n *\n * // From MetaMask\n * const signer = await normalizeWallet(window.ethereum);\n *\n * // From existing signer (pass-through)\n * const signer = await normalizeWallet(existingSigner);\n * ```\n */\nexport async function normalizeWallet(wallet: WalletInput): Promise<Signer> {\n  // Already an ethers Signer - return as-is\n  if (isSigner(wallet)) {\n    return wallet;\n  }\n\n  // WalletClientInput (provider + account)\n  if (isWalletClientInput(wallet)) {\n    const provider = new BrowserProvider(wallet.provider as EIP1193Provider);\n    return provider.getSigner(wallet.account);\n  }\n\n  // EIP-1193 Provider (window.ethereum, etc.)\n  if (isEIP1193Provider(wallet)) {\n    const provider = new BrowserProvider(wallet);\n    // Request accounts if not already connected\n    await provider.send('eth_requestAccounts', []);\n    return provider.getSigner();\n  }\n\n  throw new Error(\n    'Unsupported wallet input. Expected EIP-1193 provider, ethers Signer, or { provider, account } object.',\n  );\n}\n\n// ============================================================================\n// Token Configuration Encoding\n// ============================================================================\n\n/**\n * Encode TokenConfig into the CreateParams tuple for the factory contract.\n *\n * This function converts the high-level TokenConfig into the exact format\n * expected by the LaunchERC20Factory.createToken() function.\n *\n * @param config - Token configuration\n * @param salt - Optional vanity salt (bytes32 hex string, defaults to zero bytes)\n * @returns CreateParams struct ready for contract call\n *\n * @example\n * ```typescript\n * import { encodeTokenConfig, createDefaultConfig } from '@escapehub/token-creator';\n *\n * const config = createDefaultConfig('My Token', 'MTK', '1000000', '0x...');\n * const params = encodeTokenConfig(config);\n * // params is ready for contract.createToken(params)\n * ```\n */\nexport function encodeTokenConfig(\n  config: TokenConfig,\n  salt: string = '',\n): Record<string, unknown> {\n  // Convert human-readable supply to smallest units\n  const factor = 10n ** BigInt(config.decimals);\n  const totalSupply = safeParseBigInt(config.totalSupplyHuman, 'totalSupplyHuman') * factor;\n\n  // Hard cap (also in smallest units)\n  const hardCap = config.hardCapHuman\n    ? safeParseBigInt(config.hardCapHuman, 'hardCapHuman') * factor\n    : 0n;\n\n  // Determine if using vanity salt\n  const useSalt = salt !== '0x' + '0'.repeat(64) && salt !== '';\n  const saltBytes = useSalt ? salt : '0x' + '0'.repeat(64);\n\n  // Fee recipient - must not be zero address if fees are enabled\n  let feeRecipient: Address = ZERO_ADDRESS;\n  if (config.feesModuleEnabled && config.feesEnabled) {\n    // Only set a non-zero recipient if burn share is not 100%\n    if ((config.feeBurnShareBps ?? 0) < 10000) {\n      feeRecipient =\n        config.feeRecipient && config.feeRecipient !== ZERO_ADDRESS\n          ? config.feeRecipient\n          : config.finalOwner;\n    }\n  }\n\n  return {\n    // Core ERC20\n    name: config.name,\n    symbol: config.symbol,\n    decimals: config.decimals,\n    initialSupply: totalSupply.toString(),\n    finalOwner: config.finalOwner,\n    // Core features\n    mintingEnabled: config.mintable ?? false,\n    hardCap: hardCap.toString(),\n    pausable: config.pausable ?? false,\n    rescueEnabled: config.rescueEnabled ?? false,\n    burnEnabled: config.burnEnabled ?? false,\n    metadataMutable: config.metadataMutable ?? false,\n    socialsMutable: config.socialsMutable ?? false,\n    // Optional modules\n    feesModuleEnabled: config.feesModuleEnabled ?? false,\n    limitsModuleEnabled: config.limitsModuleEnabled ?? false,\n    cooldownModuleEnabled: config.cooldownModuleEnabled ?? false,\n    blacklistModuleEnabled: config.blacklistModuleEnabled ?? false,\n    // Socials\n    website: config.website ?? '',\n    telegram: config.telegram ?? '',\n    xHandle: config.xHandle ?? '',\n    discord: config.discord ?? '',\n    // Fees config\n    feesEnabled: config.feesEnabled ?? false,\n    maxBuyFeeBps: config.maxBuyFeeBps ?? 0,\n    maxSellFeeBps: config.maxSellFeeBps ?? 0,\n    maxTransferFeeBps: config.maxTransferFeeBps ?? 0,\n    buyFeeBps: config.buyFeeBps ?? 0,\n    sellFeeBps: config.sellFeeBps ?? 0,\n    transferFeeBps: config.transferFeeBps ?? 0,\n    feeBurnShareBps: config.feeBurnShareBps ?? 0,\n    feeRecipient: feeRecipient,\n    // Limits config\n    maxWalletAmount: config.maxWalletAmount ?? '0',\n    maxTxAmount: config.maxTxAmount ?? '0',\n    limitsEndTime: config.limitsEndTime ?? 0,\n    exemptRecipientsFromLimits: config.exemptRecipientsFromLimits ?? false,\n    additionalLimitExempt: config.additionalLimitExempt ?? [],\n    // Cooldown config\n    maxCooldownSeconds: config.maxCooldownSeconds ?? 0,\n    cooldownSeconds: config.cooldownSeconds ?? 0,\n    // Initial distribution\n    initialRecipients: config.initialRecipients ?? [],\n    initialAmounts: config.initialAmounts ?? [],\n    initialLabels: config.initialLabels ?? [],\n    // Trading schedule\n    tradingSchedule: config.tradingSchedule ?? 0,\n    // Trading enable immediately\n    enableTradingImmediately: config.enableTradingImmediately ?? false,\n    // Router protection\n    router: config.router ?? ZERO_ADDRESS,\n    routerProtectionEnabled: config.routerProtectionEnabled ?? false,\n    // Blacklist\n    initialBlacklist: config.initialBlacklist ?? [],\n    freezeBlacklistAfterDeploy: config.freezeBlacklistAfterDeploy ?? false,\n    // Ownership\n    renounceOwnership: config.renounceOwnership ?? false,\n    // Vanity deployment\n    useSalt: useSalt,\n    salt: saltBytes,\n    // Deadblock config\n    deadblocksEnabled: config.deadblocksEnabled ?? false,\n    deadBlocks: config.deadBlocks ?? 0,\n    deadblockMode: config.deadblockMode ?? 0,\n    deadblockExemptLimitExempt: config.deadblockExemptLimitExempt ?? false,\n    // Whitelist config\n    whitelistModuleEnabled: config.whitelistModuleEnabled ?? false,\n    whitelistMode: config.whitelistMode ?? 0,\n    whitelistAutoDisable: config.whitelistAutoDisable ?? false,\n    whitelistBypassesRouter: config.whitelistBypassesRouter ?? false,\n    whitelistMutable: config.whitelistMutable ?? false,\n    initialWhitelist: config.initialWhitelist ?? [],\n    freezeWhitelistAfterDeploy: config.freezeWhitelistAfterDeploy ?? false,\n    // Anti-dump config\n    antiDumpModuleEnabled: config.antiDumpModuleEnabled ?? false,\n    antiDumpType: config.antiDumpType ?? 0,\n    maxAntiDumpLimit: config.maxAntiDumpLimit ?? '0',\n    maxAntiDumpPeriod: config.maxAntiDumpPeriod ?? 0,\n    antiDumpLimit: config.antiDumpLimit ?? '0',\n    antiDumpPeriod: config.antiDumpPeriod ?? 0,\n    antiDumpEndTime: config.antiDumpEndTime ?? 0,\n    // Batch transfer config\n    batchTransferAccess: config.batchTransferAccess ?? 0,\n    batchTransferFees: config.batchTransferFees ?? 0,\n    maxBatchSize: config.maxBatchSize ?? 100,\n    // Permit config\n    permitEnabled: config.permitEnabled ?? true,\n    // Freeze after deploy config\n    freezeFeesAfterDeploy: config.freezeFeesAfterDeploy ?? false,\n    freezeLimitsAfterDeploy: config.freezeLimitsAfterDeploy ?? false,\n    freezeCooldownAfterDeploy: config.freezeCooldownAfterDeploy ?? false,\n    freezeAntiDumpAfterDeploy: config.freezeAntiDumpAfterDeploy ?? false,\n    // Ownership transfer blocking\n    blockOwnershipTransferAfterDeploy: config.blockOwnershipTransferAfterDeploy ?? false,\n  };\n}\n\n// ============================================================================\n// Creation Fee\n// ============================================================================\n\n/**\n * Get the creation fee required by the factory contract.\n *\n * The factory charges a small fee (~$1 in native tokens) for each token deployment.\n * This fee is sent as msg.value when calling createToken().\n *\n * @param provider - ethers Provider or Signer with provider\n * @param factoryAddress - Factory contract address\n * @returns Creation fee in wei (native token smallest unit)\n *\n * @example\n * ```typescript\n * import { getCreationFee } from '@escapehub/token-creator';\n * import { JsonRpcProvider, formatEther } from 'ethers';\n *\n * const provider = new JsonRpcProvider('https://rpc.sepolia.org');\n * const fee = await getCreationFee(provider, '0x...');\n * console.log('Fee:', formatEther(fee), 'ETH');\n * ```\n */\nexport async function getCreationFee(\n  provider: Provider | Signer,\n  factoryAddress: Address,\n): Promise<bigint> {\n  const contract = new Contract(factoryAddress, FACTORY_ABI, provider);\n  return await contract.getCreationFee();\n}\n\n// ============================================================================\n// Gas Estimation\n// ============================================================================\n\n/**\n * Estimate gas for token deployment.\n *\n * Returns both the estimated gas limit and the creation fee required.\n * Use this to display estimated costs to users before deployment.\n *\n * @param provider - ethers Provider or Signer (must have a provider)\n * @param factoryAddress - Factory contract address\n * @param config - Token configuration\n * @param salt - Optional vanity salt (bytes32 hex string)\n * @returns Gas estimation including factory creation fee\n *\n * @example\n * ```typescript\n * import { estimateGas, createDefaultConfig } from '@escapehub/token-creator';\n * import { formatEther, formatUnits } from 'ethers';\n *\n * const config = createDefaultConfig('My Token', 'MTK', '1000000', '0x...');\n * const estimation = await estimateGas(provider, '0x...', config);\n *\n * console.log('Gas limit:', estimation.gasLimit.toString());\n * console.log('Creation fee:', formatEther(estimation.creationFee), 'ETH');\n * ```\n */\nexport async function estimateGas(\n  provider: Provider | Signer,\n  factoryAddress: Address,\n  config: TokenConfig,\n  salt: string = '',\n): Promise<GasEstimation> {\n  const contract = new Contract(factoryAddress, FACTORY_ABI, provider);\n\n  // Build CreateParams struct\n  const createParams = encodeTokenConfig(config, salt);\n\n  // Get creation fee\n  let creationFee = 0n;\n  try {\n    creationFee = await contract.getCreationFee();\n  } catch {\n    // Assume free if cannot fetch\n  }\n\n  // Estimate gas\n  const gasLimit = await contract.createToken.estimateGas(createParams, {\n    value: creationFee,\n  });\n\n  return { gasLimit, creationFee };\n}\n\n// ============================================================================\n// Token Deployment\n// ============================================================================\n\n/**\n * Deploy a new token using the LaunchERC20Factory.\n *\n * This is the main deployment function. It:\n * 1. Encodes the token configuration\n * 2. Fetches the creation fee\n * 3. Sends the createToken transaction\n * 4. Waits for confirmation\n * 5. Extracts the token address from events\n *\n * Handles legacy chains (no EIP-1559) automatically.\n *\n * @param wallet - Wallet input (will be normalized to Signer)\n * @param config - Token configuration\n * @param chainConfig - Chain configuration with factory address\n * @param salt - Optional vanity salt (bytes32 hex string)\n * @returns Deployment result with transaction hash and token address\n *\n * @example React with wagmi\n * ```tsx\n * import { deployToken, createDefaultConfig } from '@escapehub/token-creator';\n * import { useWalletClient } from 'wagmi';\n *\n * function DeployButton() {\n *   const { data: walletClient } = useWalletClient();\n *\n *   const handleDeploy = async () => {\n *     const config = createDefaultConfig(\n *       'My Token',\n *       'MTK',\n *       '1000000',\n *       await walletClient.account.address\n *     );\n *\n *     const result = await deployToken(\n *       walletClient,\n *       config,\n *       { chainId: 11155111, factoryAddress: '0x...' }\n *     );\n *\n *     console.log('Token deployed at:', result.tokenAddress);\n *     console.log('Transaction:', result.txHash);\n *   };\n *\n *   return <button onClick={handleDeploy}>Deploy Token</button>;\n * }\n * ```\n *\n * @example Vue with ethers\n * ```vue\n * <script setup>\n * import { deployToken, normalizeWallet, createDefaultConfig } from '@escapehub/token-creator';\n *\n * const deploy = async () => {\n *   const signer = await normalizeWallet(window.ethereum);\n *   const address = await signer.getAddress();\n *\n *   const config = createDefaultConfig('My Token', 'MTK', '1000000', address);\n *\n *   const result = await deployToken(\n *     signer,\n *     config,\n *     { chainId: 11155111, factoryAddress: '0x...' }\n *   );\n *\n *   console.log('Token deployed at:', result.tokenAddress);\n * };\n * </script>\n * ```\n *\n * @example Vanilla JS\n * ```javascript\n * import { deployToken, normalizeWallet, createDefaultConfig } from '@escapehub/token-creator';\n *\n * async function deploy() {\n *   const signer = await normalizeWallet(window.ethereum);\n *   const address = await signer.getAddress();\n *\n *   const config = createDefaultConfig('My Token', 'MTK', '1000000', address);\n *\n *   const result = await deployToken(\n *     signer,\n *     config,\n *     { chainId: 11155111, factoryAddress: '0x...' }\n *   );\n *\n *   console.log('Token deployed at:', result.tokenAddress);\n * }\n * ```\n */\nexport async function deployToken(\n  wallet: WalletInput,\n  config: TokenConfig,\n  chainConfig: ChainConfig,\n  salt: string = '',\n): Promise<DeploymentResult> {\n  if (!chainConfig.factoryAddress) {\n    throw new Error('Missing factory address in chain configuration');\n  }\n\n  // Normalize wallet to ethers Signer\n  const signer = await normalizeWallet(wallet);\n  const contract = new Contract(chainConfig.factoryAddress, FACTORY_ABI, signer);\n\n  // Build CreateParams struct (finalOwner is in config)\n  const createParams = encodeTokenConfig(config, salt);\n\n  // Get required creation fee from factory\n  let creationFee = 0n;\n  try {\n    creationFee = await contract.getCreationFee();\n  } catch (e) {\n    console.warn('Could not fetch creation fee, assuming free:', e);\n  }\n\n  // Build transaction options\n  const txOptions: { value: bigint; type?: number } = { value: creationFee };\n\n  // Use legacy transaction type for chains without EIP-1559\n  if (isLegacyChain(chainConfig.chainId)) {\n    txOptions.type = 0;\n  }\n\n  // Send transaction with fee as msg.value\n  const tx = await contract.createToken(createParams, txOptions);\n  const receipt = await tx.wait();\n\n  // Extract token address from TokenCreated event\n  const tokenAddress = parseTokenCreatedEvent(receipt, chainConfig.factoryAddress as Address);\n\n  if (!tokenAddress) {\n    throw new Error('Token created but could not extract address from event');\n  }\n\n  return { txHash: receipt.hash as `0x${string}`, tokenAddress };\n}\n\n// ============================================================================\n// Address Prediction\n// ============================================================================\n\n/**\n * Predict the token address that would be created with a given salt.\n *\n * Uses the factory's predictAddress function via RPC call.\n * For local prediction without RPC, use predictAddressLocal().\n *\n * @param provider - ethers Provider or Signer\n * @param factoryAddress - Factory contract address\n * @param salt - Vanity salt (bytes32 hex string)\n * @returns Predicted token address\n *\n * @example\n * ```typescript\n * import { predictAddress } from '@escapehub/token-creator';\n *\n * const address = await predictAddress(\n *   provider,\n *   '0x10ca27bc0c4f9c1ea9169a76270c0c136d160a54',\n *   '0x0000000000000000000000000000000000000000000000000000000000000001'\n * );\n * console.log('Token will be deployed at:', address);\n * ```\n */\nexport async function predictAddress(\n  provider: Provider | Signer,\n  factoryAddress: Address,\n  salt: string,\n): Promise<Address> {\n  const contract = new Contract(factoryAddress, FACTORY_ABI, provider);\n  return (await contract.predictAddress(salt)) as Address;\n}\n\n/**\n * Compute the init code hash for OpenZeppelin Clones minimal proxy.\n * Used for local CREATE2 address prediction without RPC calls.\n *\n * @param implementationAddress - The implementation contract address\n * @returns The init code hash for CREATE2 address prediction\n *\n * @example\n * ```typescript\n * import { getMinimalProxyInitCodeHash } from '@escapehub/token-creator';\n *\n * const initCodeHash = getMinimalProxyInitCodeHash('0x...');\n * // Can be used with predictAddressLocal\n * ```\n */\nexport function getMinimalProxyInitCodeHash(implementationAddress: Address): string {\n  // Normalize implementation address (remove 0x, lowercase)\n  const impl = implementationAddress.toLowerCase().replace('0x', '');\n\n  if (impl.length !== 40) {\n    throw new Error('Invalid implementation address');\n  }\n\n  // OpenZeppelin Clones minimal proxy bytecode:\n  // prefix (10 bytes) + implementation (20 bytes) + suffix (15 bytes) = 45 bytes\n  const prefix = '3d602d80600a3d3981f3363d3d373d3d3d363d73';\n  const suffix = '5af43d82803e903d91602b57fd5bf3';\n\n  const initCode = '0x' + prefix + impl + suffix;\n  const initCodeHash = keccak256(getBytes(initCode));\n\n  return initCodeHash;\n}\n\n/**\n * Predict token address locally using CREATE2 formula.\n * Does not require RPC call - useful for vanity address mining.\n *\n * @param factoryAddress - Factory contract address\n * @param salt - Vanity salt (bytes32 hex string)\n * @param initCodeHash - Init code hash (from getMinimalProxyInitCodeHash)\n * @returns Predicted token address\n *\n * @example\n * ```typescript\n * import { predictAddressLocal, getMinimalProxyInitCodeHash } from '@escapehub/token-creator';\n *\n * const initCodeHash = getMinimalProxyInitCodeHash(implementationAddress);\n * const address = predictAddressLocal(\n *   '0x10ca27bc0c4f9c1ea9169a76270c0c136d160a54',\n *   '0x0000000000000000000000000000000000000000000000000000000000000001',\n *   initCodeHash\n * );\n * ```\n */\nexport function predictAddressLocal(\n  factoryAddress: Address,\n  salt: string,\n  initCodeHash: string,\n): Address {\n  return getCreate2Address(factoryAddress, salt, initCodeHash) as Address;\n}\n\n// ============================================================================\n// Transaction Waiting\n// ============================================================================\n\n/**\n * Wait for a deployment transaction to be confirmed.\n *\n * @param provider - ethers Provider or Signer with provider\n * @param txHash - Transaction hash to wait for\n * @param confirmations - Number of confirmations to wait for (default: 1)\n * @returns Transaction receipt\n * @throws Error if transaction fails or times out\n *\n * @example\n * ```typescript\n * import { waitForDeployment } from '@escapehub/token-creator';\n *\n * // Wait for 1 confirmation (default)\n * const receipt = await waitForDeployment(provider, txHash);\n *\n * // Wait for 3 confirmations\n * const receipt = await waitForDeployment(provider, txHash, 3);\n * ```\n */\nexport async function waitForDeployment(\n  provider: Provider | Signer,\n  txHash: string,\n  confirmations: number = 1,\n): Promise<TransactionReceipt> {\n  // Get provider from signer if needed\n  let actualProvider: Provider;\n  if ('provider' in provider && provider.provider) {\n    actualProvider = provider.provider;\n  } else if ('getTransaction' in provider) {\n    actualProvider = provider as Provider;\n  } else {\n    throw new Error('Provider or Signer with provider required');\n  }\n\n  const receipt = await actualProvider.waitForTransaction(txHash, confirmations);\n\n  if (!receipt) {\n    throw new Error(`Transaction ${txHash} not found`);\n  }\n\n  if (receipt.status === 0) {\n    throw new Error(`Transaction ${txHash} failed`);\n  }\n\n  return receipt;\n}\n\n// ============================================================================\n// Event Parsing\n// ============================================================================\n\n/**\n * Parse TokenCreated event from a transaction receipt.\n *\n * Extracts the deployed token address from the factory's TokenCreated event.\n *\n * @param receipt - Transaction receipt from deployment\n * @param factoryAddress - Factory contract address (for event filtering)\n * @returns Token address if found, null otherwise\n *\n * @example\n * ```typescript\n * import { parseTokenCreatedEvent } from '@escapehub/token-creator';\n *\n * const receipt = await provider.waitForTransaction(txHash);\n * const tokenAddress = parseTokenCreatedEvent(receipt, factoryAddress);\n *\n * if (tokenAddress) {\n *   console.log('Token deployed at:', tokenAddress);\n * } else {\n *   console.log('TokenCreated event not found in receipt');\n * }\n * ```\n */\nexport function parseTokenCreatedEvent(\n  receipt: TransactionReceipt,\n  factoryAddress: Address,\n): Address | null {\n  const iface = new Interface(FACTORY_ABI);\n\n  for (const log of receipt.logs) {\n    // Filter by factory address (optional but recommended)\n    if (log.address.toLowerCase() !== factoryAddress.toLowerCase()) {\n      continue;\n    }\n\n    try {\n      const parsed = iface.parseLog({\n        topics: log.topics as string[],\n        data: log.data,\n      });\n\n      if (parsed?.name === 'TokenCreated') {\n        return parsed.args.token as Address;\n      }\n    } catch {\n      // Not our event, skip\n    }\n  }\n\n  return null;\n}\n\n// ============================================================================\n// Additional Utilities\n// ============================================================================\n\n/**\n * Get the implementation address used by the factory.\n *\n * The factory uses OpenZeppelin Clones to deploy minimal proxies\n * pointing to this implementation.\n *\n * @param provider - ethers Provider or Signer\n * @param factoryAddress - Factory contract address\n * @returns Implementation contract address\n *\n * @example\n * ```typescript\n * import { getImplementation } from '@escapehub/token-creator';\n *\n * const impl = await getImplementation(provider, '0x...');\n * console.log('Implementation:', impl);\n * ```\n */\nexport async function getImplementation(\n  provider: Provider | Signer,\n  factoryAddress: Address,\n): Promise<Address> {\n  const contract = new Contract(factoryAddress, FACTORY_ABI, provider);\n  return (await contract.implementation()) as Address;\n}\n\n/**\n * Check if an address already has deployed code.\n * Returns true if address has code (deployment would fail).\n *\n * @param provider - ethers Provider or Signer with provider\n * @param address - Address to check\n * @returns True if address has code\n *\n * @example\n * ```typescript\n * import { addressHasCode } from '@escapehub/token-creator';\n *\n * const hasCode = await addressHasCode(provider, '0x...');\n * if (hasCode) {\n *   console.log('Address already has a contract!');\n * }\n * ```\n */\nexport async function addressHasCode(\n  provider: Provider | Signer,\n  address: Address,\n): Promise<boolean> {\n  // Get provider from signer if needed\n  let actualProvider: Provider;\n  if ('provider' in provider && provider.provider) {\n    actualProvider = provider.provider;\n  } else if ('getCode' in provider) {\n    actualProvider = provider as Provider;\n  } else {\n    throw new Error('Provider or Signer with provider required');\n  }\n\n  const code = await actualProvider.getCode(address);\n  return code !== '0x' && code !== '0x0' && code.length > 2;\n}\n\n/**\n * Verify that an address is available for deployment.\n * Throws an error if the address already has code.\n *\n * @param provider - ethers Provider or Signer\n * @param factoryAddress - Factory contract address\n * @param salt - Vanity salt (bytes32 hex string)\n * @returns Predicted address if available\n * @throws Error if address already has code\n *\n * @example\n * ```typescript\n * import { verifyAddressAvailable } from '@escapehub/token-creator';\n *\n * try {\n *   const address = await verifyAddressAvailable(provider, factoryAddress, salt);\n *   console.log('Address available:', address);\n * } catch (e) {\n *   console.log('Address already taken!');\n * }\n * ```\n */\nexport async function verifyAddressAvailable(\n  provider: Provider | Signer,\n  factoryAddress: Address,\n  salt: string,\n): Promise<Address> {\n  const predicted = await predictAddress(provider, factoryAddress, salt);\n  const hasCode = await addressHasCode(provider, predicted);\n\n  if (hasCode) {\n    throw new Error(\n      `Address ${predicted} already has a deployed contract. ` +\n        `Please use a different vanity pattern or disable vanity address.`,\n    );\n  }\n\n  return predicted;\n}\n\n/**\n * Create a read-only provider for a chain.\n * Useful when you only need to read data without a wallet.\n *\n * @param rpcUrl - RPC endpoint URL\n * @returns JsonRpcProvider instance\n *\n * @example\n * ```typescript\n * import { createProvider, getCreationFee } from '@escapehub/token-creator';\n *\n * const provider = createProvider('https://rpc.sepolia.org');\n * const fee = await getCreationFee(provider, '0x...');\n * ```\n */\nexport function createProvider(rpcUrl: string): JsonRpcProvider {\n  return new JsonRpcProvider(rpcUrl);\n}\n\n// ============================================================================\n// Re-exports\n// ============================================================================\n\nexport type {\n  TokenConfig,\n  Address,\n} from './types/deploy-config';\n\nexport type { ChainConfig } from './types/token-data';\n\nexport {\n  ZERO_ADDRESS,\n  DeadblockMode,\n  WhitelistMode,\n  AntiDumpType,\n  BatchTransferAccess,\n  BatchTransferFees,\n  createDefaultConfig,\n} from './types/deploy-config';\n","/**\n * @escapehub/token-creator - Token Data Reader\n *\n * Framework-agnostic pure functions for reading token data from the blockchain.\n * Supports both Multicall3 (batched) and individual RPC calls.\n *\n * @module read\n *\n * @example\n * ```typescript\n * import { readTokenData, createProvider, STANDARD_MULTICALL3_ADDRESS } from '@escapehub/token-creator';\n *\n * const provider = createProvider({ rpcUrl: 'https://rpc.sepolia.org', chainId: 11155111 });\n * const tokenData = await readTokenData(\n *   provider,\n *   '0x1234...5678',\n *   11155111,\n *   { multicall3Address: STANDARD_MULTICALL3_ADDRESS }\n * );\n * console.log(tokenData.name, tokenData.symbol);\n * ```\n */\n\nimport { ethers, JsonRpcProvider, Interface, Contract } from 'ethers';\nimport type {\n  TokenDataFlat,\n  DistributionEntryDetailed,\n  ReadConfig,\n  TokenIdentifier,\n} from './types/token-data';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * Standard Multicall3 address deployed on most EVM chains.\n * Verified at https://github.com/mds1/multicall\n *\n * @example\n * ```typescript\n * import { checkMulticall3Available, STANDARD_MULTICALL3_ADDRESS } from '@escapehub/token-creator';\n *\n * const available = await checkMulticall3Available(provider, STANDARD_MULTICALL3_ADDRESS);\n * ```\n */\nexport const STANDARD_MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';\n\n/**\n * Multicall3 ABI - only the aggregate3 function needed for batched reads.\n * This is the minimal ABI required for efficient multi-call operations.\n */\nexport const MULTICALL3_ABI = [\n  {\n    inputs: [\n      {\n        components: [\n          { name: 'target', type: 'address' },\n          { name: 'allowFailure', type: 'bool' },\n          { name: 'callData', type: 'bytes' },\n        ],\n        name: 'calls',\n        type: 'tuple[]',\n      },\n    ],\n    name: 'aggregate3',\n    outputs: [\n      {\n        components: [\n          { name: 'success', type: 'bool' },\n          { name: 'returnData', type: 'bytes' },\n        ],\n        name: 'returnData',\n        type: 'tuple[]',\n      },\n    ],\n    stateMutability: 'payable',\n    type: 'function',\n  },\n] as const;\n\n/**\n * Human-readable ABI strings for all token read functions.\n * Uses ethers v6 human-readable ABI format for clarity.\n */\nexport const TOKEN_READ_ABI = [\n  // ERC20 Standard\n  'function name() view returns (string)',\n  'function symbol() view returns (string)',\n  'function decimals() view returns (uint8)',\n  'function totalSupply() view returns (uint256)',\n\n  // Ownership\n  'function owner() view returns (address)',\n\n  // Metadata\n  'function metadataMutable() view returns (bool)',\n  'function socialsMutable() view returns (bool)',\n  'function website() view returns (string)',\n  'function telegram() view returns (string)',\n  'function xHandle() view returns (string)',\n  'function discord() view returns (string)',\n\n  // Core Features\n  'function mintingEnabled() view returns (bool)',\n  'function pausable() view returns (bool)',\n  'function rescueEnabled() view returns (bool)',\n  'function burnEnabled() view returns (bool)',\n  'function hardCap() view returns (uint256)',\n\n  // Module Flags\n  'function feesModuleEnabled() view returns (bool)',\n  'function limitsModuleEnabled() view returns (bool)',\n  'function cooldownModuleEnabled() view returns (bool)',\n  'function blacklistModuleEnabled() view returns (bool)',\n  'function whitelistModuleEnabled() view returns (bool)',\n  'function antiDumpModuleEnabled() view returns (bool)',\n  'function deadblocksEnabled() view returns (bool)',\n\n  // Fee Caps\n  'function maxBuyFeeBps() view returns (uint16)',\n  'function maxSellFeeBps() view returns (uint16)',\n  'function maxTransferFeeBps() view returns (uint16)',\n  'function maxCooldownSeconds() view returns (uint32)',\n\n  // Current Fees\n  'function feesEnabled() view returns (bool)',\n  'function buyFeeBps() view returns (uint16)',\n  'function sellFeeBps() view returns (uint16)',\n  'function transferFeeBps() view returns (uint16)',\n  'function feeRecipient() view returns (address)',\n  'function feeBurnShareBps() view returns (uint16)',\n\n  // Limits\n  'function maxWalletAmount() view returns (uint256)',\n  'function maxTxAmount() view returns (uint256)',\n  'function limitsEndTime() view returns (uint48)',\n\n  // Cooldown\n  'function cooldownSeconds() view returns (uint32)',\n\n  // Trading\n  'function tradingEnabled() view returns (bool)',\n  'function tradingEnabledAt() view returns (uint48)',\n  'function router() view returns (address)',\n  'function routerProtectionEnabled() view returns (bool)',\n\n  // Blacklist\n  'function blacklistFrozen() view returns (bool)',\n\n  // Whitelist\n  'function whitelistMode() view returns (uint8)',\n  'function whitelistAutoDisable() view returns (bool)',\n  'function whitelistMutable() view returns (bool)',\n  'function whitelistActive() view returns (bool)',\n\n  // Anti-Dump\n  'function antiDumpType() view returns (uint8)',\n  'function antiDumpLimit() view returns (uint256)',\n  'function antiDumpPeriod() view returns (uint32)',\n  'function antiDumpEndTime() view returns (uint48)',\n\n  // Deadblock\n  'function deadBlocks() view returns (uint8)',\n  'function deadblockMode() view returns (uint8)',\n\n  // Batch Transfer\n  'function batchTransferAccess() view returns (uint8)',\n  'function batchTransferFees() view returns (uint8)',\n  'function maxBatchSize() view returns (uint16)',\n\n  // State\n  'function paused() view returns (bool)',\n  'function permitEnabled() view returns (bool)',\n\n  // Freeze States\n  'function feesFrozen() view returns (bool)',\n  'function limitsFrozen() view returns (bool)',\n  'function cooldownFrozen() view returns (bool)',\n  'function antiDumpFrozen() view returns (bool)',\n  'function ownershipTransferBlocked() view returns (bool)',\n] as const;\n\n/**\n * Factory ABI for reading InitialDistribution events.\n */\nexport const FACTORY_EVENT_ABI = [\n  'event InitialDistribution(address indexed token, address[] recipients, uint256[] amounts, string[] labels)',\n] as const;\n\n/**\n * Ordered list of token function names to call.\n * The order MUST match the destructuring in buildTokenData.\n */\nexport const TOKEN_FUNCTION_NAMES = [\n  'name',\n  'symbol',\n  'decimals',\n  'totalSupply',\n  'owner',\n  'metadataMutable',\n  'socialsMutable',\n  'website',\n  'telegram',\n  'xHandle',\n  'discord',\n  'mintingEnabled',\n  'pausable',\n  'rescueEnabled',\n  'burnEnabled',\n  'hardCap',\n  'feesModuleEnabled',\n  'limitsModuleEnabled',\n  'cooldownModuleEnabled',\n  'blacklistModuleEnabled',\n  'maxBuyFeeBps',\n  'maxSellFeeBps',\n  'maxTransferFeeBps',\n  'maxCooldownSeconds',\n  'deadblocksEnabled',\n  'deadBlocks',\n  'deadblockMode',\n  'whitelistModuleEnabled',\n  'whitelistMode',\n  'whitelistAutoDisable',\n  'whitelistMutable',\n  'whitelistActive',\n  'antiDumpModuleEnabled',\n  'antiDumpType',\n  'antiDumpLimit',\n  'antiDumpPeriod',\n  'antiDumpEndTime',\n  'batchTransferAccess',\n  'batchTransferFees',\n  'maxBatchSize',\n  'feesEnabled',\n  'buyFeeBps',\n  'sellFeeBps',\n  'transferFeeBps',\n  'feeRecipient',\n  'feeBurnShareBps',\n  'tradingEnabled',\n  'tradingEnabledAt',\n  'router',\n  'routerProtectionEnabled',\n  'maxWalletAmount',\n  'maxTxAmount',\n  'limitsEndTime',\n  'cooldownSeconds',\n  'blacklistFrozen',\n  'paused',\n  'permitEnabled',\n  'feesFrozen',\n  'limitsFrozen',\n  'cooldownFrozen',\n  'antiDumpFrozen',\n  'ownershipTransferBlocked',\n] as const;\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Format a token amount to human-readable string.\n *\n * @param amount - Amount in smallest units (wei)\n * @param decimals - Token decimals\n * @returns Formatted string representation\n *\n * @example\n * ```typescript\n * formatAmount(1000000000000000000n, 18); // \"1.0\"\n * formatAmount(0n, 18); // \"0\"\n * ```\n */\nexport function formatAmount(amount: bigint, decimals: number): string {\n  if (amount === 0n) return '0';\n  return ethers.formatUnits(amount, decimals);\n}\n\n/**\n * Calculate percentage of total supply.\n *\n * @param amount - Amount in smallest units\n * @param totalSupply - Total supply in smallest units\n * @returns Percentage with 2 decimal precision (e.g., 5.25 for 5.25%)\n *\n * @example\n * ```typescript\n * calcPercent(1000n, 10000n); // 10.0\n * calcPercent(525n, 10000n);  // 5.25\n * ```\n */\nexport function calcPercent(amount: bigint, totalSupply: bigint): number {\n  if (totalSupply === 0n || amount === 0n) return 0;\n  return Number((amount * 10000n) / totalSupply) / 100;\n}\n\n/**\n * Build TokenData object from decoded multicall/individual call results.\n *\n * @param tokenAddress - Token contract address\n * @param decoded - Array of decoded values in order of TOKEN_FUNCTION_NAMES\n * @param decimals - Token decimals for formatting\n * @returns Complete TokenDataFlat object\n *\n * @example\n * ```typescript\n * const decoded = [...]; // From multicall or individual calls\n * const tokenData = buildTokenData('0x1234...', decoded, 18);\n * ```\n */\nexport function buildTokenData(\n  tokenAddress: string,\n  decoded: unknown[],\n  decimals: number\n): TokenDataFlat {\n  // Destructure in same order as TOKEN_FUNCTION_NAMES\n  const [\n    name,\n    symbol,\n    _decimals,\n    totalSupply,\n    owner,\n    metadataMutable,\n    socialsMutable,\n    website,\n    telegram,\n    xHandle,\n    discord,\n    mintingEnabled,\n    pausable,\n    rescueEnabled,\n    burnEnabled,\n    hardCap,\n    feesModuleEnabled,\n    limitsModuleEnabled,\n    cooldownModuleEnabled,\n    blacklistModuleEnabled,\n    maxBuyFeeBps,\n    maxSellFeeBps,\n    maxTransferFeeBps,\n    maxCooldownSeconds,\n    deadblocksEnabled,\n    deadBlocks,\n    deadblockMode,\n    whitelistModuleEnabled,\n    whitelistMode,\n    whitelistAutoDisable,\n    whitelistMutable,\n    whitelistActive,\n    antiDumpModuleEnabled,\n    antiDumpType,\n    antiDumpLimit,\n    antiDumpPeriod,\n    antiDumpEndTime,\n    batchTransferAccess,\n    batchTransferFees,\n    maxBatchSize,\n    feesEnabled,\n    buyFeeBps,\n    sellFeeBps,\n    transferFeeBps,\n    feeRecipient,\n    feeBurnShareBps,\n    tradingEnabled,\n    tradingEnabledAt,\n    router,\n    routerProtectionEnabled,\n    maxWalletAmount,\n    maxTxAmount,\n    limitsEndTime,\n    cooldownSeconds,\n    blacklistFrozen,\n    paused,\n    permitEnabled,\n    feesFrozen,\n    limitsFrozen,\n    cooldownFrozen,\n    antiDumpFrozen,\n    ownershipTransferBlocked,\n  ] = decoded as [\n    string, // name\n    string, // symbol\n    bigint | number, // decimals\n    bigint, // totalSupply\n    string, // owner\n    boolean, // metadataMutable\n    boolean, // socialsMutable\n    string, // website\n    string, // telegram\n    string, // xHandle\n    string, // discord\n    boolean, // mintingEnabled\n    boolean, // pausable\n    boolean, // rescueEnabled\n    boolean, // burnEnabled\n    bigint, // hardCap\n    boolean, // feesModuleEnabled\n    boolean, // limitsModuleEnabled\n    boolean, // cooldownModuleEnabled\n    boolean, // blacklistModuleEnabled\n    bigint | number, // maxBuyFeeBps\n    bigint | number, // maxSellFeeBps\n    bigint | number, // maxTransferFeeBps\n    bigint | number, // maxCooldownSeconds\n    boolean, // deadblocksEnabled\n    bigint | number, // deadBlocks\n    bigint | number, // deadblockMode\n    boolean, // whitelistModuleEnabled\n    bigint | number, // whitelistMode\n    boolean, // whitelistAutoDisable\n    boolean, // whitelistMutable\n    boolean, // whitelistActive\n    boolean, // antiDumpModuleEnabled\n    bigint | number, // antiDumpType\n    bigint, // antiDumpLimit\n    bigint | number, // antiDumpPeriod\n    bigint | number, // antiDumpEndTime\n    bigint | number, // batchTransferAccess\n    bigint | number, // batchTransferFees\n    bigint | number, // maxBatchSize\n    boolean, // feesEnabled\n    bigint | number, // buyFeeBps\n    bigint | number, // sellFeeBps\n    bigint | number, // transferFeeBps\n    string, // feeRecipient\n    bigint | number, // feeBurnShareBps\n    boolean, // tradingEnabled\n    bigint | number, // tradingEnabledAt\n    string, // router\n    boolean, // routerProtectionEnabled\n    bigint, // maxWalletAmount\n    bigint, // maxTxAmount\n    bigint | number, // limitsEndTime\n    bigint | number, // cooldownSeconds\n    boolean, // blacklistFrozen\n    boolean, // paused\n    boolean, // permitEnabled\n    boolean, // feesFrozen\n    boolean, // limitsFrozen\n    boolean, // cooldownFrozen\n    boolean, // antiDumpFrozen\n    boolean, // ownershipTransferBlocked\n  ];\n\n  const supply = totalSupply as bigint;\n\n  return {\n    address: tokenAddress,\n    name: name as string,\n    symbol: symbol as string,\n    decimals,\n    totalSupply: supply,\n    totalSupplyFormatted: formatAmount(supply, decimals),\n    owner: owner as string,\n    metadataMutable: metadataMutable as boolean,\n    socialsMutable: socialsMutable as boolean,\n    website: website as string,\n    telegram: telegram as string,\n    xHandle: xHandle as string,\n    discord: discord as string,\n    mintingEnabled: mintingEnabled as boolean,\n    pausable: pausable as boolean,\n    rescueEnabled: rescueEnabled as boolean,\n    burnEnabled: burnEnabled as boolean,\n    hardCap: hardCap as bigint,\n    hardCapFormatted: formatAmount(hardCap as bigint, decimals),\n    feesModuleEnabled: feesModuleEnabled as boolean,\n    limitsModuleEnabled: limitsModuleEnabled as boolean,\n    cooldownModuleEnabled: cooldownModuleEnabled as boolean,\n    blacklistModuleEnabled: blacklistModuleEnabled as boolean,\n    whitelistModuleEnabled: whitelistModuleEnabled as boolean,\n    antiDumpModuleEnabled: antiDumpModuleEnabled as boolean,\n    deadblocksEnabled: deadblocksEnabled as boolean,\n    maxBuyFeeBps: Number(maxBuyFeeBps),\n    maxSellFeeBps: Number(maxSellFeeBps),\n    maxTransferFeeBps: Number(maxTransferFeeBps),\n    maxCooldownSeconds: Number(maxCooldownSeconds),\n    feesEnabled: feesEnabled as boolean,\n    buyFeeBps: Number(buyFeeBps),\n    sellFeeBps: Number(sellFeeBps),\n    transferFeeBps: Number(transferFeeBps),\n    feeRecipient: feeRecipient as string,\n    feeBurnShareBps: Number(feeBurnShareBps),\n    maxWalletAmount: maxWalletAmount as bigint,\n    maxWalletFormatted: formatAmount(maxWalletAmount as bigint, decimals),\n    maxWalletPercent: calcPercent(maxWalletAmount as bigint, supply),\n    maxTxAmount: maxTxAmount as bigint,\n    maxTxFormatted: formatAmount(maxTxAmount as bigint, decimals),\n    maxTxPercent: calcPercent(maxTxAmount as bigint, supply),\n    limitsEndTime: Number(limitsEndTime),\n    cooldownSeconds: Number(cooldownSeconds),\n    tradingEnabled: tradingEnabled as boolean,\n    tradingEnabledAt: Number(tradingEnabledAt),\n    router: router as string,\n    routerProtectionEnabled: routerProtectionEnabled as boolean,\n    blacklistFrozen: blacklistFrozen as boolean,\n    whitelistMode: Number(whitelistMode),\n    whitelistAutoDisable: whitelistAutoDisable as boolean,\n    whitelistMutable: whitelistMutable as boolean,\n    whitelistActive: whitelistActive as boolean,\n    antiDumpType: Number(antiDumpType),\n    antiDumpLimit: antiDumpLimit as bigint,\n    antiDumpLimitFormatted: formatAmount(antiDumpLimit as bigint, decimals),\n    antiDumpPeriod: Number(antiDumpPeriod),\n    antiDumpEndTime: Number(antiDumpEndTime),\n    deadBlocks: Number(deadBlocks),\n    deadblockMode: Number(deadblockMode),\n    batchTransferAccess: Number(batchTransferAccess),\n    batchTransferFees: Number(batchTransferFees),\n    maxBatchSize: Number(maxBatchSize),\n    paused: paused as boolean,\n    whitelistFrozen: !(whitelistMutable as boolean), // whitelistMutable=false means frozen\n    permitEnabled: permitEnabled as boolean,\n    feesFrozen: feesFrozen as boolean,\n    limitsFrozen: limitsFrozen as boolean,\n    cooldownFrozen: cooldownFrozen as boolean,\n    antiDumpFrozen: antiDumpFrozen as boolean,\n    ownershipTransferBlocked: ownershipTransferBlocked as boolean,\n  };\n}\n\n// ============================================================================\n// Provider Utilities\n// ============================================================================\n\n/**\n * Create a JsonRpcProvider from configuration.\n *\n * @param config - Configuration with rpcUrl and optional chainId\n * @returns Configured JsonRpcProvider instance\n *\n * @example\n * ```typescript\n * const provider = createProvider({\n *   rpcUrl: 'https://rpc.sepolia.org',\n *   chainId: 11155111\n * });\n * ```\n */\nexport function createProvider(config: {\n  rpcUrl: string;\n  chainId?: number;\n}): JsonRpcProvider {\n  return new JsonRpcProvider(config.rpcUrl, config.chainId);\n}\n\n/**\n * Check if Multicall3 is available at the given address.\n *\n * @param provider - JsonRpcProvider instance\n * @param multicallAddress - Address to check (defaults to STANDARD_MULTICALL3_ADDRESS)\n * @returns True if Multicall3 contract exists at address\n *\n * @example\n * ```typescript\n * const available = await checkMulticall3Available(provider);\n * if (available) {\n *   // Use batched reads\n * } else {\n *   // Fall back to individual calls\n * }\n * ```\n */\nexport async function checkMulticall3Available(\n  provider: JsonRpcProvider,\n  multicallAddress: string = STANDARD_MULTICALL3_ADDRESS\n): Promise<boolean> {\n  try {\n    const code = await provider.getCode(multicallAddress);\n    return code !== '0x' && code.length > 2;\n  } catch {\n    return false;\n  }\n}\n\n// ============================================================================\n// Core Read Functions\n// ============================================================================\n\n/**\n * Read token data using Multicall3 - batches all calls into a single RPC request.\n * This is approximately 60x more efficient than individual calls.\n *\n * @param provider - JsonRpcProvider instance\n * @param tokenAddress - Token contract address\n * @param multicallAddress - Multicall3 contract address\n * @returns Complete TokenDataFlat object\n * @throws Error if any multicall fails\n *\n * @example\n * ```typescript\n * const provider = createProvider({ rpcUrl: 'https://rpc.sepolia.org' });\n * const tokenData = await readTokenDataViaMulticall(\n *   provider,\n *   '0x1234...5678',\n *   STANDARD_MULTICALL3_ADDRESS\n * );\n * ```\n */\nexport async function readTokenDataViaMulticall(\n  provider: JsonRpcProvider,\n  tokenAddress: string,\n  multicallAddress: string\n): Promise<TokenDataFlat> {\n  const multicall = new Contract(multicallAddress, MULTICALL3_ABI, provider);\n  const tokenInterface = new Interface(TOKEN_READ_ABI);\n\n  // Build Multicall3 calls array\n  const calls = TOKEN_FUNCTION_NAMES.map((fn) => ({\n    target: tokenAddress,\n    allowFailure: false,\n    callData: tokenInterface.encodeFunctionData(fn),\n  }));\n\n  // Execute single batched call\n  const results: { success: boolean; returnData: string }[] =\n    await multicall.aggregate3.staticCall(calls);\n\n  // Decode all results\n  const decoded = results.map((result, i) => {\n    if (!result.success) {\n      throw new Error(`Multicall failed for ${TOKEN_FUNCTION_NAMES[i]}`);\n    }\n    return tokenInterface.decodeFunctionResult(\n      TOKEN_FUNCTION_NAMES[i],\n      result.returnData\n    )[0];\n  });\n\n  // Get decimals from decoded results (index 2)\n  const decimals = Number(decoded[2]);\n\n  return buildTokenData(tokenAddress, decoded, decimals);\n}\n\n/**\n * Read token data using individual calls - fallback for chains without Multicall3.\n * Makes approximately 60 parallel RPC requests.\n *\n * @param provider - JsonRpcProvider instance\n * @param tokenAddress - Token contract address\n * @returns Complete TokenDataFlat object\n *\n * @example\n * ```typescript\n * const provider = createProvider({ rpcUrl: 'https://rpc.pepeunchained.com' });\n * const tokenData = await readTokenDataIndividual(provider, '0x1234...5678');\n * ```\n */\nexport async function readTokenDataIndividual(\n  provider: JsonRpcProvider,\n  tokenAddress: string\n): Promise<TokenDataFlat> {\n  const contract = new Contract(tokenAddress, TOKEN_READ_ABI, provider);\n\n  // Read all data in parallel for performance\n  const decoded = await Promise.all(\n    TOKEN_FUNCTION_NAMES.map((fn) =>\n      (contract[fn] as () => Promise<unknown>)()\n    )\n  );\n\n  // Get decimals from decoded results (index 2)\n  const decimals = Number(decoded[2]);\n\n  return buildTokenData(tokenAddress, decoded, decimals);\n}\n\n/**\n * Read all token data from the blockchain.\n * Automatically uses Multicall3 if available, otherwise falls back to individual calls.\n *\n * @param provider - JsonRpcProvider instance\n * @param tokenAddress - Token contract address\n * @param chainId - Chain ID (used for logging/debugging)\n * @param config - Optional configuration with multicall3Address\n * @returns Complete TokenDataFlat object\n *\n * @example\n * ```typescript\n * // With Multicall3 (recommended)\n * const provider = createProvider({ rpcUrl: 'https://rpc.sepolia.org', chainId: 11155111 });\n * const tokenData = await readTokenData(\n *   provider,\n *   '0x1234...5678',\n *   11155111,\n *   { multicall3Address: STANDARD_MULTICALL3_ADDRESS }\n * );\n *\n * // Without Multicall3 (falls back to individual calls)\n * const tokenData = await readTokenData(\n *   provider,\n *   '0x1234...5678',\n *   97741 // Pepe Unchained - no Multicall3\n * );\n * ```\n */\nexport async function readTokenData(\n  provider: JsonRpcProvider,\n  tokenAddress: string,\n  _chainId: number,\n  config?: ReadConfig\n): Promise<TokenDataFlat> {\n  const multicallAddress = config?.multicall3Address;\n\n  // Use Multicall3 if available (batches ~60 calls into 1 RPC request)\n  if (multicallAddress) {\n    return readTokenDataViaMulticall(provider, tokenAddress, multicallAddress);\n  }\n\n  // Fall back to individual calls for chains without Multicall3\n  return readTokenDataIndividual(provider, tokenAddress);\n}\n\n/**\n * Read multiple tokens in parallel.\n * Useful for fetching data for a list of tokens (e.g., launchpad).\n *\n * @param tokens - Array of token identifiers (address + chainId)\n * @param configs - Map of chainId to ReadConfig (with multicall3Address)\n * @param providerFactory - Function to create/retrieve provider for a chain\n * @returns Array of TokenDataFlat (in same order as input tokens)\n *\n * @example\n * ```typescript\n * const tokens = [\n *   { address: '0x1234...', chainId: 11155111 },\n *   { address: '0x5678...', chainId: 11155111 },\n *   { address: '0x9abc...', chainId: 1 },\n * ];\n *\n * const configs = {\n *   11155111: { multicall3Address: STANDARD_MULTICALL3_ADDRESS },\n *   1: { multicall3Address: STANDARD_MULTICALL3_ADDRESS },\n * };\n *\n * const providerCache = new Map<number, JsonRpcProvider>();\n * const getProvider = (chainId: number) => {\n *   if (!providerCache.has(chainId)) {\n *     providerCache.set(chainId, createProvider({ rpcUrl: RPC_URLS[chainId], chainId }));\n *   }\n *   return providerCache.get(chainId)!;\n * };\n *\n * const results = await readMultipleTokens(tokens, configs, getProvider);\n * ```\n */\nexport async function readMultipleTokens(\n  tokens: TokenIdentifier[],\n  configs: Record<number, ReadConfig>,\n  providerFactory: (chainId: number) => JsonRpcProvider\n): Promise<TokenDataFlat[]> {\n  const promises = tokens.map((token) => {\n    const provider = providerFactory(token.chainId);\n    const config = configs[token.chainId];\n    return readTokenData(provider, token.address, token.chainId, config);\n  });\n\n  return Promise.all(promises);\n}\n\n// ============================================================================\n// Event Reading Functions\n// ============================================================================\n\n/**\n * Fetch initial distribution data from the factory's InitialDistribution event.\n * Also calculates and includes the owner's remainder if any tokens weren't explicitly distributed.\n *\n * @param provider - JsonRpcProvider instance\n * @param tokenAddress - Token contract address\n * @param factoryAddress - LaunchERC20Factory contract address\n * @param decimals - Token decimals for formatting\n * @param totalSupply - Total supply for percentage calculations\n * @param ownerAddress - Optional owner address to calculate remainder\n * @param lookbackBlocks - Number of blocks to look back (default: 10000)\n * @returns Array of DistributionEntryDetailed objects\n *\n * @example\n * ```typescript\n * const distribution = await fetchInitialDistribution(\n *   provider,\n *   '0x1234...5678',\n *   '0xfactory...',\n *   18,\n *   1000000000000000000000000n,\n *   '0xowner...'\n * );\n *\n * for (const entry of distribution) {\n *   console.log(`${entry.label}: ${entry.percent}% (${entry.amountFormatted})`);\n * }\n * ```\n */\nexport async function fetchInitialDistribution(\n  provider: JsonRpcProvider,\n  tokenAddress: string,\n  factoryAddress: string,\n  decimals: number,\n  totalSupply: bigint,\n  ownerAddress?: string,\n  lookbackBlocks: number = 10000\n): Promise<DistributionEntryDetailed[]> {\n  const factory = new Contract(factoryAddress, FACTORY_EVENT_ABI, provider);\n\n  try {\n    // Query logs for InitialDistribution event for this specific token\n    const filter = factory.filters.InitialDistribution(tokenAddress);\n    const logs = await factory.queryFilter(filter, -lookbackBlocks);\n\n    if (logs.length === 0) {\n      return [];\n    }\n\n    // Take the most recent event (should only be one per token)\n    const event = logs[logs.length - 1];\n    const parsed = factory.interface.parseLog({\n      topics: event.topics as string[],\n      data: event.data,\n    });\n\n    if (!parsed) {\n      return [];\n    }\n\n    const recipients = parsed.args.recipients as string[];\n    const amounts = parsed.args.amounts as bigint[];\n    const labels = parsed.args.labels as string[];\n\n    // Format the distribution entries\n    const entries: DistributionEntryDetailed[] = [];\n    let totalDistributed = 0n;\n\n    for (let i = 0; i < recipients.length; i++) {\n      const amount = amounts[i];\n      totalDistributed += amount;\n\n      const percent = calcPercent(amount, totalSupply);\n\n      const label = labels[i] || '';\n      // Mark entries with \"Owner\" label as owner remainder (new tokens include owner in event)\n      const isOwnerEntry =\n        label === 'Owner' &&\n        !!ownerAddress &&\n        recipients[i].toLowerCase() === ownerAddress.toLowerCase();\n\n      entries.push({\n        recipient: recipients[i],\n        amount,\n        amountFormatted: formatAmount(amount, decimals),\n        percent,\n        label,\n        isOwnerRemainder: isOwnerEntry,\n      });\n    }\n\n    // Calculate and add owner's remainder if there's any (backward compatibility for old tokens)\n    // New tokens will have owner included in the event, so totalDistributed will equal totalSupply\n    if (ownerAddress && totalSupply > totalDistributed) {\n      const remainderAmount = totalSupply - totalDistributed;\n      const remainderPercent = calcPercent(remainderAmount, totalSupply);\n\n      entries.push({\n        recipient: ownerAddress,\n        amount: remainderAmount,\n        amountFormatted: formatAmount(remainderAmount, decimals),\n        percent: remainderPercent,\n        label: 'Owner',\n        isOwnerRemainder: true,\n      });\n    }\n\n    return entries;\n  } catch (err) {\n    console.warn('Failed to fetch initial distribution:', err);\n    return [];\n  }\n}\n\n// ============================================================================\n// Re-exports\n// ============================================================================\n\nexport type {\n  TokenDataFlat,\n  DistributionEntryDetailed,\n  ReadConfig,\n  TokenIdentifier,\n};\n","/**\n * @fileoverview CREATE2 and vanity address utilities for EscapeHub SDK.\n *\n * This module provides framework-agnostic utilities for:\n * - Computing CREATE2 addresses\n * - Building minimal proxy (clone) bytecode\n * - Mining vanity addresses with customizable patterns\n *\n * @module @escapehub/token-creator/create2\n */\n\nimport { keccak256, getBytes, concat, getAddress, hexlify, randomBytes } from 'ethers';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * CREATE2 prefix byte (0xff) as defined in EIP-1014.\n * Used in CREATE2 address computation: keccak256(0xff ++ deployer ++ salt ++ keccak256(initCode))\n *\n * @example\n * ```typescript\n * import { CREATE2_PREFIX } from '@escapehub/token-creator';\n *\n * console.log(CREATE2_PREFIX); // '0xff'\n * ```\n */\nexport const CREATE2_PREFIX = '0xff';\n\n/**\n * OpenZeppelin Clones minimal proxy bytecode prefix.\n * This is the first part of the EIP-1167 minimal proxy pattern.\n *\n * The full bytecode is: MINIMAL_PROXY_PREFIX + implementation address (20 bytes) + MINIMAL_PROXY_SUFFIX\n *\n * @see https://eips.ethereum.org/EIPS/eip-1167\n */\nexport const MINIMAL_PROXY_PREFIX = '0x3d602d80600a3d3981f3363d3d373d3d3d363d73';\n\n/**\n * OpenZeppelin Clones minimal proxy bytecode suffix.\n * This is the second part of the EIP-1167 minimal proxy pattern.\n *\n * The full bytecode is: MINIMAL_PROXY_PREFIX + implementation address (20 bytes) + MINIMAL_PROXY_SUFFIX\n *\n * @see https://eips.ethereum.org/EIPS/eip-1167\n */\nexport const MINIMAL_PROXY_SUFFIX = '0x5af43d82803e903d91602b57fd5bf3';\n\n/**\n * Zero salt (32 bytes of zeros).\n * Commonly used as a default salt when no vanity pattern is required.\n *\n * @example\n * ```typescript\n * import { ZERO_SALT, isZeroSalt } from '@escapehub/token-creator';\n *\n * console.log(ZERO_SALT);\n * // '0x0000000000000000000000000000000000000000000000000000000000000000'\n *\n * console.log(isZeroSalt(ZERO_SALT)); // true\n * ```\n */\nexport const ZERO_SALT = '0x0000000000000000000000000000000000000000000000000000000000000000';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Vanity pattern matching mode.\n *\n * - `'prefix'`: Pattern must match the beginning of the address (after 0x)\n * - `'suffix'`: Pattern must match the end of the address\n *\n * @example\n * ```typescript\n * import { VanityPatternMode } from '@escapehub/token-creator';\n *\n * const mode: VanityPatternMode = 'prefix';\n * // Matches addresses like 0xDEAD...\n *\n * const suffixMode: VanityPatternMode = 'suffix';\n * // Matches addresses like 0x...BEEF\n * ```\n */\nexport type VanityPatternMode = 'prefix' | 'suffix';\n\n/**\n * Result of vanity address mining.\n */\nexport interface VanityResult {\n  /**\n   * The salt that produces the vanity address (32 bytes hex string).\n   */\n  salt: string;\n\n  /**\n   * The computed CREATE2 address matching the vanity pattern.\n   * Checksummed (EIP-55) format.\n   */\n  address: string;\n\n  /**\n   * Number of attempts taken to find the matching address.\n   */\n  attempts: number;\n}\n\n/**\n * Options for vanity address mining.\n */\nexport interface VanityMiningOptions {\n  /**\n   * Hex pattern to match (without 0x prefix).\n   * Only hex characters (0-9, a-f, A-F) are allowed.\n   *\n   * @example 'DEAD', 'cafe', '1234'\n   */\n  pattern: string;\n\n  /**\n   * Where to match the pattern in the address.\n   *\n   * - `'prefix'`: Match at the start (e.g., 0xDEAD...)\n   * - `'suffix'`: Match at the end (e.g., ...BEEF)\n   *\n   * @default 'prefix'\n   */\n  mode?: VanityPatternMode;\n\n  /**\n   * Whether pattern matching is case-sensitive.\n   *\n   * When false, 'dead' matches 'DEAD', 'Dead', 'deAD', etc.\n   * When true, pattern must match exactly (considering EIP-55 checksum).\n   *\n   * @default false\n   */\n  caseSensitive?: boolean;\n\n  /**\n   * Maximum number of attempts before giving up.\n   * Set to 0 or Infinity for unlimited attempts.\n   *\n   * @default 1_000_000\n   */\n  maxAttempts?: number;\n\n  /**\n   * Callback invoked periodically during mining to report progress.\n   * Useful for updating UI or logging progress.\n   *\n   * @param attempts - Current number of attempts\n   * @param hashRate - Current hashes per second (approximate)\n   */\n  onProgress?: (attempts: number, hashRate: number) => void;\n\n  /**\n   * Starting nonce for salt generation.\n   * Allows resuming mining from a previous point.\n   *\n   * @default 0\n   */\n  startNonce?: number;\n}\n\n// ============================================================================\n// Core CREATE2 Functions\n// ============================================================================\n\n/**\n * Computes a CREATE2 address from factory, salt, and init code hash.\n *\n * The CREATE2 address is computed as:\n * `keccak256(0xff ++ factoryAddress ++ salt ++ initCodeHash)[12:]`\n *\n * @param factoryAddress - Address of the factory contract deploying via CREATE2\n * @param salt - 32-byte salt value (hex string with 0x prefix)\n * @param initCodeHash - keccak256 hash of the contract init code\n * @returns Checksummed CREATE2 address\n *\n * @example\n * ```typescript\n * import { computeCreate2Address, getMinimalProxyInitCodeHash } from '@escapehub/token-creator';\n *\n * const factory = '0x1234567890123456789012345678901234567890';\n * const implementation = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd';\n * const salt = '0x0000000000000000000000000000000000000000000000000000000000000001';\n *\n * const initCodeHash = getMinimalProxyInitCodeHash(implementation);\n * const predictedAddress = computeCreate2Address(factory, salt, initCodeHash);\n *\n * console.log(predictedAddress); // '0x...'\n * ```\n */\nexport function computeCreate2Address(\n  factoryAddress: string,\n  salt: string,\n  initCodeHash: string\n): string {\n  const normalizedFactory = normalizeAddress(factoryAddress);\n\n  // Ensure salt is 32 bytes\n  if (getBytes(salt).length !== 32) {\n    throw new Error('Salt must be 32 bytes');\n  }\n\n  // Ensure initCodeHash is 32 bytes\n  if (getBytes(initCodeHash).length !== 32) {\n    throw new Error('Init code hash must be 32 bytes');\n  }\n\n  // CREATE2 address = keccak256(0xff ++ factory ++ salt ++ initCodeHash)[12:]\n  const data = concat([CREATE2_PREFIX, normalizedFactory, salt, initCodeHash]);\n  const hash = keccak256(data);\n\n  // Take last 20 bytes (40 hex chars) of the hash\n  const addressHex = '0x' + hash.slice(-40);\n\n  return getAddress(addressHex);\n}\n\n/**\n * Builds the minimal proxy (EIP-1167 clone) bytecode for an implementation address.\n *\n * The bytecode follows the OpenZeppelin Clones pattern:\n * `MINIMAL_PROXY_PREFIX + implementation (20 bytes) + MINIMAL_PROXY_SUFFIX`\n *\n * @param implementationAddress - Address of the implementation contract\n * @returns The complete minimal proxy bytecode\n *\n * @example\n * ```typescript\n * import { buildMinimalProxyBytecode } from '@escapehub/token-creator';\n *\n * const implementation = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd';\n * const bytecode = buildMinimalProxyBytecode(implementation);\n *\n * console.log(bytecode);\n * // '0x3d602d80600a3d3981f3363d3d373d3d3d363d73abcdefabcdefabcdefabcdefabcdefabcdefabcd5af43d82803e903d91602b57fd5bf3'\n * ```\n */\nexport function buildMinimalProxyBytecode(implementationAddress: string): string {\n  const normalized = normalizeAddress(implementationAddress).toLowerCase();\n  // Remove 0x prefix from implementation address for concatenation\n  const implWithout0x = normalized.slice(2);\n\n  return (\n    MINIMAL_PROXY_PREFIX.toLowerCase() +\n    implWithout0x +\n    MINIMAL_PROXY_SUFFIX.slice(2).toLowerCase()\n  );\n}\n\n/**\n * Computes the keccak256 hash of the minimal proxy bytecode for an implementation.\n *\n * This is the init code hash needed for CREATE2 address computation when\n * deploying clones via OpenZeppelin's Clones library.\n *\n * @param implementationAddress - Address of the implementation contract\n * @returns keccak256 hash of the minimal proxy bytecode\n *\n * @example\n * ```typescript\n * import { getMinimalProxyInitCodeHash, computeCreate2Address } from '@escapehub/token-creator';\n *\n * const implementation = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd';\n * const initCodeHash = getMinimalProxyInitCodeHash(implementation);\n *\n * // Use with computeCreate2Address to predict clone addresses\n * const factory = '0x1234567890123456789012345678901234567890';\n * const salt = '0x0000000000000000000000000000000000000000000000000000000000000001';\n * const cloneAddress = computeCreate2Address(factory, salt, initCodeHash);\n * ```\n */\nexport function getMinimalProxyInitCodeHash(implementationAddress: string): string {\n  const bytecode = buildMinimalProxyBytecode(implementationAddress);\n  return keccak256(bytecode);\n}\n\n// ============================================================================\n// Vanity Address Mining\n// ============================================================================\n\n/**\n * Checks if an address matches a vanity pattern.\n *\n * @param address - The address to check (with or without 0x prefix)\n * @param pattern - The hex pattern to match (without 0x prefix)\n * @param mode - Whether to match at 'prefix' or 'suffix' of address\n * @param caseSensitive - Whether matching is case-sensitive\n * @returns True if the address matches the pattern\n *\n * @example\n * ```typescript\n * import { matchesVanityPattern } from '@escapehub/token-creator';\n *\n * // Prefix matching (case-insensitive)\n * matchesVanityPattern('0xDEAD1234...', 'dead', 'prefix', false); // true\n * matchesVanityPattern('0xDEAD1234...', 'dead', 'prefix', true);  // false (case mismatch)\n *\n * // Suffix matching\n * matchesVanityPattern('0x...BEEF', 'beef', 'suffix', false); // true\n * ```\n */\nexport function matchesVanityPattern(\n  address: string,\n  pattern: string,\n  mode: VanityPatternMode = 'prefix',\n  caseSensitive: boolean = false\n): boolean {\n  // Remove 0x prefix if present\n  const addressHex = address.startsWith('0x') ? address.slice(2) : address;\n\n  const compareAddress = caseSensitive ? addressHex : addressHex.toLowerCase();\n  const comparePattern = caseSensitive ? pattern : pattern.toLowerCase();\n\n  if (mode === 'prefix') {\n    return compareAddress.startsWith(comparePattern);\n  } else {\n    return compareAddress.endsWith(comparePattern);\n  }\n}\n\n/**\n * Validates that a pattern contains only valid hex characters.\n *\n * @param pattern - The pattern to validate (without 0x prefix)\n * @returns True if pattern contains only hex characters (0-9, a-f, A-F)\n *\n * @example\n * ```typescript\n * import { isValidHexPattern } from '@escapehub/token-creator';\n *\n * isValidHexPattern('DEAD');   // true\n * isValidHexPattern('cafe');   // true\n * isValidHexPattern('1234');   // true\n * isValidHexPattern('GHIJ');   // false (G, H, I, J are not hex)\n * isValidHexPattern('0xDEAD'); // false (0x prefix not allowed)\n * ```\n */\nexport function isValidHexPattern(pattern: string): boolean {\n  return /^[0-9a-fA-F]+$/.test(pattern);\n}\n\n/**\n * Generates a random salt, optionally incorporating a nonce.\n *\n * When a nonce is provided, the salt is derived from random bytes XORed\n * with the nonce to ensure uniqueness while maintaining randomness.\n *\n * @param nonce - Optional nonce to incorporate into the salt\n * @returns 32-byte hex string salt\n *\n * @example\n * ```typescript\n * import { generateRandomSalt } from '@escapehub/token-creator';\n *\n * // Fully random salt\n * const salt1 = generateRandomSalt();\n *\n * // Salt with nonce (for sequential mining)\n * const salt2 = generateRandomSalt(12345);\n * ```\n */\nexport function generateRandomSalt(nonce?: number): string {\n  const random = randomBytes(32);\n\n  if (nonce !== undefined) {\n    // Incorporate nonce into the last 8 bytes\n    const nonceBytes = new Uint8Array(8);\n    const view = new DataView(nonceBytes.buffer);\n    view.setBigUint64(0, BigInt(nonce), false); // big-endian\n\n    const randomArray = new Uint8Array(random);\n    for (let i = 0; i < 8; i++) {\n      randomArray[24 + i] ^= nonceBytes[i];\n    }\n    return hexlify(randomArray);\n  }\n\n  return hexlify(random);\n}\n\n/**\n * Mines a vanity address by trying different salts until a match is found.\n *\n * This is a synchronous operation that will block the event loop.\n * For non-blocking mining, use {@link generateSaltAsync}.\n *\n * @param factoryAddress - Address of the CREATE2 factory contract\n * @param initCodeHash - keccak256 hash of the contract init code\n * @param options - Vanity mining options\n * @returns The salt and address that match the pattern, or null if max attempts reached\n *\n * @throws Error if pattern is invalid\n *\n * @example\n * ```typescript\n * import { generateSalt, getMinimalProxyInitCodeHash } from '@escapehub/token-creator';\n *\n * const factory = '0x1234567890123456789012345678901234567890';\n * const implementation = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd';\n * const initCodeHash = getMinimalProxyInitCodeHash(implementation);\n *\n * const result = generateSalt(factory, initCodeHash, {\n *   pattern: 'DEAD',\n *   mode: 'prefix',\n *   maxAttempts: 100_000,\n *   onProgress: (attempts, hashRate) => {\n *     console.log(`Attempts: ${attempts}, Rate: ${hashRate} H/s`);\n *   }\n * });\n *\n * if (result) {\n *   console.log(`Found! Address: ${result.address}, Salt: ${result.salt}`);\n *   console.log(`Took ${result.attempts} attempts`);\n * } else {\n *   console.log('No match found within max attempts');\n * }\n * ```\n */\nexport function generateSalt(\n  factoryAddress: string,\n  initCodeHash: string,\n  options: VanityMiningOptions\n): VanityResult | null {\n  const {\n    pattern,\n    mode = 'prefix',\n    caseSensitive = false,\n    maxAttempts = 1_000_000,\n    onProgress,\n    startNonce = 0,\n  } = options;\n\n  // Validate pattern\n  if (!isValidHexPattern(pattern)) {\n    throw new Error(`Invalid hex pattern: ${pattern}. Only hex characters (0-9, a-f, A-F) allowed.`);\n  }\n\n  if (pattern.length > 40) {\n    throw new Error(`Pattern too long: ${pattern.length} chars. Maximum is 40 (full address).`);\n  }\n\n  const normalizedFactory = normalizeAddress(factoryAddress);\n  const limit = maxAttempts === 0 ? Infinity : maxAttempts;\n  const progressInterval = 10_000; // Report every 10k attempts\n  let lastProgressTime = Date.now();\n  let lastProgressAttempts = 0;\n\n  for (let nonce = startNonce; nonce - startNonce < limit; nonce++) {\n    const salt = generateRandomSalt(nonce);\n    const address = computeCreate2Address(normalizedFactory, salt, initCodeHash);\n\n    if (matchesVanityPattern(address, pattern, mode, caseSensitive)) {\n      return {\n        salt,\n        address,\n        attempts: nonce - startNonce + 1,\n      };\n    }\n\n    // Progress callback\n    if (onProgress && (nonce - startNonce + 1) % progressInterval === 0) {\n      const now = Date.now();\n      const elapsed = (now - lastProgressTime) / 1000;\n      const attemptsDone = nonce - startNonce + 1 - lastProgressAttempts;\n      const hashRate = Math.round(attemptsDone / elapsed);\n\n      onProgress(nonce - startNonce + 1, hashRate);\n\n      lastProgressTime = now;\n      lastProgressAttempts = nonce - startNonce + 1;\n    }\n  }\n\n  return null;\n}\n\n/**\n * Mines a vanity address asynchronously, yielding to the event loop periodically.\n *\n * This allows other operations to proceed during mining, making it suitable\n * for use in web applications or Node.js servers.\n *\n * @param factoryAddress - Address of the CREATE2 factory contract\n * @param initCodeHash - keccak256 hash of the contract init code\n * @param options - Vanity mining options\n * @param yieldInterval - Number of attempts between event loop yields\n * @returns Promise resolving to the salt and address, or null if max attempts reached\n *\n * @example\n * ```typescript\n * import { generateSaltAsync, getMinimalProxyInitCodeHash } from '@escapehub/token-creator';\n *\n * const factory = '0x1234567890123456789012345678901234567890';\n * const implementation = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd';\n * const initCodeHash = getMinimalProxyInitCodeHash(implementation);\n *\n * const result = await generateSaltAsync(factory, initCodeHash, {\n *   pattern: 'CAFE',\n *   mode: 'prefix',\n *   maxAttempts: 500_000,\n *   onProgress: (attempts, hashRate) => {\n *     console.log(`Progress: ${attempts} attempts, ${hashRate} H/s`);\n *   }\n * }, 1000); // Yield every 1000 attempts\n *\n * if (result) {\n *   console.log(`Found vanity address: ${result.address}`);\n * }\n * ```\n */\nexport async function generateSaltAsync(\n  factoryAddress: string,\n  initCodeHash: string,\n  options: VanityMiningOptions,\n  yieldInterval: number = 1000\n): Promise<VanityResult | null> {\n  const {\n    pattern,\n    mode = 'prefix',\n    caseSensitive = false,\n    maxAttempts = 1_000_000,\n    onProgress,\n    startNonce = 0,\n  } = options;\n\n  // Validate pattern\n  if (!isValidHexPattern(pattern)) {\n    throw new Error(`Invalid hex pattern: ${pattern}. Only hex characters (0-9, a-f, A-F) allowed.`);\n  }\n\n  if (pattern.length > 40) {\n    throw new Error(`Pattern too long: ${pattern.length} chars. Maximum is 40 (full address).`);\n  }\n\n  const normalizedFactory = normalizeAddress(factoryAddress);\n  const limit = maxAttempts === 0 ? Infinity : maxAttempts;\n  const progressInterval = 10_000;\n  let lastProgressTime = Date.now();\n  let lastProgressAttempts = 0;\n\n  for (let nonce = startNonce; nonce - startNonce < limit; nonce++) {\n    const salt = generateRandomSalt(nonce);\n    const address = computeCreate2Address(normalizedFactory, salt, initCodeHash);\n\n    if (matchesVanityPattern(address, pattern, mode, caseSensitive)) {\n      return {\n        salt,\n        address,\n        attempts: nonce - startNonce + 1,\n      };\n    }\n\n    // Yield to event loop\n    if ((nonce - startNonce + 1) % yieldInterval === 0) {\n      await new Promise((resolve) => setTimeout(resolve, 0));\n    }\n\n    // Progress callback\n    if (onProgress && (nonce - startNonce + 1) % progressInterval === 0) {\n      const now = Date.now();\n      const elapsed = (now - lastProgressTime) / 1000;\n      const attemptsDone = nonce - startNonce + 1 - lastProgressAttempts;\n      const hashRate = Math.round(attemptsDone / elapsed);\n\n      onProgress(nonce - startNonce + 1, hashRate);\n\n      lastProgressTime = now;\n      lastProgressAttempts = nonce - startNonce + 1;\n    }\n  }\n\n  return null;\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Normalizes an Ethereum address to checksummed format.\n *\n * @param address - The address to normalize\n * @returns Checksummed address (EIP-55)\n * @throws Error if address is invalid\n *\n * @example\n * ```typescript\n * import { normalizeAddress } from '@escapehub/token-creator';\n *\n * normalizeAddress('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef');\n * // '0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF' (checksummed)\n * ```\n */\nexport function normalizeAddress(address: string): string {\n  return getAddress(address);\n}\n\n/**\n * Checks if a salt is the zero salt (all zeros).\n *\n * @param salt - The salt to check\n * @returns True if salt is all zeros\n *\n * @example\n * ```typescript\n * import { isZeroSalt, ZERO_SALT, generateRandomSalt } from '@escapehub/token-creator';\n *\n * isZeroSalt(ZERO_SALT);           // true\n * isZeroSalt(generateRandomSalt()); // false (almost certainly)\n * ```\n */\nexport function isZeroSalt(salt: string): boolean {\n  const bytes = getBytes(salt);\n  if (bytes.length !== 32) return false;\n  return bytes.every((b) => b === 0);\n}\n\n/**\n * Estimates the expected number of attempts to find a vanity address.\n *\n * For prefix matching: 16^patternLength attempts on average\n * For case-sensitive matching with checksummed addresses, the calculation\n * is more complex due to EIP-55 checksum rules.\n *\n * @param patternLength - Length of the vanity pattern\n * @param caseSensitive - Whether matching is case-sensitive\n * @returns Estimated number of attempts\n *\n * @example\n * ```typescript\n * import { estimateVanityAttempts, formatAttempts } from '@escapehub/token-creator';\n *\n * // 4-character prefix\n * const attempts = estimateVanityAttempts(4, false);\n * console.log(formatAttempts(attempts)); // '65,536'\n *\n * // 6-character prefix (harder)\n * const harder = estimateVanityAttempts(6, false);\n * console.log(formatAttempts(harder)); // '16,777,216'\n * ```\n */\nexport function estimateVanityAttempts(patternLength: number, caseSensitive: boolean = false): number {\n  if (patternLength <= 0) return 1;\n  if (patternLength > 40) return Infinity;\n\n  // For case-insensitive: 16^n (each hex position has 16 possibilities)\n  // For case-sensitive: More complex due to checksum, but approximately 22^n\n  // (letters can be upper/lower based on checksum, numbers are always the same)\n  const base = caseSensitive ? 22 : 16;\n  return Math.pow(base, patternLength);\n}\n\n/**\n * Formats a large number with thousand separators.\n *\n * @param attempts - The number to format\n * @returns Formatted string with commas\n *\n * @example\n * ```typescript\n * import { formatAttempts } from '@escapehub/token-creator';\n *\n * formatAttempts(1000);      // '1,000'\n * formatAttempts(1000000);   // '1,000,000'\n * formatAttempts(16777216);  // '16,777,216'\n * ```\n */\nexport function formatAttempts(attempts: number): string {\n  if (!Number.isFinite(attempts)) return '∞';\n  return attempts.toLocaleString('en-US');\n}\n\n/**\n * Estimates the time to find a vanity address given a hash rate.\n *\n * @param patternLength - Length of the vanity pattern\n * @param hashRate - Hashes per second (attempts per second)\n * @param caseSensitive - Whether matching is case-sensitive\n * @returns Estimated time in seconds\n *\n * @example\n * ```typescript\n * import { estimateVanityTime, formatDuration } from '@escapehub/token-creator';\n *\n * // 4-character pattern at 10,000 H/s\n * const seconds = estimateVanityTime(4, 10_000, false);\n * console.log(formatDuration(seconds)); // '~7 seconds'\n *\n * // 6-character pattern at 10,000 H/s\n * const harder = estimateVanityTime(6, 10_000, false);\n * console.log(formatDuration(harder)); // '~28 minutes'\n * ```\n */\nexport function estimateVanityTime(\n  patternLength: number,\n  hashRate: number,\n  caseSensitive: boolean = false\n): number {\n  if (hashRate <= 0) return Infinity;\n  const attempts = estimateVanityAttempts(patternLength, caseSensitive);\n  return attempts / hashRate;\n}\n\n/**\n * Formats a duration in seconds to a human-readable string.\n *\n * @param seconds - Duration in seconds\n * @returns Human-readable duration string\n *\n * @example\n * ```typescript\n * import { formatDuration } from '@escapehub/token-creator';\n *\n * formatDuration(5);         // '~5 seconds'\n * formatDuration(90);        // '~2 minutes'\n * formatDuration(3700);      // '~1 hour'\n * formatDuration(90000);     // '~1 day'\n * formatDuration(1000000);   // '~12 days'\n * formatDuration(Infinity);  // '∞'\n * ```\n */\nexport function formatDuration(seconds: number): string {\n  if (!Number.isFinite(seconds)) return '∞';\n  if (seconds < 0) return '0 seconds';\n\n  if (seconds < 60) {\n    return `~${Math.round(seconds)} second${Math.round(seconds) !== 1 ? 's' : ''}`;\n  }\n\n  const minutes = seconds / 60;\n  if (minutes < 60) {\n    const rounded = Math.round(minutes);\n    return `~${rounded} minute${rounded !== 1 ? 's' : ''}`;\n  }\n\n  const hours = minutes / 60;\n  if (hours < 24) {\n    const rounded = Math.round(hours);\n    return `~${rounded} hour${rounded !== 1 ? 's' : ''}`;\n  }\n\n  const days = hours / 24;\n  if (days < 365) {\n    const rounded = Math.round(days);\n    return `~${rounded} day${rounded !== 1 ? 's' : ''}`;\n  }\n\n  const years = days / 365;\n  const rounded = Math.round(years * 10) / 10;\n  return `~${rounded} year${rounded !== 1 ? 's' : ''}`;\n}\n"],"names":["BrowserProvider","ZERO_ADDRESS","Contract","getMinimalProxyInitCodeHash","keccak256","getBytes","getCreate2Address","Interface","createProvider","JsonRpcProvider","ethers","concat","getAddress","randomBytes","hexlify","rounded"],"mappings":"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"}