开发者指南

代码示例 v1.1

Microcosm 代码示例

版本: v1.1 | 日期: 2026-02-15

配套文档: Microcosm-开发者接入指南.md | Microcosm-API参考手册.md


目录

  1. Next.js 完整接入(4 个文件)
  2. 认证 Hooks
  3. 资产数据 Hooks
  4. 挖矿 & 轮回 Hooks
  5. 领地 & 生态 Hooks
  6. 图表 & 聚合 Hooks
  7. 通用 API 查询
  8. 菜单嵌入
  9. 路由守卫
  10. 纯后端调用(无 SDK)
  11. 钱包管理
  12. 领地管理(完整)
  13. 投票系统
  14. 拍卖增强
  15. 回购管理
  16. 挖矿增强
  17. MCD 详情
  18. 统计数据
  19. 挖矿执行(写操作)
  20. 回购执行(写操作)
  21. 领地编辑(写操作)
  22. 拍卖出价/取消(写操作)
  23. 投票操作(写操作)
  24. MCC 综合历史
  25. 链上交易构建指南

1. Next.js 完整接入

1.1 安装

bash
npm install @microcosmmoney/auth-core @microcosmmoney/auth-react @microcosmmoney/portal-react

1.2 后端: Token Exchange

typescript
// app/api/auth/exchange/route.ts
import { createTokenExchangeHandler } from '@microcosmmoney/auth-react/server'

export const POST = createTokenExchangeHandler({
  clientId: process.env.OAUTH_CLIENT_ID!,
  clientSecret: process.env.OAUTH_CLIENT_SECRET!,
})

1.3 前端: Provider

tsx
// app/providers.tsx
'use client'
import { MicrocosmAuthProvider } from '@microcosmmoney/auth-react'

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <MicrocosmAuthProvider
      clientId={process.env.NEXT_PUBLIC_OAUTH_CLIENT_ID!}
      redirectUri="/auth/callback"
    >
      {children}
    </MicrocosmAuthProvider>
  )
}

app/layout.tsx 中使用:

tsx
// app/layout.tsx
import { Providers } from './providers'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  )
}

1.4 回调页

tsx
// app/auth/callback/page.tsx
import { AuthCallback } from '@microcosmmoney/auth-react'

export default function CallbackPage() {
  return <AuthCallback redirectTo="/dashboard" />
}

1.5 环境变量

bash
# .env.local
NEXT_PUBLIC_OAUTH_CLIENT_ID=your_client_id   # 前端用 (浏览器可见)
OAUTH_CLIENT_ID=your_client_id               # 后端用 (Token Exchange 必须)
OAUTH_CLIENT_SECRET=your_client_secret       # 后端用 (Token Exchange 必须)

重要: 后端 OAUTH_CLIENT_IDOAUTH_CLIENT_SECRET 缺少任一,首次请求时会返回 500 错误(server_configuration_error)。SDK ≥1.0.2 不会在构建时抛出异常,兼容 next build


2. 认证 Hooks

2.1 useAuth — 登录/登出

tsx
import { useAuth } from '@microcosmmoney/auth-react'

function LoginButton() {
  const { user, isAuthenticated, isLoading, login, logout } = useAuth()

  if (isLoading) return <div>Loading...</div>

  if (!isAuthenticated) {
    return <button onClick={() => login()}>Login with Microcosm</button>
  }

  return (
    <div>
      <p>Welcome, {user?.displayName || user?.email}</p>
      <p>Role: {user?.role} | Level: {user?.level}</p>
      <button onClick={logout}>Logout</button>
    </div>
  )
}

useAuth 返回值:

typescript
{
  user: User | null              // 用户信息
  isAuthenticated: boolean       // 是否已登录
  isLoading: boolean             // 初始化中
  error: Error | null            // 错误
  login: (options?) => void      // 触发登录
  logout: () => Promise<void>    // 登出
  getAccessToken: () => Promise<string | null>  // 获取 Token
  client: MicrocosmAuthClient    // 底层客户端
}

2.2 useProfile — 资料管理

tsx
import { useProfile } from '@microcosmmoney/auth-react'

function ProfileEditor() {
  const { profile, loading, updateProfile, uploadAvatar } = useProfile()

  async function handleNameChange() {
    await updateProfile({ display_name: 'New Name' })
  }

  async function handleAvatarUpload(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0]
    if (file) {
      const url = await uploadAvatar(file)
      console.log('Avatar uploaded:', url)
    }
  }

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <img src={profile?.avatarUrl || '/default-avatar.png'} alt="Avatar" />
      <p>{profile?.displayName}</p>
      <p>{profile?.email}</p>
      <button onClick={handleNameChange}>Change Name</button>
      <input type="file" accept="image/*" onChange={handleAvatarUpload} />
    </div>
  )
}

useProfile 返回值:

typescript
{
  profile: User | null
  loading: boolean
  error: Error | null
  updateProfile: (data: { display_name?: string }) => Promise<void>
  uploadAvatar: (file: File) => Promise<string | null>
  refresh: () => Promise<void>
}

3. 资产数据 Hooks

3.1 useMCC — MCC 余额 + 价格

tsx
import { useMCC } from '@microcosmmoney/auth-react'

function MCCWallet() {
  const { balance, price, loading, refresh } = useMCC()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <h3>MCC Balance</h3>
      <p>Balance: {balance?.balance} MCC</p>
      <p>Raw: {balance?.raw_balance} (9 decimals)</p>
      {balance?.wallet_address && (
        <p>Wallet: {balance.wallet_address}</p>
      )}
      {balance?.wallets?.map(w => (
        <p key={w.wallet_address}>{w.wallet_address}: {w.balance} MCC {w.is_primary ? '(primary)' : ''}</p>
      ))}

      <h3>MCC Price</h3>
      <p>${price?.price?.toFixed(6)}</p>
      <p>Buyback: ${price?.buyback_price?.toFixed(6)}</p>
      <p>Source: {price?.source}</p>

      <button onClick={refresh}>Refresh</button>
    </div>
  )
}

useMCC 返回值:

typescript
{
  balance: {
    balance: number
    raw_balance: number
    decimals: number
    symbol: string
    wallet_address?: string | null
    wallets?: MCCWalletBalance[]
    mint?: string
  } | null
  price: MCCPrice | null
  loading: boolean
  error: Error | null
  refresh: () => Promise<void>
}

3.2 useMCD — MCD 余额

tsx
import { useMCD } from '@microcosmmoney/auth-react'

function MCDBalance() {
  const { balance, loading } = useMCD()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      {/* MCDBalance 字段是 string 类型,需 parseFloat 参与计算 */}
      <p>Total MCD: {balance?.total_balance}</p>
      <p>Available: {balance?.available_balance}</p>
      <p>Frozen: {balance?.frozen_balance}</p>
    </div>
  )
}

3.3 useMCCPrice — 实时价格(无需登录)

tsx
import { useMCCPrice } from '@microcosmmoney/auth-react'

function PriceTicker() {
  // 默认 30 秒刷新,可自定义
  const { data: price, loading } = useMCCPrice({ refetchInterval: 10_000 })

  if (loading) return <span>--</span>

  return <span>${price?.price?.toFixed(6)}</span>
}

3.4 useMCCStats / useMCDStats — 全局统计(无需登录)

tsx
import { useMCCStats, useMCDStats } from '@microcosmmoney/auth-react'

function TokenStats() {
  const { data: mccStats } = useMCCStats()  // 5 分钟缓存
  const { data: mcdStats } = useMCDStats()

  return (
    <div>
      <h3>MCC Stats</h3>
      <pre>{JSON.stringify(mccStats, null, 2)}</pre>

      <h3>MCD Stats</h3>
      <pre>{JSON.stringify(mcdStats, null, 2)}</pre>
    </div>
  )
}

