From 5bfc7654fcf07ece0541cc4d4c2ef26dc23b4b88 Mon Sep 17 00:00:00 2001 From: xszxc <2112067692@qq.com> Date: Mon, 31 Mar 2025 20:52:29 +0800 Subject: [PATCH] add reward system --- app/rewards/layout.tsx | 147 +++++++ app/rewards/page.tsx | 321 +++++++++++++++ components/navbar/navbar.tsx | 5 + components/ui/use-toast.tsx | 10 + components/web3-provider.tsx | 8 +- lib/contracts/CFCToke.json | 666 +++++++++++++++++++++++++++++++ lib/contracts/RewardToken.json | 339 ++++++++++++++++ lib/contracts/contract-config.ts | 30 ++ lib/hooks/useERC20Token.ts | 231 +++++++++++ lib/hooks/useRewardToken.ts | 114 ++++++ package-lock.json | 486 ++++++++++++---------- package.json | 3 +- tailwind.config.ts | 115 ++++-- 13 files changed, 2232 insertions(+), 243 deletions(-) create mode 100644 app/rewards/layout.tsx create mode 100644 app/rewards/page.tsx create mode 100644 components/ui/use-toast.tsx create mode 100644 lib/contracts/CFCToke.json create mode 100644 lib/contracts/RewardToken.json create mode 100644 lib/contracts/contract-config.ts create mode 100644 lib/hooks/useERC20Token.ts create mode 100644 lib/hooks/useRewardToken.ts diff --git a/app/rewards/layout.tsx b/app/rewards/layout.tsx new file mode 100644 index 0000000..78b09e9 --- /dev/null +++ b/app/rewards/layout.tsx @@ -0,0 +1,147 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useAppKit } from "@reown/appkit/react"; +import { useAppKitAccount } from "@reown/appkit/react"; +import { useChainId } from "wagmi"; +import { sepolia } from "wagmi/chains"; + +export default function RewardsLayout({ + children, +}: { + children: React.ReactNode; +}) { + const { open } = useAppKit(); + const { isConnected } = useAppKitAccount(); + const chainId = useChainId(); + const [showNetworkAlert, setShowNetworkAlert] = useState(false); + + // 检查网络是否是Sepolia + useEffect(() => { + // 只有当用户已连接钱包时才显示网络提示 + if (isConnected && chainId && chainId !== sepolia.id) { + setShowNetworkAlert(true); + } else { + setShowNetworkAlert(false); + } + }, [chainId, isConnected]); + + // 手动添加Sepolia网络 + const addSepoliaNetwork = async () => { + // 检查window.ethereum是否可用 + if (typeof window !== "undefined" && "ethereum" in window) { + try { + // 请求添加Sepolia测试网络 + await (window as any).ethereum.request({ + method: "wallet_addEthereumChain", + params: [ + { + chainId: "0xaa36a7", // 十六进制形式的链ID + chainName: "Sepolia Testnet", + nativeCurrency: { + name: "Sepolia ETH", + symbol: "ETH", + decimals: 18, + }, + rpcUrls: ["https://eth-sepolia.g.alchemy.com/v2/demo"], + blockExplorerUrls: ["https://sepolia.etherscan.io"], + }, + ], + }); + console.log("Sepolia网络添加成功"); + } catch (error) { + console.error("添加Sepolia网络失败:", error); + } + } else { + console.error( + "未检测到以太坊提供程序,请确保已安装MetaMask或其他兼容钱包" + ); + } + }; + + return ( +
+ {/* 网络提示 */} + {showNetworkAlert && ( +
+
+

+ 请切换到Sepolia测试网以访问奖励功能 +

+
+ + +
+
+ 如果添加失败,请尝试手动添加网络或刷新页面 +
+
+
+ )} + + {/* 背景装饰 - 动态粒子版本 */} +
+ {/* 主要背景渐变 */} +
+ + {/* 网格图案 - 区块链风格 */} +
+
+
+ + {/* 动态光晕效果 */} +
+
+ + {/* 右上角 - 代表代币/奖励的光效 */} +
+ + {/* 左下角 - 小粒子区域 */} +
+ + {/* 粒子动画区 - 左侧 */} +
+
+
+
+
+ + {/* 粒子动画区 - 右侧 */} +
+
+
+
+
+ + {/* 粒子连接线 - 模拟区块链节点连接 (使用绝对定位的伪元素模拟连接) */} +
+
+
+ + {/* 区块节点 - 大型节点 */} +
+
+
+
+
+
+
+
+
+
+ + {/* 内容区域 */} +
{children}
+
+ ); +} diff --git a/app/rewards/page.tsx b/app/rewards/page.tsx new file mode 100644 index 0000000..92a5952 --- /dev/null +++ b/app/rewards/page.tsx @@ -0,0 +1,321 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { useAppKitAccount } from "@reown/appkit/react"; +import { useAppKit } from "@reown/appkit/react"; +import { useRewardToken } from "@/lib/hooks/useRewardToken"; +import { useERC20Token } from "@/lib/hooks/useERC20Token"; +import { formatDistanceToNow } from "date-fns"; +import { zhCN } from "date-fns/locale"; +import { Coins, History, Wallet, ChevronRight, Gift, Clock, ExternalLink, Plus, Info } from "lucide-react"; +import { sepolia } from "wagmi/chains"; + +export default function RewardsPage() { + const { isConnected, address } = useAppKitAccount(); + const { open } = useAppKit(); + const { + availableRewards, + lastClaimTime, + claimRewards, + isLoading, + isSuccess, + errorMessage, + isSepoliaNetwork + } = useRewardToken(); + const { + balance, + } = useERC20Token(); + const [showSuccessMessage, setShowSuccessMessage] = useState(false); + const [showNetworkInfo, setShowNetworkInfo] = useState(false); + const [switchError, setSwitchError] = useState(null); + + // 处理成功领取后的提示 + useEffect(() => { + if (isSuccess) { + setShowSuccessMessage(true); + const timer = setTimeout(() => { + setShowSuccessMessage(false); + }, 3000); + + return () => clearTimeout(timer); + } + }, [isSuccess]); + + // 格式化上次领取时间 + const formattedLastClaimTime = lastClaimTime ? + formatDistanceToNow(new Date(lastClaimTime * 1000), { addSuffix: true, locale: zhCN }) : + "从未领取"; + + // 处理钱包连接 + const handleConnectWallet = () => { + open(); + }; + + // 处理网络切换 + const handleSwitchNetwork = () => { + setSwitchError(null); + open({ view: "Networks" }); + }; + + // 手动添加Sepolia网络 + const addSepoliaNetwork = async () => { + try { + setSwitchError(null); + if (typeof window !== 'undefined' && 'ethereum' in window) { + await (window as any).ethereum.request({ + method: 'wallet_addEthereumChain', + params: [ + { + chainId: '0xaa36a7', // 十六进制形式的链ID + chainName: 'Sepolia Testnet', + nativeCurrency: { + name: 'Sepolia ETH', + symbol: 'ETH', + decimals: 18, + }, + rpcUrls: ['https://endpoints.omniatech.io/v1/eth/sepolia/public'], + blockExplorerUrls: ['https://sepolia.etherscan.io'], + }, + ], + }); + console.log('Sepolia网络添加成功'); + } else { + setSwitchError("未检测到以太坊提供程序,请确保已安装MetaMask或其他兼容钱包"); + } + } catch (error) { + console.error('添加Sepolia网络失败:', error); + setSwitchError("添加网络失败,请尝试手动添加或刷新页面"); + } + }; + + // 渲染连接钱包按钮 + const renderConnectWalletPrompt = () => ( +
+ +

连接您的钱包

+

+ 请先连接您的钱包以查看和领取奖励代币 +

+ +
+ ); + + // 渲染网络切换提示 + const renderNetworkSwitchPrompt = () => ( +
+ + + +