4. 挖矿 & 轮回 Hooks

4.1 useMiningStats — 用户挖矿统计

tsx
import { useMiningStats } from '@microcosmmoney/auth-react'

function MiningDashboard() {
  const { data: stats, loading } = useMiningStats()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <h3>Mining Stats</h3>
      <p>Total Mined: {stats?.total_mined?.toFixed(2)} MCC</p>
      <p>Total Paid: ${stats?.total_paid?.toFixed(2)}</p>
      <p>Mining Count: {stats?.mining_count}</p>
      <p>Today: {stats?.today_mined?.toFixed(2)} MCC</p>
      <p>Last 30d: {stats?.last_30d_mined?.toFixed(2)} MCC</p>
      <p>Active Days (30d): {stats?.active_days_30d}</p>
      <p>Last Mined: {stats?.last_mined_at ? new Date(stats.last_mined_at).toLocaleString() : 'Never'}</p>
    </div>
  )
}

4.2 useMiningRecords — 挖矿历史

tsx
import { useMiningRecords } from '@microcosmmoney/auth-react'

function MiningHistory() {
  const { data: records, loading } = useMiningRecords()

  if (loading) return <div>Loading...</div>

  return (
    <table>
      <thead>
        <tr><th>Time</th><th>MCC Amount</th><th>Paid</th><th>TX</th></tr>
      </thead>
      <tbody>
        {records?.map((r: any, i: number) => (
          <tr key={i}>
            <td>{new Date(r.mined_at).toLocaleString()}</td>
            <td>{r.mcc_amount} MCC</td>
            <td>${r.paid_amount} {r.stablecoin}</td>
            <td>{r.tx_signature?.slice(0, 8)}...</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

4.3 useReincarnationPool — 轮回池(无需登录)

tsx
import { useReincarnationPool } from '@microcosmmoney/auth-react'

function ReincarnationPool() {
  const { data: pool, loading } = useReincarnationPool()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <h3>Reincarnation Pool</h3>
      <p>USDC: {pool?.usdc_balance?.toFixed(2)}</p>
      <p>USDT: {pool?.usdt_balance?.toFixed(2)}</p>
      <p>MCC: {pool?.mcc_balance?.toFixed(2)}</p>
      <p>Total Stablecoin: ${pool?.total_stablecoin?.toFixed(2)}</p>
    </div>
  )
}

5. 领地 & 生态 Hooks

5.1 useUserLevel — 用户等级

tsx
import { useUserLevel } from '@microcosmmoney/auth-react'

function UserLevelBadge() {
  const { data: level, loading } = useUserLevel()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <span>Level: {level?.level}</span>
      <span>Title: {level?.title || 'None'}</span>
    </div>
  )
}

5.2 useTerritoryNFTs — 领地 NFT(无需登录)

tsx
import { useTerritoryNFTs } from '@microcosmmoney/auth-react'

function TerritoryNFTList() {
  const { data: nfts, loading } = useTerritoryNFTs()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      {nfts?.map((nft: any) => (
        <div key={nft.mint}>
          <p>{nft.name}</p>
          <p>Mint: {nft.mint}</p>
        </div>
      ))}
    </div>
  )
}

5.3 useAuctions — 活跃拍卖

tsx
import { useAuctions } from '@microcosmmoney/auth-react'

function AuctionList() {
  const { data: auctions, loading } = useAuctions()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      {auctions?.map((a: any) => (
        <div key={a.id}>
          <p>{a.title} - Current Bid: {a.current_bid} MCC</p>
        </div>
      ))}
    </div>
  )
}

5.4 useOrganizations — 组织列表

tsx
import { useOrganizations } from '@microcosmmoney/auth-react'

function OrgList() {
  const { data: orgs, loading } = useOrganizations()

  if (loading) return <div>Loading...</div>

  return (
    <ul>
      {orgs?.map((org: any) => (
        <li key={org.id}>{org.name} ({org.member_count} members)</li>
      ))}
    </ul>
  )
}

5.5 useTechTree — 科技树

tsx
import { useTechTree } from '@microcosmmoney/auth-react'

function TechTreeProgress() {
  const { data: tree, loading } = useTechTree()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <pre>{JSON.stringify(tree, null, 2)}</pre>
    </div>
  )
}

6. 图表 & 聚合 Hooks

6.1 usePriceHistory — 价格走势

tsx
import { usePriceHistory } from '@microcosmmoney/auth-react'
import { useState } from 'react'

type TimeRange = '1D' | '7D' | '30D'

function PriceChart() {
  const [range, setRange] = useState<TimeRange>('7D')
  const { data: history, loading } = usePriceHistory(range)

  return (
    <div>
      <div>
        {(['1D', '7D', '30D'] as TimeRange[]).map((r) => (
          <button
            key={r}
            onClick={() => setRange(r)}
            style={{ fontWeight: range === r ? 'bold' : 'normal' }}
          >
            {r}
          </button>
        ))}
      </div>

      {loading ? (
        <div>Loading chart...</div>
      ) : (
        <div>
          {/* 使用 Recharts 或其他图表库渲染 */}
          <pre>{JSON.stringify(history?.slice(0, 3), null, 2)}</pre>
          <p>... ({history?.length} data points)</p>
        </div>
      )}
    </div>
  )
}

6.2 useDashboardSummary — 仪表盘汇总

tsx
import { useDashboardSummary } from '@microcosmmoney/auth-react'

function DashboardOverview() {
  const { data: summary, loading } = useDashboardSummary()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <pre>{JSON.stringify(summary, null, 2)}</pre>
    </div>
  )
}

7. 通用 API 查询

useApiQuery — 调用任意 API 端点

tsx
import { useApiQuery } from '@microcosmmoney/auth-react'