请切换至 Sepolia 测试网

+

+ CFC 奖励仅在 Sepolia 测试网上可用,请切换您的钱包网络 +

+ + {/* 错误信息 */} + {switchError && ( +
+ {switchError} +
+ )} + +
+ + + +
+ + + + {showNetworkInfo && ( +
+

Sepolia测试网配置信息:

+
    +
  • 网络名称: Sepolia
  • +
  • RPC URL: https://eth-sepolia.g.alchemy.com/v2/demo
  • +
  • 链ID (十进制): 11155111
  • +
  • 链ID (十六进制): 0xaa36a7
  • +
  • 货币符号: ETH
  • +
  • 区块浏览器: https://sepolia.etherscan.io
  • +
+

复制上述信息到您的钱包App中手动添加网络

+
+ )} +
+ ); + + // 渲染奖励卡片 + const renderRewardsCard = () => ( +
+ {/* 当前余额卡片 */} +
+
+
+ +
+

当前余额

+
+
+
+ {balance} + CFC +
+
+
+
+ + 上次领取: +
+ {formattedLastClaimTime} +
+
+ + {/* 可用奖励卡片 */} +
+
+
+ +
+

可用奖励

+
+
+
+ {availableRewards} + CFC +
+
+ +
+
+ ); + + // 渲染合适的内容 + const renderContent = () => { + if (!isConnected) { + return renderConnectWalletPrompt(); + } else if (!isSepoliaNetwork) { + return renderNetworkSwitchPrompt(); + } else { + return renderRewardsCard(); + } + }; + + return ( +
+
+ {/* 标题部分 */} +
+

+ CFC 奖励中心 +

+

+ 参与社区活动和贡献代码,获取 CFC 代币奖励。这些代币可用于社区治理和解锁特殊功能。 +

+
+ + {/* 动态内容区域 */} + {renderContent()} + + {/* 错误信息 */} + {errorMessage && ( +
+ {errorMessage} +
+ )} + + {/* 成功信息 */} + {showSuccessMessage && ( +
+
+ + + +
+ 您已成功领取奖励代币! +
+ )} + + {/* 奖励说明 */} +
+

如何获取更多奖励

+
    +
  • +
    + 1 +
    +
    +

    参与 Hackathon 活动

    +

    根据项目完成情况和评分,获得相应的代币奖励。

    +
    +
  • +
  • +
    + 2 +
    +
    +

    贡献代码

    +

    向我们的开源项目提交PR,审核通过后获得代币奖励。

    +
    +
  • +
  • +
    + 3 +
    +
    +

    参与社区讨论

    +

    在社区中积极参与讨论,提供有价值的建议和反馈。

    +
    +
  • +
+
+
+
+ ); +} \ No newline at end of file diff --git a/components/navbar/navbar.tsx b/components/navbar/navbar.tsx index a87b410..50dded0 100644 --- a/components/navbar/navbar.tsx +++ b/components/navbar/navbar.tsx @@ -59,6 +59,11 @@ export function Navbar() { href: "/hackathon", icon: , }, + { + name: "Rewards", + href: "/rewards", + icon: , + }, // { name: "Contact", href: "/contact", icon: }, ]; diff --git a/components/ui/use-toast.tsx b/components/ui/use-toast.tsx new file mode 100644 index 0000000..ae30f8c --- /dev/null +++ b/components/ui/use-toast.tsx @@ -0,0 +1,10 @@ +import { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from "./toast" +import { useToast as useToastOriginal } from "./use-toast-hook" + +export const ToastProvider = ToastProvider +export const ToastViewport = ToastViewport +export const Toast = Toast +export const ToastTitle = ToastTitle +export const ToastDescription = ToastDescription +export const ToastClose = ToastClose +export const useToast = useToastOriginal \ No newline at end of file diff --git a/components/web3-provider.tsx b/components/web3-provider.tsx index e41db4d..fc2617a 100644 --- a/components/web3-provider.tsx +++ b/components/web3-provider.tsx @@ -9,6 +9,7 @@ import { base, polygon, solana, + sepolia, } from "@reown/appkit/networks"; import { SolflareWalletAdapter, @@ -27,7 +28,7 @@ if (!projectId) { throw new Error("Project ID is not defined"); } -export const networks = [mainnet, arbitrum, polygon, base, solana]; +export const networks = [mainnet, arbitrum, polygon, base, solana, sepolia]; //Set up the Wagmi Adapter (Config) export const wagmiAdapter = new WagmiAdapter({ @@ -50,6 +51,9 @@ export const wagmiAdapter = new WagmiAdapter({ [base.id]: http( "https://base-mainnet.g.alchemy.com/v2/uGsEZP9zr3DFh1rGiFj4safeFvFfC7B0" ), + [sepolia.id]: http( + "https://endpoints.omniatech.io/v1/eth/sepolia/public" + ), }, }); @@ -78,7 +82,7 @@ const metadata = { const modal = createAppKit({ adapters: [wagmiAdapter, solanaWeb3JsAdapter], projectId, - networks: [mainnet, arbitrum, base, polygon, solana], + networks: [mainnet, arbitrum, base, polygon, solana, sepolia], defaultNetwork: mainnet, metadata: metadata, enableWalletConnect: false, diff --git a/lib/contracts/CFCToke.json b/lib/contracts/CFCToke.json new file mode 100644 index 0000000..a876a89 --- /dev/null +++ b/lib/contracts/CFCToke.json @@ -0,0 +1,666 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ECDSAInvalidSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "name": "ECDSAInvalidSignatureLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "ECDSAInvalidSignatureS", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "ERC2612ExpiredSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC2612InvalidSigner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "currentNonce", + "type": "uint256" + } + ], + "name": "InvalidAccountNonce", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidShortString", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "str", + "type": "string" + } + ], + "name": "StringTooLong", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EIP712DomainChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approveMinter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eip712Domain", + "outputs": [ + { + "internalType": "bytes1", + "name": "fields", + "type": "bytes1" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "verifyingContract", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256[]", + "name": "extensions", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/lib/contracts/RewardToken.json b/lib/contracts/RewardToken.json new file mode 100644 index 0000000..ed2bb2c --- /dev/null +++ b/lib/contracts/RewardToken.json @@ -0,0 +1,339 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "addReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "users", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "batchAddRewards", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "claimRewards", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "depositTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "emergencyWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "RewardAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "RewardClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newTokenContract", + "type": "address" + } + ], + "name": "TokenContractUpdated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newTokenAddress", + "type": "address" + } + ], + "name": "updateTokenContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "availableRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "canClaim", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cfcToken", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CLAIM_COOLDOWN", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "lastClaimTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "timeUntilNextClaim", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/lib/contracts/contract-config.ts b/lib/contracts/contract-config.ts new file mode 100644 index 0000000..106c46f --- /dev/null +++ b/lib/contracts/contract-config.ts @@ -0,0 +1,30 @@ +import { mainnet, sepolia } from "wagmi/chains"; +import { Address } from "viem"; + +interface ContractConfig { + address: Record; + name: string; +} + +export const REWARD_TOKEN_CONTRACT: ContractConfig = { + name: "CrazyForCode Token", + address: { + [mainnet.id]: "0x1234567890123456789012345678901234567890", // 主网测试地址,实际部署时更换 + [sepolia.id]: "0x3e61503D6504F5f72Fc46a04229048e96d5F9089", // Sepolia测试网地址,实际部署时更换 + }, +}; + +export const CFC_TOKEN_CONTRACT: ContractConfig = { + name: "CrazyForCode Token", + address: { + [mainnet.id]: "0x1234567890123456789012345678901234567890", // 主网测试地址,实际部署时更换 + [sepolia.id]: "0x7c0de9efd6edf521d71bbd4085728b8771a3f675", // Sepolia测试网地址,实际部署时更换 + }, + }; + +export function getContractAddress( + contract: ContractConfig, + chainId: number +): Address | undefined { + return contract.address[chainId]; +} \ No newline at end of file diff --git a/lib/hooks/useERC20Token.ts b/lib/hooks/useERC20Token.ts new file mode 100644 index 0000000..9c74002 --- /dev/null +++ b/lib/hooks/useERC20Token.ts @@ -0,0 +1,231 @@ +import { useReadContract, useWriteContract, useWaitForTransactionReceipt, useChainId } from "wagmi"; +import { formatUnits, parseUnits } from "viem"; +import { useState, useEffect } from "react"; +import { useAppKitAccount } from "@reown/appkit/react"; +import { sepolia } from "wagmi/chains"; +import ERC20_ABI from "../contracts/CFCToke.json"; +import { CFC_TOKEN_CONTRACT, getContractAddress } from "../contracts/contract-config"; + + +/** + * 使用ERC20代币的钩子函数 + * @param contractAddress ERC20代币合约地址 + * @returns 代币相关函数和状态 + */ +export function useERC20Token() { + const { address, isConnected } = useAppKitAccount(); + const chainId = useChainId(); + const [isLoading, setIsLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + const [tokenInfo, setTokenInfo] = useState<{ + name: string | undefined; + symbol: string | undefined; + decimals: number; + }>({ + name: undefined, + symbol: undefined, + decimals: 18, + }); + + // 检查网络是否是Sepolia + const isSepoliaNetwork = chainId === sepolia.id; + + // 获取合约地址 + const contractAddress = isSepoliaNetwork + ? getContractAddress(CFC_TOKEN_CONTRACT, sepolia.id) + : undefined; + + // 读取代币名称 + const { data: tokenName } = useReadContract({ + address: contractAddress, + abi: ERC20_ABI, + functionName: "name", + query: { + enabled: Boolean(isConnected && contractAddress && isSepoliaNetwork), + }, + }); + + // 读取代币符号 + const { data: tokenSymbol } = useReadContract({ + address: contractAddress, + abi: ERC20_ABI, + functionName: "symbol", + query: { + enabled: Boolean(isConnected && contractAddress && isSepoliaNetwork), + }, + }); + + // 读取代币精度 + const { data: tokenDecimals } = useReadContract({ + address: contractAddress, + abi: ERC20_ABI, + functionName: "decimals", + query: { + enabled: Boolean(isConnected && contractAddress && isSepoliaNetwork), + }, + }); + + // 更新代币信息 + useEffect(() => { + if (tokenName || tokenSymbol || tokenDecimals !== undefined) { + setTokenInfo({ + name: tokenName as string | undefined, + symbol: tokenSymbol as string | undefined, + decimals: tokenDecimals !== undefined ? Number(tokenDecimals) : 18, + }); + } + }, [tokenName, tokenSymbol, tokenDecimals]); + + // 读取用户余额 + const { data: balance, refetch: refetchBalance } = useReadContract({ + address: contractAddress, + abi: ERC20_ABI, + functionName: "balanceOf", + args: address ? [address] : undefined, + query: { + enabled: Boolean(isConnected && address && contractAddress && isSepoliaNetwork), + staleTime: 10_000, + }, + }); + + // 读取授权额度 + const { data: allowanceData, refetch: refetchAllowance } = useReadContract({ + address: contractAddress, + abi: ERC20_ABI, + functionName: "allowance", + args: address && contractAddress ? [address, contractAddress] : undefined, + query: { + enabled: Boolean(isConnected && address && contractAddress && isSepoliaNetwork), + staleTime: 10_000, + }, + }); + + // 转账相关 + const { writeContract, data: transferHash, isPending: isTransferPending } = useWriteContract(); + + // 授权相关 + const { writeContract: writeApprove, data: approveHash, isPending: isApprovePending } = useWriteContract(); + + // 等待交易完成 + const { isLoading: isTransferConfirming, isSuccess: isTransferSuccess } = useWaitForTransactionReceipt({ + hash: transferHash, + }); + + const { isLoading: isApproveConfirming, isSuccess: isApproveSuccess } = useWaitForTransactionReceipt({ + hash: approveHash, + }); + + // 转账函数 + const transfer = async (to: `0x${string}`, amount: string) => { + if (!isConnected || !address) { + setErrorMessage("请先连接您的钱包"); + return; + } + + if (!isSepoliaNetwork) { + setErrorMessage("请切换到Sepolia测试网"); + return; + } + + if (!contractAddress) { + setErrorMessage("合约地址无效"); + return; + } + + try { + setIsLoading(true); + setErrorMessage(null); + + // 将金额转换为合约所需的格式,考虑代币精度 + const amountInWei = parseUnits(amount, tokenInfo.decimals); + + writeContract({ + address: contractAddress, + abi: ERC20_ABI, + functionName: "transfer", + args: [to, amountInWei], + }); + } catch (error) { + console.error("转账失败:", error); + setErrorMessage("转账失败,请稍后再试"); + setIsLoading(false); + } + }; + + // 授权函数 + const approve = async (spender: `0x${string}`, amount: string) => { + if (!isConnected || !address) { + setErrorMessage("请先连接您的钱包"); + return; + } + + if (!isSepoliaNetwork) { + setErrorMessage("请切换到Sepolia测试网"); + return; + } + + if (!contractAddress) { + setErrorMessage("合约地址无效"); + return; + } + + try { + setIsLoading(true); + setErrorMessage(null); + + // 将金额转换为合约所需的格式,考虑代币精度 + const amountInWei = parseUnits(amount, tokenInfo.decimals); + + writeApprove({ + address: contractAddress, + abi: ERC20_ABI, + functionName: "approve", + args: [spender, amountInWei], + }); + } catch (error) { + console.error("授权失败:", error); + setErrorMessage("授权失败,请稍后再试"); + setIsLoading(false); + } + }; + + // 监听交易状态 + useEffect(() => { + if (isTransferSuccess || isApproveSuccess) { + // 刷新数据 + refetchBalance(); + refetchAllowance(); + setIsLoading(false); + } + }, [isTransferSuccess, isApproveSuccess, refetchBalance, refetchAllowance]); + + // 格式化代币金额 + const formatTokenAmount = (amount: bigint | undefined) => { + if (!amount) return "0"; + return formatUnits(amount, tokenInfo.decimals); + }; + + return { + // 代币信息 + tokenName: tokenInfo.name, + tokenSymbol: tokenInfo.symbol, + tokenDecimals: tokenInfo.decimals, + + // 余额 + balance: balance ? formatTokenAmount(balance as bigint) : "0", + allowance: allowanceData ? formatTokenAmount(allowanceData as bigint) : "0", + + // 操作函数 + transfer, + approve, + refetchBalance, + refetchAllowance, + + // 状态 + isLoading: isLoading || isTransferPending || isTransferConfirming || isApprovePending || isApproveConfirming, + isTransferSuccess, + isApproveSuccess, + errorMessage, + isSepoliaNetwork, + }; +} \ No newline at end of file diff --git a/lib/hooks/useRewardToken.ts b/lib/hooks/useRewardToken.ts new file mode 100644 index 0000000..2355871 --- /dev/null +++ b/lib/hooks/useRewardToken.ts @@ -0,0 +1,114 @@ +import { useReadContract, useWriteContract, useWaitForTransactionReceipt, useChainId } from "wagmi"; +import { formatUnits } from "viem"; +import { useState, useEffect } from "react"; +import { useAppKitAccount } from "@reown/appkit/react"; +import { REWARD_TOKEN_CONTRACT, getContractAddress } from "../contracts/contract-config"; +import RewardTokenABI from "../contracts/RewardToken.json"; +import { sepolia } from "wagmi/chains"; + +export function useRewardToken() { + const { address, isConnected } = useAppKitAccount(); + const [isLoading, setIsLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + + // 获取当前链ID + const currentChainId = useChainId(); + + // 检查是否在Sepolia测试网上 + const isSepoliaNetwork = currentChainId === sepolia.id; + + // 获取合约地址 + const contractAddress = isSepoliaNetwork + ? getContractAddress(REWARD_TOKEN_CONTRACT, sepolia.id) + : undefined; + + // 读取可用奖励数量 + const { data: availableRewards, refetch: refetchAvailableRewards } = useReadContract({ + address: contractAddress, + abi: RewardTokenABI, + functionName: "availableRewards", + args: address ? [address] : undefined, + query: { + enabled: Boolean(isConnected && address && contractAddress && isSepoliaNetwork), + staleTime: 10_000, + } + }); + + // 读取上次领取时间 + const { data: lastClaimTime, refetch: refetchLastClaimTime } = useReadContract({ + address: contractAddress, + abi: RewardTokenABI, + functionName: "lastClaimTime", + args: address ? [address] : undefined, + query: { + enabled: Boolean(isConnected && address && contractAddress && isSepoliaNetwork), + staleTime: 10_000, + } + }); + + // 领取奖励 + const { writeContract, data: hash, isPending } = useWriteContract(); + + // 等待交易完成 + const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ + hash, + }); + + // 处理领取奖励 + const claimRewards = async () => { + if (!isConnected || !address) { + setErrorMessage("请先连接您的钱包"); + return; + } + + if (!isSepoliaNetwork) { + setErrorMessage("请切换到Sepolia测试网"); + return; + } + + if (!contractAddress) { + setErrorMessage("合约地址未配置"); + return; + } + + try { + setIsLoading(true); + setErrorMessage(null); + writeContract({ + address: contractAddress, + abi: RewardTokenABI, + functionName: "claimRewards", + }); + } catch (error) { + console.error("领取奖励失败:", error); + setErrorMessage("领取奖励时出现错误,请稍后再试"); + setIsLoading(false); + } + }; + + // 监听交易状态 + useEffect(() => { + if (isSuccess) { + // 刷新数据 + refetchAvailableRewards(); + refetchLastClaimTime(); + setIsLoading(false); + } + }, [isSuccess, refetchAvailableRewards, refetchLastClaimTime]); + + // 格式化代币金额 + const formatTokenAmount = (amount: bigint | undefined, decimals = 18) => { + if (!amount) return "0"; + return formatUnits(amount, decimals); + }; + + return { + availableRewards: availableRewards ? formatTokenAmount(availableRewards as bigint) : "0", + lastClaimTime: lastClaimTime ? Number(lastClaimTime) : 0, + claimRewards, + isLoading: isLoading || isPending || isConfirming, + isSuccess, + errorMessage, + isSepoliaNetwork, + }; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d33542a..9df066e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,10 +30,11 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "convex": "^1.19.2", + "date-fns": "^4.1.0", "lucide-react": "^0.469.0", "mini-svg-data-uri": "^1.4.4", "motion": "^11.18.2", - "next": "15.1.3", + "next": "^15.2.4", "next-mdx-remote": "^5.0.0", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -363,24 +364,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -469,9 +472,10 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -480,13 +484,14 @@ } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" @@ -518,9 +523,10 @@ } }, "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -589,12 +595,13 @@ "integrity": "sha512-IlVABlRgo9XaTR1NunwZpWcxnfEv04ba2l1vkUz4S1W7Jt36F4CtffP+jPeqBZGnAe+fnUwo0XjIJC3ZTNToNQ==" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "aix" @@ -604,12 +611,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -619,12 +627,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -634,12 +643,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -649,12 +659,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -664,12 +675,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -679,12 +691,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -694,12 +707,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -709,12 +723,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -724,12 +739,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -739,12 +755,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -754,12 +771,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", "cpu": [ "loong64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -769,12 +787,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", "cpu": [ "mips64el" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -784,12 +803,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -799,12 +819,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", "cpu": [ "riscv64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -814,12 +835,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", "cpu": [ "s390x" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -829,12 +851,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -843,13 +866,30 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -859,12 +899,13 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -874,12 +915,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -889,12 +931,13 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "sunos" @@ -904,12 +947,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -919,12 +963,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -934,12 +979,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1169,9 +1215,9 @@ } }, "node_modules/@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", + "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", "funding": [ { "type": "individual", @@ -1182,8 +1228,9 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], + "license": "MIT", "dependencies": { - "@ethersproject/logger": "^5.7.0" + "@ethersproject/logger": "^5.8.0" } }, "node_modules/@ethersproject/constants": { @@ -1224,9 +1271,9 @@ } }, "node_modules/@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", + "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", "funding": [ { "type": "individual", @@ -1236,12 +1283,13 @@ "type": "individual", "url": "https://www.buymeacoffee.com/ricmoo" } - ] + ], + "license": "MIT" }, "node_modules/@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", + "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", "funding": [ { "type": "individual", @@ -1252,8 +1300,9 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], + "license": "MIT", "dependencies": { - "@ethersproject/logger": "^5.7.0" + "@ethersproject/logger": "^5.8.0" } }, "node_modules/@ethersproject/rlp": { @@ -1276,9 +1325,9 @@ } }, "node_modules/@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", + "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", "funding": [ { "type": "individual", @@ -1289,34 +1338,16 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], + "license": "MIT", "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", "bn.js": "^5.2.1", - "elliptic": "6.5.4", + "elliptic": "6.6.1", "hash.js": "1.1.7" } }, - "node_modules/@ethersproject/signing-key/node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/@ethersproject/signing-key/node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==" - }, "node_modules/@ethersproject/transactions": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", @@ -2485,6 +2516,22 @@ "socket.io-client": "^4.5.1" } }, + "node_modules/@metamask/sdk-communication-layer/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/@metamask/sdk-install-modal-web": { "version": "0.32.0", "resolved": "https://registry.npmjs.org/@metamask/sdk-install-modal-web/-/sdk-install-modal-web-0.32.0.tgz", @@ -2618,9 +2665,10 @@ } }, "node_modules/@next/env": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.3.tgz", - "integrity": "sha512-Q1tXwQCGWyA3ehMph3VO+E6xFPHDKdHFYosadt0F78EObYxPio0S09H9UGYznDe6Wc8eLKLG89GqcFJJDiK5xw==" + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", + "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==", + "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { "version": "15.1.3", @@ -2632,12 +2680,13 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.3.tgz", - "integrity": "sha512-aZtmIh8jU89DZahXQt1La0f2EMPt/i7W+rG1sLtYJERsP7GRnNFghsciFpQcKHcGh4dUiyTB5C1X3Dde/Gw8gg==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.4.tgz", + "integrity": "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2647,12 +2696,13 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.3.tgz", - "integrity": "sha512-aw8901rjkVBK5mbq5oV32IqkJg+CQa6aULNlN8zyCWSsePzEG3kpDkAFkkTOh3eJ0p95KbkLyWBzslQKamXsLA==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.4.tgz", + "integrity": "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2662,12 +2712,13 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.3.tgz", - "integrity": "sha512-YbdaYjyHa4fPK4GR4k2XgXV0p8vbU1SZh7vv6El4bl9N+ZSiMfbmqCuCuNU1Z4ebJMumafaz6UCC2zaJCsdzjw==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.4.tgz", + "integrity": "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -2677,12 +2728,13 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.3.tgz", - "integrity": "sha512-qgH/aRj2xcr4BouwKG3XdqNu33SDadqbkqB6KaZZkozar857upxKakbRllpqZgWl/NDeSCBYPmUAZPBHZpbA0w==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.4.tgz", + "integrity": "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -2692,12 +2744,13 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.3.tgz", - "integrity": "sha512-uzafnTFwZCPN499fNVnS2xFME8WLC9y7PLRs/yqz5lz1X/ySoxfaK2Hbz74zYUdEg+iDZPd8KlsWaw9HKkLEVw==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.4.tgz", + "integrity": "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -2707,12 +2760,13 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.3.tgz", - "integrity": "sha512-el6GUFi4SiDYnMTTlJJFMU+GHvw0UIFnffP1qhurrN1qJV3BqaSRUjkDUgVV44T6zpw1Lc6u+yn0puDKHs+Sbw==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.4.tgz", + "integrity": "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -2722,12 +2776,13 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.3.tgz", - "integrity": "sha512-6RxKjvnvVMM89giYGI1qye9ODsBQpHSHVo8vqA8xGhmRPZHDQUE4jcDbhBwK0GnFMqBnu+XMg3nYukNkmLOLWw==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.4.tgz", + "integrity": "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -2737,12 +2792,13 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.3.tgz", - "integrity": "sha512-VId/f5blObG7IodwC5Grf+aYP0O8Saz1/aeU3YcWqNdIUAmFQY3VEPKPaIzfv32F/clvanOb2K2BR5DtDs6XyQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.4.tgz", + "integrity": "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -8993,9 +9049,10 @@ } }, "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -10036,11 +10093,12 @@ "peer": true }, "node_modules/convex": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/convex/-/convex-1.19.2.tgz", - "integrity": "sha512-rwhg5uVxFKVWf9zM8+QmpQ0MONE6FfeRoy8I/F2lZ7MIjtZyHVaIqpzmQ5ZvZFduseuBMrNy/Y9H4Olq1FlazQ==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/convex/-/convex-1.21.0.tgz", + "integrity": "sha512-+peYmXSWsnnyASKI302RIlsf07gv6uHAOduBwDFLBjNZciO+kOVjQcskJwhTeNbAV59glL+tf7kvDh8PEnhmCw==", + "license": "Apache-2.0", "dependencies": { - "esbuild": "0.23.0", + "esbuild": "0.25.1", "jwt-decode": "^3.1.2", "prettier": "3.4.2" }, @@ -10316,18 +10374,13 @@ } }, "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/dayjs": { @@ -10947,10 +11000,11 @@ } }, "node_modules/esbuild": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -10958,30 +11012,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" } }, "node_modules/escalade": { @@ -14693,11 +14748,12 @@ "dev": true }, "node_modules/next": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/next/-/next-15.1.3.tgz", - "integrity": "sha512-5igmb8N8AEhWDYzogcJvtcRDU6n4cMGtBklxKD4biYv4LXN8+awc/bbQ2IM2NQHdVPgJ6XumYXfo3hBtErg1DA==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.2.4.tgz", + "integrity": "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==", + "license": "MIT", "dependencies": { - "@next/env": "15.1.3", + "@next/env": "15.2.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", @@ -14712,14 +14768,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.1.3", - "@next/swc-darwin-x64": "15.1.3", - "@next/swc-linux-arm64-gnu": "15.1.3", - "@next/swc-linux-arm64-musl": "15.1.3", - "@next/swc-linux-x64-gnu": "15.1.3", - "@next/swc-linux-x64-musl": "15.1.3", - "@next/swc-win32-arm64-msvc": "15.1.3", - "@next/swc-win32-x64-msvc": "15.1.3", + "@next/swc-darwin-arm64": "15.2.4", + "@next/swc-darwin-x64": "15.2.4", + "@next/swc-linux-arm64-gnu": "15.2.4", + "@next/swc-linux-arm64-musl": "15.2.4", + "@next/swc-linux-x64-gnu": "15.2.4", + "@next/swc-linux-x64-musl": "15.2.4", + "@next/swc-win32-arm64-msvc": "15.2.4", + "@next/swc-win32-x64-msvc": "15.2.4", "sharp": "^0.33.5" }, "peerDependencies": { diff --git a/package.json b/package.json index 73e5e64..167d066 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,11 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "convex": "^1.19.2", + "date-fns": "^4.1.0", "lucide-react": "^0.469.0", "mini-svg-data-uri": "^1.4.4", "motion": "^11.18.2", - "next": "15.1.3", + "next": "^15.2.4", "next-mdx-remote": "^5.0.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/tailwind.config.ts b/tailwind.config.ts index a820074..4bc576f 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,5 +1,6 @@ import type { Config } from "tailwindcss"; import flattenColorPalette from "tailwindcss/lib/util/flattenColorPalette"; +import plugin from "tailwindcss/plugin"; export default { darkMode: ["class"], @@ -10,6 +11,85 @@ export default { ], theme: { extend: { + animation: { + // 粒子相关动画 + 'pulse-slow': 'pulse 8s cubic-bezier(0.4, 0, 0.6, 1) infinite', + 'float': 'float 10s ease-in-out infinite', + 'particle-1': 'particle1 15s ease-in-out infinite', + 'particle-2': 'particle2 20s ease-in-out infinite', + 'particle-3': 'particle3 18s ease-in-out infinite', + 'particle-4': 'particle4 25s ease-in-out infinite', + 'fade-in-out': 'fadeInOut 8s ease-in-out infinite', + 'rotate-slow': 'rotateSlow 12s linear infinite', + + // 其他动画 + "meteor-effect": "meteor 5s linear infinite", + "float-slow": "float 8s ease-in-out infinite", + "float-slower": "float 10s ease-in-out infinite", + "neon-sparkle": "neon-sparkle 0.6s cubic-bezier(0.4, 0, 0.6, 1)", + + }, + keyframes: { + // 粒子相关关键帧 + float: { + '0%, 100%': { transform: 'translateY(0)' }, + '50%': { transform: 'translateY(-10px)' }, + }, + particle1: { + '0%, 100%': { transform: 'translate(0, 0)' }, + '25%': { transform: 'translate(20px, -15px)' }, + '50%': { transform: 'translate(5px, 20px)' }, + '75%': { transform: 'translate(-15px, 5px)' }, + }, + particle2: { + '0%, 100%': { transform: 'translate(0, 0)' }, + '25%': { transform: 'translate(-15px, 10px)' }, + '50%': { transform: 'translate(10px, -20px)' }, + '75%': { transform: 'translate(20px, 10px)' }, + }, + particle3: { + '0%, 100%': { transform: 'translate(0, 0)' }, + '25%': { transform: 'translate(15px, 20px)' }, + '50%': { transform: 'translate(-10px, -15px)' }, + '75%': { transform: 'translate(-20px, 10px)' }, + }, + particle4: { + '0%, 100%': { transform: 'translate(0, 0)' }, + '20%': { transform: 'translate(-20px, -10px)' }, + '40%': { transform: 'translate(15px, 10px)' }, + '60%': { transform: 'translate(10px, -15px)' }, + '80%': { transform: 'translate(-15px, 15px)' }, + }, + fadeInOut: { + '0%, 100%': { opacity: '0' }, + '50%': { opacity: '1' }, + }, + rotateSlow: { + '0%': { transform: 'rotate(0deg)' }, + '100%': { transform: 'rotate(360deg)' }, + }, + + // 其他关键帧 + meteor: { + "0%": { transform: "rotate(215deg) translateX(0)", opacity: "1" }, + "70%": { opacity: "1" }, + "100%": { + transform: "rotate(215deg) translateX(-500px)", + opacity: "0", + }, + }, + "neon-sparkle": { + "0%, 100%": { transform: "translateY(0) scale(1)" }, + "50%": { transform: "translateY(200%) scale(1.5)" }, + }, + }, + // 动画延迟类 + animationDelay: { + '1000': '1000ms', + '1500': '1500ms', + '2000': '2000ms', + '3000': '3000ms', + }, colors: { border: "hsl(var(--border))", input: "hsl(var(--input))", @@ -224,31 +304,6 @@ export default { xs: "calc(var(--radius) - 8px)", xxs: "calc(var(--radius) - 10px)", }, - animation: { - "meteor-effect": "meteor 5s linear infinite", - float: "float 6s ease-in-out infinite", - "float-slow": "float 8s ease-in-out infinite", - "float-slower": "float 10s ease-in-out infinite", - "neon-sparkle": "neon-sparkle 0.6s cubic-bezier(0.4, 0, 0.6, 1)", - }, - keyframes: { - meteor: { - "0%": { transform: "rotate(215deg) translateX(0)", opacity: "1" }, - "70%": { opacity: "1" }, - "100%": { - transform: "rotate(215deg) translateX(-500px)", - opacity: "0", - }, - }, - float: { - "0%, 100%": { transform: "translateY(0)" }, - "50%": { transform: "translateY(-20px)" }, - }, - "neon-sparkle": { - "0%, 100%": { transform: "translateY(0) scale(1)" }, - "50%": { transform: "translateY(200%) scale(1.5)" }, - }, - }, fontFamily: { mono: ["'Space Mono'", "monospace"], retro: ["'VT323'", "monospace"], @@ -380,6 +435,16 @@ export default { require("tailwindcss-animate"), require("@tailwindcss/typography"), addVariablesForColors, + plugin(({ matchUtilities, theme }) => { + matchUtilities( + { + 'animation-delay': (value) => ({ + animationDelay: value, + }), + }, + { values: theme('animationDelay') } + ); + }), ], } satisfies Config;