// 公开端点(无需登录)
function MarketData() {
  const { data, loading, error } = useApiQuery({
    path: '/v1/dashboard/market',
    requireAuth: false,
    refetchInterval: 60_000,
  })

  if (loading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>
  return <pre>{JSON.stringify(data, null, 2)}</pre>
}

// 用户端点(需登录)— 未登录时自动 skip
function MyTransactions() {
  const { data, loading } = useApiQuery({
    path: '/v1/mcc/transactions?page=1&per_page=10',
    requireAuth: true,
  })

  if (loading) return <div>Loading...</div>
  return <pre>{JSON.stringify(data, null, 2)}</pre>
}

8. 菜单嵌入

8.1 基础用法

tsx
import { MicrocosmMenuSection } from '@microcosmmoney/portal-react'
import { usePathname, useRouter } from 'next/navigation'

function Sidebar() {
  const pathname = usePathname()
  const router = useRouter()

  return (
    <aside>
      <MicrocosmMenuSection
        basePath="/v2"
        currentPath={pathname}
        onNavigate={(path) => router.push(path)}
      />
    </aside>
  )
}

8.2 自定义渲染

tsx
import { MicrocosmMenuSection } from '@microcosmmoney/portal-react'
import type { MicrocosmMenuItem, MicrocosmMenuGroup } from '@microcosmmoney/portal-react'

function CustomSidebar() {
  return (
    <MicrocosmMenuSection
      basePath="/app"
      currentPath="/app/mcc/mining"
      onNavigate={(path) => window.location.href = path}
      renderSectionHeader={(group: MicrocosmMenuGroup) => (
        <h4 className="text-xs uppercase text-gray-500 mt-4 mb-2">
          {group.title}
        </h4>
      )}
      renderItem={(item: MicrocosmMenuItem, path: string, isActive: boolean) => (
        <a
          href={path}
          className={isActive ? 'font-bold text-blue-600' : 'text-gray-700'}
        >
          {item.icon} {item.title}
        </a>
      )}
    />
  )
}

8.3 菜单内容

菜单项路径
Blockchain铸造{basePath}/mcc/mining
轮回回购{basePath}/mcc/reincarnation
钱包{basePath}/mcc/wallet
MCD 积分{basePath}/mcc/mcd
Web3 OS拍卖市场{basePath}/mcc/auctions
领地管理{basePath}/user-system/territory
社区投票{basePath}/mcc/voting
组织架构{basePath}/user-system/organization

9. 路由守卫

9.1 HOC 方式

tsx
import { withAuth } from '@microcosmmoney/auth-react'

function DashboardPage() {
  return <div>Protected Dashboard Content</div>
}

export default withAuth(DashboardPage)

9.2 组件方式

tsx
import { RequireRole } from '@microcosmmoney/auth-react'

function AdminPage() {
  return (
    <div>
      <h1>Admin Area</h1>

      <RequireRole roles={['admin']} fallback={<div>No permission</div>}>
        <AdminPanel />
      </RequireRole>

      <RequireRole roles={['admin', 'user']}>
        <UserContent />
      </RequireRole>
    </div>
  )
}

10. 纯后端调用(无 SDK)

如果你的后端不使用 JavaScript/TypeScript,可以直接调用 HTTP API。

10.1 Python (Flask)

python
import requests

MICROCOSM_API = "https://api.microcosm.money"

# 公开端点 — 无需认证
def get_mcc_price():
    resp = requests.get(f"{MICROCOSM_API}/v1/mcc/price")
    data = resp.json()
    if data["success"]:
        return data["data"]
    return None

# 用户端点 — Bearer Token
def get_user_balance(access_token: str):
    resp = requests.get(
        f"{MICROCOSM_API}/v1/mcc/balance",
        headers={"Authorization": f"Bearer {access_token}"}
    )
    return resp.json()

# Token Exchange
def exchange_token(code: str, redirect_uri: str):
    resp = requests.post(
        "https://microcosm.money/api/oauth/token",
        json={
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": redirect_uri,
            "client_id": "your-client-id",
            "client_secret": "your-client-secret",
        }
    )
    return resp.json()
    # Returns: { access_token, refresh_token, expires_in, user }

# Token Refresh
def refresh_token(refresh_token: str):
    resp = requests.post(
        "https://microcosm.money/api/oauth/token",
        json={
            "grant_type": "refresh_token",
            "refresh_token": refresh_token,
            "client_id": "your-client-id",
            "client_secret": "your-client-secret",
        }
    )
    return resp.json()

# Token Introspect
def verify_token(access_token: str):
    resp = requests.post(
        "https://microcosm.money/api/oauth/introspect",
        json={
            "token": access_token,
            "client_id": "your-client-id",
            "client_secret": "your-client-secret",
        }
    )
    data = resp.json()
    if data.get("active"):
        return data  # { active, uid, email, role, level, scope, exp, iat }
    return None

10.2 cURL

bash
# 公开: MCC 价格
curl https://api.microcosm.money/v1/mcc/price

# 公开: 轮回池
curl https://api.microcosm.money/v1/reincarnation/pool

# 用户: 余额 (需 Token)
curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://api.microcosm.money/v1/mcc/balance

# OAuth: Token Exchange
curl -X POST https://microcosm.money/api/oauth/token \
  -H "Content-Type: application/json" \
  -d '{"grant_type":"authorization_code","code":"AUTH_CODE","redirect_uri":"https://your-app.com/callback","client_id":"YOUR_ID","client_secret":"YOUR_SECRET"}'

11. 钱包管理

11.1 useWallets — 用户绑定钱包列表

tsx
import { useWallets } from '@microcosmmoney/auth-react'

function WalletList() {
  const { data: wallets, loading } = useWallets()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <h3>My Wallets</h3>
      {wallets?.map((w) => (
        <div key={w.wallet_address}>
          <p>{w.wallet_address}</p>
          <p>Type: {w.wallet_type} | Primary: {w.is_primary ? 'Yes' : 'No'}</p>
        </div>
      ))}
    </div>
  )
}

11.2 useTokenPortfolio — 完整代币组合

tsx
import { useTokenPortfolio } from '@microcosmmoney/auth-react'

function WalletTokens({ address }: { address: string }) {
  const { data: portfolio, loading } = useTokenPortfolio(address)

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <p>SOL: {portfolio?.sol_balance}</p>
      <p>MCC: {portfolio?.mcc_balance}</p>
      <p>USDT: {portfolio?.usdt_balance}</p>
      <p>USDC: {portfolio?.usdc_balance}</p>
    </div>
  )
}

11.3 useMCCLocks — MCC 锁仓期

tsx
import { useMCCLocks } from '@microcosmmoney/auth-react'

function LockList() {
  const { data: locks, loading } = useMCCLocks()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <h3>MCC Locks</h3>
      {locks?.map((lock) => (
        <div key={lock.lock_id}>
          <p>{lock.amount} MCC - {lock.reason}</p>
          <p>{lock.lock_start} ~ {lock.lock_end}</p>
          <p>Status: {lock.status}</p>
        </div>
      ))}
    </div>
  )
}

12. 领地管理(完整)

12.1 useTerritories — 领地列表

tsx
import { useTerritories } from '@microcosmmoney/auth-react'

function TerritoryList() {
  const { data: territories, loading } = useTerritories({ unitType: 'station', page: 1 })

  if (loading) return <div>Loading...</div>

  return (
    <div>
      {territories?.map((t) => (
        <div key={t.unit_id}>
          <h4>{t.unit_name} ({t.short_id})</h4>
          <p>Type: {t.unit_type} | Members: {t.member_count}/{t.max_capacity}</p>
          <p>Vault: {t.vault_balance} MCD</p>
        </div>
      ))}
    </div>
  )
}

12.2 useTerritoryDetail + useTerritoryStats

tsx
import { useTerritoryDetail, useTerritoryStats } from '@microcosmmoney/auth-react'

function TerritoryPage({ id }: { id: string }) {
  const { data: territory, loading: l1 } = useTerritoryDetail(id)
  const { data: stats, loading: l2 } = useTerritoryStats(id)

  if (l1 || l2) return <div>Loading...</div>

  return (
    <div>
      <h2>{territory?.unit_name}</h2>
      <p>{territory?.description}</p>
      <p>Location: {territory?.location}</p>

      <h3>Stats</h3>
      <p>Members: {stats?.member_count} / {stats?.max_capacity}</p>
      <p>Occupancy: {((stats?.occupancy_rate ?? 0) * 100).toFixed(1)}%</p>
      <p>Vault MCD: {stats?.vault_mcd}</p>
    </div>
  )
}

12.3 useTerritoryMembers — 成员列表

tsx
import { useTerritoryMembers } from '@microcosmmoney/auth-react'

function MemberList({ territoryId }: { territoryId: string }) {
  const { data: members, loading } = useTerritoryMembers(territoryId, { page: 1, pageSize: 20 })

  if (loading) return <div>Loading...</div>

  return (
    <table>
      <thead>
        <tr><th>Name</th><th>Level</th><th>MCD Received</th><th>Joined</th></tr>
      </thead>
      <tbody>
        {members?.map((m) => (
          <tr key={m.uid}>
            <td>{m.display_name || m.uid}</td>
            <td>{m.level}</td>
            <td>{m.mcd_received}</td>
            <td>{m.joined_at ? new Date(m.joined_at).toLocaleDateString() : '-'}</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

12.4 useTerritorySummary — 领地汇总

tsx
import { useTerritorySummary } from '@microcosmmoney/auth-react'

function TerritorySummaryCard() {
  const { data: summary, loading } = useTerritorySummary()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <p>Total Stations: {summary?.total_stations}</p>
      <p>Total Members: {summary?.total_members}</p>
      <p>Total Vault MCD: {summary?.total_vault_mcd}</p>
      <p>Avg KPI: {summary?.avg_kpi_score?.toFixed(2)}</p>
    </div>
  )
}

13. 投票系统

13.1 useProposals — 提案列表

tsx
import { useProposals } from '@microcosmmoney/auth-react'

function ProposalList() {
  const { data: proposals, loading } = useProposals({ status: 'active' })

  if (loading) return <div>Loading...</div>

  return (
    <div>
      {proposals?.map((p) => (
        <div key={p.id}>
          <h4>{p.title}</h4>
          <p>{p.description}</p>
          <p>Status: {p.status} | Type: {p.proposal_type}</p>
          <p>Options: {p.options.join(', ')}</p>
          <p>Ends: {new Date(p.ends_at).toLocaleString()}</p>
        </div>
      ))}
    </div>
  )
}

13.2 useProposalDetail + useVotePower — 投票

tsx
import { useProposalDetail, useVotePower, useAuth } from '@microcosmmoney/auth-react'

function VotingPage({ proposalId }: { proposalId: string }) {
  const { client } = useAuth()
  const { data: proposal, loading: l1 } = useProposalDetail(proposalId)
  const { data: power, loading: l2 } = useVotePower()

  if (l1 || l2) return <div>Loading...</div>

  async function handleVote(optionIndex: number) {
    const api = client.getApiClient()
    await api.post(`/voting/proposals/${proposalId}/vote`, {
      option_index: optionIndex,
      vote_count: 1,
    })
    alert('Vote cast!')
  }

  return (
    <div>
      <h2>{proposal?.title}</h2>
      <p>{proposal?.description}</p>
      <p>Your Vote Power: {power?.vote_power} (Max: {power?.max_votes})</p>

      <h3>Results</h3>
      {proposal?.vote_results?.map((r, i) => (
        <div key={i}>
          <p>{r.option}: {r.votes} votes ({r.voter_count} voters, {r.mcc_total} MCC)</p>
          <button onClick={() => handleVote(i)} disabled={!power?.can_vote}>
            Vote for "{r.option}"
          </button>
        </div>
      ))}
    </div>
  )
}

14. 拍卖增强

14.1 useAuctionDetail — 拍卖详情

tsx
import { useAuctionDetail } from '@microcosmmoney/auth-react'

function AuctionPage({ auctionId }: { auctionId: number }) {
  const { data: auction, loading } = useAuctionDetail(auctionId)

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <h2>Auction #{auctionId}</h2>
      <pre>{JSON.stringify(auction, null, 2)}</pre>
    </div>
  )
}

14.2 useMyBids — 我的竞价 + 出价

tsx
import { useMyBids, useAuth } from '@microcosmmoney/auth-react'
import { useState } from 'react'

function MyBidsPage() {
  const { data: bids, loading, refresh } = useMyBids()
  const { client } = useAuth()
  const [bidAmount, setBidAmount] = useState('')
  const [auctionId, setAuctionId] = useState('')

  async function handleBid() {
    const api = client.getApiClient()
    await api.post(`/auction-solana/auction/${auctionId}/bid`, {
      bid_amount: parseFloat(bidAmount),
    })
    await refresh()
  }

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <h3>My Bids</h3>
      {bids?.map((b) => (
        <div key={b.bid_id}>
          <p>Auction #{b.auction_id}: {b.bid_amount} MCC - {b.status}</p>
        </div>
      ))}

      <h3>Place Bid</h3>
      <input placeholder="Auction ID" value={auctionId} onChange={e => setAuctionId(e.target.value)} />
      <input placeholder="Bid Amount" value={bidAmount} onChange={e => setBidAmount(e.target.value)} />
      <button onClick={handleBid}>Place Bid</button>
    </div>
  )
}

15. 回购管理

15.1 useBuybackQuote — 回购报价

tsx
import { useBuybackQuote } from '@microcosmmoney/auth-react'
import { useState } from 'react'

function BuybackCalculator() {
  const [amount, setAmount] = useState(1)
  const { data: quote, loading } = useBuybackQuote(amount)

  return (
    <div>
      <h3>Buyback Quote</h3>
      <input
        type="number"
        value={amount}
        onChange={e => setAmount(parseFloat(e.target.value) || 0)}
        min={0.01}
      />
      <span> MCC</span>

      {loading ? (
        <p>Calculating...</p>
      ) : quote ? (
        <div>
          <p>Market Price: ${quote.market_price?.toFixed(6)}</p>
          <p>Buyback Price: ${quote.buyback_price?.toFixed(6)} (+5% premium)</p>
          <p>You Receive: ${quote.usdc_amount?.toFixed(6)} stablecoin</p>
          <p>Premium: ${quote.premium_amount?.toFixed(6)}</p>
          {quote.fee != null && <p>Fee: ${quote.fee?.toFixed(6)} ({(quote.fee_rate! * 100).toFixed(2)}%)</p>}
          {quote.net_amount != null && <p>Net Amount: ${quote.net_amount?.toFixed(6)}</p>}
          {quote.vault_type && <p>Vault: {quote.vault_type}</p>}
          {quote.pool_remaining != null && <p>Pool Remaining: ${quote.pool_remaining?.toFixed(2)}</p>}
        </div>
      ) : null}
    </div>
  )
}

15.2 useBuybackHistory — 回购记录

tsx
import { useBuybackHistory } from '@microcosmmoney/auth-react'

function BuybackHistory() {
  const { data: records, loading } = useBuybackHistory({ page: 1 })

  if (loading) return <div>Loading...</div>

  return (
    <table>
      <thead>
        <tr><th>Time</th><th>MCC</th><th>Received</th><th>Stablecoin</th><th>TX</th></tr>
      </thead>
      <tbody>
        {records?.map((r, i) => (
          <tr key={i}>
            <td>{new Date(r.created_at).toLocaleString()}</td>
            <td>{r.mcc_amount} MCC</td>
            <td>${r.usdc_amount}</td>
            <td>{r.stablecoin}</td>
            <td>{r.tx_signature?.slice(0, 8)}...</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

16. 挖矿增强

16.1 useMiningRatio — 挖矿比率/阶段

tsx
import { useMiningRatio } from '@microcosmmoney/auth-react'

function MiningRatioCard() {
  const { data: ratio, loading } = useMiningRatio()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <h3>Mining Ratio</h3>
      <p>Current Stage: {ratio?.current_stage}</p>
      <p>Total Minted: {ratio?.total_minted?.toLocaleString()} MCC</p>
      <p>Current Rate: {ratio?.current_rate}:1</p>
      <p>USDC per MCC: ${ratio?.usdc_per_mcc?.toFixed(6)}</p>
    </div>
  )
}

16.2 useMiningDistribution — 伴生矿分配历史

tsx
import { useMiningDistribution } from '@microcosmmoney/auth-react'

function DistributionHistory() {
  const { data: records, loading } = useMiningDistribution({ page: 1 })

  if (loading) return <div>Loading...</div>

  return (
    <table>
      <thead>
        <tr><th>Time</th><th>User MCC</th><th>Team MCC</th><th>Magistrate MCC</th><th>Vault MCD</th></tr>
      </thead>
      <tbody>
        {records?.map((r, i) => (
          <tr key={i}>
            <td>{new Date(r.created_at).toLocaleString()}</td>
            <td>{r.user_mcc}</td>
            <td>{r.team_mcc}</td>
            <td>{r.magistrate_mcc}</td>
            <td>{r.vault_mcd}</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

17. MCD 详情

17.1 useMCDTransactions — 交易记录

tsx
import { useMCDTransactions } from '@microcosmmoney/auth-react'

function MCDTransactionList() {
  const { data: txs, loading } = useMCDTransactions({ page: 1, type: 'reward' })

  if (loading) return <div>Loading...</div>

  return (
    <table>
      <thead>
        <tr><th>Time</th><th>Type</th><th>Amount</th><th>From</th><th>To</th></tr>
      </thead>
      <tbody>
        {txs?.map((tx) => (
          <tr key={tx.id}>
            <td>{new Date(tx.created_at).toLocaleString()}</td>
            <td>{tx.tx_type}</td>
            <td>{tx.amount} MCD</td>
            <td>{tx.from_account_type}</td>
            <td>{tx.to_account_type}</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

17.2 useMCDRewards — 每日奖励

tsx
import { useMCDRewards } from '@microcosmmoney/auth-react'

function MCDRewardList() {
  const { data: rewards, loading } = useMCDRewards({ page: 1 })

  if (loading) return <div>Loading...</div>

  return (
    <table>
      <thead>
        <tr><th>Date</th><th>Territory</th><th>MCD Received</th></tr>
      </thead>
      <tbody>
        {rewards?.map((r) => (
          <tr key={r.id}>
            <td>{r.reward_date}</td>
            <td>{r.territory_id || '-'}</td>
            <td>{r.mcd_received} MCD</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

18. 统计数据

18.1 useUserStats — 用户等级分布

tsx
import { useUserStats } from '@microcosmmoney/auth-react'

function UserStatsCard() {
  const { data: stats, loading } = useUserStats()

  if (loading) return <div>Loading...</div>

  return (
    <div>
      <h3>User Statistics</h3>
      <pre>{JSON.stringify(stats, null, 2)}</pre>
    </div>
  )
}

18.2 MicrocosmAPI — 纯 TypeScript 调用

typescript
import { MicrocosmAPI } from '@microcosmmoney/auth-core'

const api = new MicrocosmAPI()

const marketPrice = await api.mcc.getPrice()
const miningRatio = await api.mining.getRatio()
const pool = await api.reincarnation.getPool()
const buybackQuote = await api.reincarnation.getQuote(10)

const userBalance = await api.mcc.getBalance(token)
const wallets = await api.wallets.list(token)
const territories = await api.territories.list(token, { unit_type: 'station' })
const proposals = await api.voting.getProposals(token, { status: 'active' })
const myBids = await api.auctions.getMyBids(token)

19. 挖矿执行(写操作)

19.1 useMiningAction — 认证用户挖矿

挖矿是两步流程:先创建请求获取支付信息,用户链上转账稳定币后确认。

tsx
import { useMiningAction, useMiningConfig } from '@microcosmmoney/auth-react'

function MiningPanel({ walletAddress }: { walletAddress: string }) {
  const { createRequest, confirm, loading, error } = useMiningAction()
  const { data: config } = useMiningConfig()
  const [amount, setAmount] = useState(1)
  const [step, setStep] = useState<'input' | 'paying' | 'done'>('input')
  const [request, setRequest] = useState<any>(null)

  const handleMine = async () => {
    const req = await createRequest({
      mcc_amount: amount,
      stablecoin: 'USDT',
      wallet_address: walletAddress,
    })
    setRequest(req)
    setStep('paying')
  }

  const handleConfirm = async (txSignature: string) => {
    await confirm({
      request_id: request.request_id,
      tx_signature: txSignature,
      mcc_amount: request.mcc_amount,
      usdc_amount: request.usdc_amount,
      stablecoin_type: 'USDT',
    })
    setStep('done')
  }

  return (
    <div>
      {step === 'input' && (
        <div>
          <p>Mining Price: {config?.distribution_ratios ? 'Oracle × 2' : '...'}</p>
          <input type="number" value={amount} onChange={e => setAmount(+e.target.value)} />
          <button onClick={handleMine} disabled={loading}>Mine MCC</button>
        </div>
      )}
      {step === 'paying' && request && (
        <div>
          <p>Transfer {request.usdc_amount} USDT to: {request.payment_address}</p>
          <p>Request expires at: {request.expires_at}</p>
          <button onClick={() => handleConfirm('USER_TX_SIGNATURE')}>
            Confirm Payment
          </button>
        </div>
      )}
      {step === 'done' && <p>Mining complete!</p>}
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </div>
  )
}

19.2 usePublicMining — 钱包直连挖矿(无账户)

不需要 OAuth 登录,仅凭钱包地址即可挖矿。

tsx
import { usePublicMining } from '@microcosmmoney/auth-react'

function PublicMiningWidget() {
  const { createRequest, verify, loading, error } = usePublicMining()

  const handlePublicMine = async (walletAddress: string) => {
    const req = await createRequest({
      wallet_address: walletAddress,
      mcc_amount: 1,
      stablecoin: 'USDT',
    })

    const txSignature = 'USER_SIGNED_TX'
    const result = await verify(req.request_id, txSignature)
    console.log('Mining verified:', result)
  }

  return (
    <button onClick={() => handlePublicMine('USER_WALLET')} disabled={loading}>
      Mine with Wallet Only
    </button>
  )
}

20. 回购执行(写操作)

20.1 useBuybackAction — 记录链上回购

回购流程:前端构建 Reincarnation execute_buyback 指令 → 用户用 Phantom 签名 → 链上执行 → 调用 record 记录到后端。

tsx
import { useBuybackAction, useBuybackQuote } from '@microcosmmoney/auth-react'

function BuybackPanel() {
  const { record, loading, error } = useBuybackAction()
  const { data: quote } = useBuybackQuote(10)
  const [txSig, setTxSig] = useState('')

  const handleRecord = async () => {
    await record({
      tx_signature: txSig,
      wallet_address: 'USER_WALLET_ADDRESS',
      mcc_amount: 10,
      usdc_amount: quote?.usdc_amount || 0,
      stablecoin: 'USDT',
    })
    alert('Buyback recorded!')
  }

  return (
    <div>
      <p>Quote: {quote?.usdc_amount} USDT for 10 MCC (premium: {quote?.premium_amount})</p>
      <input value={txSig} onChange={e => setTxSig(e.target.value)} placeholder="Transaction signature" />
      <button onClick={handleRecord} disabled={loading || !txSig}>
        Record Buyback
      </button>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </div>
  )
}

21. 领地编辑(写操作)

21.1 useTerritoryUpdate — 编辑领地信息

tsx
import { useTerritoryUpdate, useTerritoryDetail } from '@microcosmmoney/auth-react'

function TerritoryEditor({ territoryId }: { territoryId: string }) {
  const { update, updateName, loading, error } = useTerritoryUpdate()
  const { data: territory, refresh } = useTerritoryDetail(territoryId)
  const [name, setName] = useState('')
  const [desc, setDesc] = useState('')

  useEffect(() => {
    if (territory) {
      setName(territory.unit_name || '')
      setDesc(territory.description || '')
    }
  }, [territory])

  const handleSave = async () => {
    await update(territoryId, { description: desc, image_url: territory?.image_url })
    refresh()
  }

  const handleRename = async () => {
    await updateName(territoryId, name) // 第三参数 force=true 可跳过冷却限制
    refresh()
  }

  return (
    <div>
      <div>
        <label>Name (90-day cooldown)</label>
        <input value={name} onChange={e => setName(e.target.value)} />
        <button onClick={handleRename} disabled={loading}>Rename</button>
      </div>
      <div>
        <label>Description</label>
        <textarea value={desc} onChange={e => setDesc(e.target.value)} />
        <button onClick={handleSave} disabled={loading}>Save</button>
      </div>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </div>
  )
}

22. 拍卖出价/取消(写操作)

22.1 useAuctionBid — 拍卖出价

tsx
import { useAuctionBid, useAuctionDetail } from '@microcosmmoney/auth-react'

function BidPanel({ auctionId }: { auctionId: number }) {
  const { placeBid, loading, error } = useAuctionBid()
  const { data: auction, refresh } = useAuctionDetail(auctionId)
  const [amount, setAmount] = useState(0)

  const handleBid = async () => {
    await placeBid(auctionId, amount)
    refresh()
  }

  return (
    <div>
      <h3>{auction?.unit_name} Auction</h3>
      <p>Current highest: {auction?.highest_bid ?? 'No bids'}</p>
      <input type="number" value={amount} onChange={e => setAmount(+e.target.value)} />
      <button onClick={handleBid} disabled={loading}>Place Bid</button>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </div>
  )
}

22.2 useAuctionCancel — 取消竞价/退款

tsx
import { useAuctionCancel, useMyBids } from '@microcosmmoney/auth-react'

function MyBidsPanel() {
  const { prepareCancelBid, loading } = useAuctionCancel()
  const { data: bids, refresh } = useMyBids()

  const handleCancel = async (auctionId: number) => {
    const txParams = await prepareCancelBid(auctionId)
    console.log('Build Solana TX with params:', txParams)
    refresh()
  }

  return (
    <ul>
      {bids?.map((bid: any) => (
        <li key={bid.bid_id}>
          Auction #{bid.auction_id}: {bid.bid_amount} SOL — {bid.status}
          {bid.status === 'outbid' && (
            <button onClick={() => handleCancel(bid.auction_id)} disabled={loading}>
              Cancel & Refund
            </button>
          )}
        </li>
      ))}
    </ul>
  )
}

23. 投票操作(写操作)

23.1 useVoteAction — 创建提案 & 投票

tsx
import {
  useVoteAction, useProposals, useProposalDetail, useVotePower
} from '@microcosmmoney/auth-react'

function VotingPage() {
  const { createProposal, castVote, loading, error } = useVoteAction()
  const { data: proposals, refresh } = useProposals('active')
  const { data: power } = useVotePower()

  const handleCreate = async () => {
    await createProposal({
      title: 'Increase mining rate',
      description: 'Proposal to increase MCC mining rate by 10%',
      proposal_type: 'governance',
      voting_hours: 72,
      options: ['Approve', 'Reject', 'Abstain'],
    })
    refresh()
  }

  const handleVote = async (proposalId: string, optionIndex: number) => {
    await castVote(proposalId, optionIndex, power?.available_votes)
    refresh()
  }

  return (
    <div>
      <p>Your voting power: {power?.total_votes ?? 0} (available: {power?.available_votes ?? 0})</p>
      <button onClick={handleCreate} disabled={loading}>New Proposal</button>

      {proposals?.map((p: any) => (
        <div key={p.proposal_id}>
          <h4>{p.title}</h4>
          <p>{p.description}</p>
          {p.options?.map((opt: string, i: number) => (
            <button key={i} onClick={() => handleVote(p.proposal_id, i)} disabled={loading}>
              Vote: {opt}
            </button>
          ))}
        </div>
      ))}
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </div>
  )
}

24. MCC 综合历史

24.1 useMCCHistory — 全类型交易历史

tsx
import { useMCCHistory } from '@microcosmmoney/auth-react'

function MCCHistoryPage() {
  const [txType, setTxType] = useState<string>('all')
  const [page, setPage] = useState(1)
  const { data, loading } = useMCCHistory({ tx_type: txType, page, page_size: 20 })

  return (
    <div>
      <select value={txType} onChange={e => { setTxType(e.target.value); setPage(1) }}>
        <option value="all">All</option>
        <option value="mining">Mining</option>
        <option value="buyback">Buyback</option>
        <option value="transfer">Transfer</option>
      </select>

      {loading ? <p>Loading...</p> : (
        <table>
          <thead>
            <tr><th>Type</th><th>MCC</th><th>Stablecoin</th><th>TX</th><th>Date</th></tr>
          </thead>
          <tbody>
            {data?.records?.map((r, i) => (
              <tr key={i}>
                <td>{r.type}</td>
                <td>{r.mcc_amount}</td>
                <td>{r.stablecoin_amount} {r.stablecoin}</td>
                <td>{r.tx_signature?.slice(0, 8)}...</td>
                <td>{new Date(r.created_at).toLocaleDateString()}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}

      <div>
        <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page <= 1}>Prev</button>
        <span>Page {page} / {Math.ceil((data?.total || 0) / 20)}</span>
        <button onClick={() => setPage(p => p + 1)} disabled={(data?.records?.length || 0) < 20}>Next</button>
      </div>
    </div>
  )
}

25. 链上交易构建指南

25.1 Reincarnation 回购交易构建

回购交易需要在前端构建 Solana 指令,用户用钱包签名后广播链上。

合约地址:

  • Program: REn8oKyydvjRsistZ2cVi6tksPubvR3bEuLdVTyGknb
  • Pool PDA: 通过 findProgramAddressSync([Buffer.from("reincarnation_pool")], programId) 计算

execute_buyback 指令必须传 10 个账户:

#账户说明
1user用户钱包 (Signer)
2reincarnation_poolPool PDA
3mcc_mintMCCpDtigJLYnfGe1fW5xrSA8AXo6AeAj8ECE7wVqP5e
4usdc_mintUSDT: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
5user_mcc_account用户 MCC ATA
6user_usdc_account用户 USDT ATA
7usdc_vaultPool 的 USDT Vault
8mcc_vaultPool 的 MCC Vault
9mcc_token_programTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
10usdc_token_programTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
typescript
import {
  PublicKey, TransactionInstruction, Transaction, Connection
} from '@solana/web3.js'
import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from '@solana/spl-token'

const PROGRAM_ID = new PublicKey('REn8oKyydvjRsistZ2cVi6tksPubvR3bEuLdVTyGknb')
const MCC_MINT = new PublicKey('MCCpDtigJLYnfGe1fW5xrSA8AXo6AeAj8ECE7wVqP5e')
const USDT_MINT = new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB')

function buildBuybackInstruction(
  userPubkey: PublicKey,
  mccAmount: number,
): TransactionInstruction {
  const [poolPDA] = PublicKey.findProgramAddressSync(
    [Buffer.from('reincarnation_pool')],
    PROGRAM_ID
  )

  const userMccAta = getAssociatedTokenAddressSync(MCC_MINT, userPubkey, false, TOKEN_PROGRAM_ID)
  const userUsdtAta = getAssociatedTokenAddressSync(USDT_MINT, userPubkey, false, TOKEN_PROGRAM_ID)

  const poolMccVault = getAssociatedTokenAddressSync(MCC_MINT, poolPDA, true, TOKEN_PROGRAM_ID)
  const poolUsdtVault = getAssociatedTokenAddressSync(USDT_MINT, poolPDA, true, TOKEN_PROGRAM_ID)

  const discriminator = Buffer.from([47, 32, 19, 100, 184, 96, 144, 49])
  const amountBuf = Buffer.alloc(8)
  amountBuf.writeBigUInt64LE(BigInt(Math.floor(mccAmount * 1e9)))
  const data = Buffer.concat([discriminator, amountBuf])

  return new TransactionInstruction({
    programId: PROGRAM_ID,
    keys: [
      { pubkey: userPubkey, isSigner: true, isWritable: true },
      { pubkey: poolPDA, isSigner: false, isWritable: true },
      { pubkey: MCC_MINT, isSigner: false, isWritable: false },
      { pubkey: USDT_MINT, isSigner: false, isWritable: false },
      { pubkey: userMccAta, isSigner: false, isWritable: true },
      { pubkey: userUsdtAta, isSigner: false, isWritable: true },
      { pubkey: poolUsdtVault, isSigner: false, isWritable: true },
      { pubkey: poolMccVault, isSigner: false, isWritable: true },
      { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
      { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
    ],
    data,
  })
}

25.2 完整回购 + 记录流程

tsx
import { useBuybackAction, useReincarnationConfig } from '@microcosmmoney/auth-react'
import { useConnection, useWallet } from '@solana/wallet-adapter-react'

function BuybackWithSolana() {
  const { record, loading } = useBuybackAction()
  const { data: config } = useReincarnationConfig()
  const { connection } = useConnection()
  const { publicKey, sendTransaction } = useWallet()

  const handleBuyback = async (mccAmount: number) => {
    if (!publicKey) return

    const ix = buildBuybackInstruction(publicKey, mccAmount)
    const tx = new Transaction().add(ix)
    tx.feePayer = publicKey
    tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash

    const signature = await sendTransaction(tx, connection)
    await connection.confirmTransaction(signature, 'confirmed')

    await record({
      tx_signature: signature,
      wallet_address: publicKey.toBase58(),
      mcc_amount: mccAmount,
      usdc_amount: mccAmount * (config?.premium_rate ? 1 + config.premium_rate : 1.05),
      stablecoin: 'USDT',
    })
  }

  return (
    <button onClick={() => handleBuyback(10)} disabled={loading || !publicKey}>
      Buyback 10 MCC
    </button>
  )
}

25.3 ATA 注意事项

  • MCC 和 USDT 均使用 SPL Token (TOKEN_PROGRAM_ID),不是 Token-2022
  • ATA 地址 = PDA([owner, TOKEN_PROGRAM_ID, mint])
  • 如果用户没有 USDT ATA,需要先创建(使用 createAssociatedTokenAccountIdempotent
  • Pool 的 Vault 是 PDA 拥有的 ATA(allowOwnerOffCurve: true

类型定义速查

typescript
// 用户
interface User {
  uid: string
  email: string
  displayName?: string | null
  avatarUrl?: string | null
  role: 'admin' | 'user' | 'agent'
  level?: 'recruit' | 'prospect' | 'miner'
  title?: 'commander' | 'pioneer' | 'warden' | 'admiral' | null
  stationId?: number | null
  emailVerified?: boolean
}

// MCC 余额
interface MCCBalance {
  balance: number
  raw_balance: number
  decimals: number
  symbol: string
  wallet_address?: string | null
  wallets?: MCCWalletBalance[]
  mint?: string
}

interface MCCWalletBalance {
  wallet_address: string
  is_primary: boolean
  balance: number
  raw_balance: number
}

// MCC 价格
interface MCCPrice {
  price: number
  price_change_24h?: number | null
  volume_24h?: number | null
  market_cap?: number | null
  source: string
  updated_at: string
  buyback_price?: number
  premium_rate?: number
}

// 用户挖矿统计
interface MiningStats {
  total_mined: number            // 累计挖矿获得 MCC
  total_paid: number             // 累计支付稳定币
  mining_count: number           // 挖矿次数
  today_mined: number            // 今日挖矿获得 MCC
  last_30d_mined: number         // 近 30 天挖矿获得 MCC
  active_days_30d: number        // 近 30 天活跃挖矿天数
  last_mined_at: string | null   // 最后挖矿时间 (ISO 8601)
}

// 挖矿记录
interface MiningRecord {
  mcc_amount: number              // 挖矿获得 MCC
  paid_amount: number             // 支付稳定币金额
  stablecoin: 'USDC' | 'USDT'    // 支付币种
  tx_signature: string            // Solana 交易签名
  mined_at: string | null         // 挖矿时间 (ISO 8601)
}

// MCD 余额
interface MCDBalance {
  total_balance: string
  available_balance: string
  frozen_balance: string
}

// 钱包
interface Wallet {
  wallet_address: string
  wallet_type: string
  is_primary: boolean
  created_at: string
}

// 代币组合
interface TokenPortfolio {
  sol_balance?: number
  mcc_balance?: number
  usdt_balance?: number
  usdc_balance?: number
}

// MCC 锁仓
interface MCCLock {
  lock_id: string
  amount: number
  reason: string
  lock_start: string
  lock_end: string
  status: string
}

// 挖矿比率
interface MiningRatio {
  current_stage?: number
  total_minted?: number
  ratio?: number
  usdc_per_mcc?: number
  current_rate?: number
}

// 伴生矿分配
interface MiningDistribution {
  user_mcc: number
  team_mcc: number
  magistrate_mcc: number
  vault_mcd: number
  source: string
  territory_id?: string
  tx_signature?: string
  created_at: string
}

// 回购报价
interface BuybackQuote {
  mcc_amount: number
  market_price: number
  buyback_price: number
  usdc_amount: number
  premium_amount: number
  fee?: number
  fee_rate?: number
  slippage?: number
  net_amount?: number
  vault_type?: string
  pool_remaining?: number
}

// 回购记录
interface BuybackRecord {
  mcc_amount: number
  usdc_amount: number
  stablecoin: string
  tx_signature?: string
  buyback_price?: number
  created_at: string
}

// 领地
interface Territory {
  unit_id: string
  unit_name: string
  short_id?: string
  unit_type: string
  description?: string
  location?: string
  image_url?: string
  parent_id?: string
  member_count?: number
  max_capacity?: number
  vault_balance?: number
  manager_uid?: string
}

// 领地汇总
interface TerritorySummary {
  total_stations: number
  total_members: number
  total_vault_mcd: number
  avg_kpi_score?: number
}

// 领地统计
interface TerritoryStats {
  member_count: number
  max_capacity: number
  vault_mcd: number
  occupancy_rate: number
}

// 领地成员
interface TerritoryMember {
  uid: string
  display_name?: string
  level?: string
  mcd_received?: number
  joined_at?: string
}

// 提案
interface Proposal {
  id: string
  title: string
  description: string
  proposal_type: string
  status: string
  options: string[]
  min_votes?: number
  ends_at: string
  created_at: string
}

// 提案详情
interface ProposalDetail extends Proposal {
  vote_results: VoteResult[]
}

// 投票结果
interface VoteResult {
  option: string
  votes: number
  mcc_total: number
  voter_count: number
}

// 投票权
interface VotePower {
  vote_power: number
  user_rank?: string
  cost_per_vote_mcc?: number
  mcc_balance?: number
  max_votes?: number
  can_vote: boolean
}

// 拍卖竞价
interface AuctionBid {
  bid_id?: string
  auction_id: number
  unit_name?: string
  bid_amount: number
  deposit_amount?: number
  status: string
  created_at: string
}

// MCD 交易
interface MCDTransaction {
  id: string
  tx_type: string
  amount: number
  from_account_type?: string
  to_account_type?: string
  created_at: string
}

// MCD 奖励
interface MCDReward {
  id: string
  reward_date: string
  territory_id?: string
  mcd_received: number
  created_at: string
}

// 挖矿请求
interface MiningRequest {
  mcc_amount: number
  stablecoin?: string
  wallet_address: string
}

// 挖矿确认
interface MiningConfirmData {
  request_id: string
  tx_signature: string
  mcc_amount: number
  usdc_amount: number
  stablecoin_type?: string
}

// 挖矿请求结果
interface MiningRequestResult {
  request_id: string
  mcc_amount: number
  usdc_amount: number
  mining_price: number
  stablecoin: string
  payment_address: string
  expires_at: string
}

// 挖矿配置
interface MiningConfig {
  program_id: string
  pool_address: string
  mcc_mint: string
  usdt_mint: string
  usdc_mint: string
  distribution_ratios: Record<string, number>
  min_amount: number
  max_amount: number
}

// 回购记录输入
interface BuybackRecordInput {
  tx_signature: string
  wallet_address: string
  mcc_amount: number
  usdc_amount: number
  stablecoin?: string
}

// 轮回合约配置
interface ReincarnationConfig {
  program_id: string
  pool_address: string
  mcc_vault: string
  usdc_vault: string
  usdt_vault: string
  min_buyback: number
  max_buyback: number
  premium_rate: number
}

// MCC 综合历史记录
interface MCCHistoryRecord {
  mcc_amount: number
  stablecoin_amount: number
  stablecoin: string
  tx_signature: string
  type: string
  wallet_address?: string
  created_at: string
}

// 月度轮回历史
interface CycleHistory {
  cycle_id: number
  executed_at: string
  mcc_returned: number
  mcd_returned: number
  status: string
}

// 拍卖历史
interface AuctionHistory {
  auction_id: number
  unit_name: string
  winner_wallet?: string
  winning_bid?: number
  status: string
  ended_at: string
}

// API 统一响应
interface ApiResponse<T> {
  success: boolean
  data: T | null
  error: string | null
  message?: string | null
}

// Provider 配置
interface MicrocosmAuthConfig {
  clientId: string             // (必填) OAuth Client ID
  redirectUri: string          // (必填) 回调路径
  scope?: string[]             // 默认 ['openid', 'profile', 'email']
  authEndpoint?: string        // 默认 'https://microcosm.money'
  tokenExchangeUri?: string    // 默认 '/api/auth/exchange'
  profileUri?: string          // 默认 '/api/users/profile'
  storage?: 'localStorage' | 'sessionStorage' | 'memory'
  autoRefresh?: boolean        // 默认 true
  debug?: boolean              // 默认 false
}

26. Solana 钱包连接

26.1 useSolanaWallet — Phantom 钱包

tsx
import { useSolanaWallet } from '@microcosmmoney/auth-react'

function WalletButton() {
  const { connected, publicKey, connect, disconnect, connecting, error, signTransaction, signAllTransactions } = useSolanaWallet()

  if (connected) {
    return (
      <div>
        <p>{publicKey?.slice(0, 8)}...{publicKey?.slice(-4)}</p>
        <button onClick={disconnect}>Disconnect</button>
      </div>
    )
  }

  return <button onClick={connect} disabled={connecting}>
    {connecting ? 'Connecting...' : 'Connect Phantom'}
  </button>
}

26.2 useMiningFlow — 铸造完整流程

tsx
import { useMiningFlow, useSolanaWallet } from '@microcosmmoney/auth-react'

function MiningPage() {
  const { step, startMining, confirmMining, reset, error } = useMiningFlow()
  const { publicKey, signTransaction } = useSolanaWallet()

  const handleMine = async () => {
    const request = await startMining({
      amount: 100,
      wallet_address: publicKey!,
      stablecoin: 'USDT',
    })
    // sign transaction with wallet, then confirm
    const txSig = '...' // signed tx signature
    await confirmMining(request.request_id, txSig)
  }

  return (
    <div>
      <p>Step: {step}</p>
      {error && <p>Error: {error}</p>}
      <button onClick={handleMine} disabled={step !== 'idle'}>Start Mining</button>
      <button onClick={reset}>Reset</button>
    </div>
  )
}

26.3 useBuybackFlow — 回购完整流程

tsx
import { useBuybackFlow } from '@microcosmmoney/auth-react'

function BuybackPage() {
  const { step, getQuote, executeBuyback, quote, reset, error } = useBuybackFlow()

  const handleBuyback = async () => {
    const q = await getQuote({ amount: 10, wallet_address: '...', stablecoin: 'USDT' })
    // sign transaction, then execute
    await executeBuyback(q.quote_id, 'tx_signature_here')
  }

  return (
    <div>
      <p>Step: {step}</p>
      {quote && <p>Price: {quote.price} USDT/MCC</p>}
      <button onClick={handleBuyback}>Buyback</button>
    </div>
  )
}

27. UI 组件 (@microcosmmoney/portal-react)

27.1 TerminalTable — 数据表格

tsx
import { TerminalTable, TerminalCard } from '@microcosmmoney/portal-react'

const columns = [
  { key: 'date', header: 'Date' },
  { key: 'amount', header: 'Amount', align: 'right' as const,
    render: (row: any) => <span className="text-emerald-400">{row.amount} MCC</span> },
  { key: 'status', header: 'Status' },
]

<TerminalCard filename="history.log">
  <TerminalTable columns={columns} data={records} rowKey={(r) => r.id} />
</TerminalCard>

27.2 TerminalDialog — 模态对话框

tsx
import { TerminalDialog, TerminalInput } from '@microcosmmoney/portal-react'

const [open, setOpen] = useState(false)

<TerminalDialog open={open} onClose={() => setOpen(false)} title="Confirm" width="max-w-md">
  <p className="text-zinc-300 mb-4">Are you sure?</p>
  <TerminalInput label="Amount" type="number" placeholder="0.00" suffix="MCC" />
  <button className="mt-4 w-full bg-indigo-600 text-white py-2 rounded" onClick={confirm}>
    Confirm
  </button>
</TerminalDialog>

27.3 TerminalTabs — 标签页

tsx
import { TerminalTabs } from '@microcosmmoney/portal-react'

<TerminalTabs
  tabs={[
    { key: 'overview', label: 'Overview' },
    { key: 'history', label: 'History' },
    { key: 'settings', label: 'Settings' },
  ]}
  onChange={(key) => setActiveTab(key)}
>
  {(activeKey) => activeKey === 'overview' ? <Overview /> : <History />}
</TerminalTabs>

27.4 TerritoryCard — 领地卡片

tsx
import { TerritoryCard } from '@microcosmmoney/portal-react'

<TerritoryCard
  territoryId="S-00001"
  name="Alpha Station"
  type="station"
  memberCount={456}
  capacity={1000}
  magistrate="GhXBd...TAxJ"
  mcdBalance={12345.67}
  status="active"
  onClick={() => router.push('/territory/S-00001')}
/>

27.5 KPIRadialChart — 径向图

tsx
import { KPIRadialChart } from '@microcosmmoney/portal-react'

<KPIRadialChart value={75} max={100} label="Mining Rate" unit="%" color="#10b981" />

27.6 VoteResultBar — 投票结果

tsx
import { VoteResultBar } from '@microcosmmoney/portal-react'

<VoteResultBar
  options={[
    { label: 'Approve', votes: 150 },
    { label: 'Reject', votes: 45 },
    { label: 'Abstain', votes: 20 },
  ]}
/>

27.7 菜单增强

tsx
import { MicrocosmMenuSection, dashboardMenu } from '@microcosmmoney/portal-react'

<MicrocosmMenuSection
  basePath="/app"
  currentPath={pathname}
  onNavigate={(path) => router.push(path)}
  onItemClick={(item, path) => analytics.track('menu_click', { key: item.key })}
  filterItems={(item) => item.key !== 'mcd'}
  extraItems={[{
    title: 'Help', key: 'help', path: '/help',
    icon: HelpCircle, badge: 'New'
  }]}
/>

28. 管理员操作 Hooks

28.1 useAuctionEnd — 结束拍卖

tsx
import { useAuctionEnd } from '@microcosmmoney/auth-react'

function AdminAuction({ auctionId }: { auctionId: number }) {
  const { endAuction, loading, error } = useAuctionEnd()
  return <button onClick={() => endAuction(auctionId)} disabled={loading}>End Auction</button>
}

28.2 useNotifications — 通知系统

tsx
import { useNotifications, useNotificationAction } from '@microcosmmoney/auth-react'

function NotificationBell() {
  const { data: notifications } = useNotifications({ unreadOnly: true })
  const { markRead, markAllRead } = useNotificationAction()

  return (
    <div>
      <span>{notifications?.length || 0} unread</span>
      <button onClick={markAllRead}>Mark All Read</button>
    </div>
  )
}

参考

  • 接入指南: Microcosm-开发者接入指南.md
  • API 手册: Microcosm-API参考手册.md
  • 交互式文档: https://api.microcosm.money/docs
  • 参考实现: Double Helix 前端
询问 AI
询问 AI