📘 阅读指南:本文档是 GS DeFi DApp 的 技术设计文档(TDD), 定义 「怎么做」——技术栈、项目结构、合约对接、页面/组件设计、环境配置等。 业务需求与规则请参考配套的 产品需求文档(PRD), 本文档中的需求 ID(FR-x / BR-x / NFR-x)均指向 PRD。
🎨 高保真原型:本文档配套有一份可交互的 HTML 原型,覆盖全部 4 个页面 + 中英双语 + 钱包连接态切换,可直接在浏览器中体验完整 UI 流程 → 2026-05-15-dapp-prototype.html
🚀 营销首页:面向终端用户的品牌入口(Hero + 产品特性 + Launch App CTA),参考 Sky / Aave 模式设计 → 2026-05-15-dapp-landing.html

目录

  1. 文档说明 & PRD 链接
  2. 技术栈
  3. 项目结构
  4. 合约对接
  5. 页面设计
    1. 设计语言 & 整体布局
    2. 首页 / 仪表盘
    3. 质押借贷
    4. Swap 兑换
    5. 水龙头
  6. 导航栏 & 钱包
  7. 国际化实现
  8. 水龙头 API
  9. 移动端实现
  10. 环境变量
  11. 开发阶段计划

📋1. 文档说明 & PRD 链接

本 TDD 描述 GS DeFi DApp 的技术实现方案,聚焦「怎么做」。 业务需求、规则、验收标准统一在 PRD 中维护。本文档各章节通过需求 ID 回引 PRD, 实现时请同时打开两份文档对照

📕 PRD(业务需求)

回答「做什么、为什么、约束是什么」。包含功能需求 FR、业务规则 BR、 非功能需求 NFR、验收标准 AC。

→ 2026-05-15-dapp-requirements.html

📘 TDD(技术设计,本文档)

回答「怎么做」。包含技术栈、项目结构、合约对接、页面/组件设计、 API 规范、环境变量、开发阶段计划。

需求 ID 引用约定

网络与目标:对接 gsc_v2_test 测试网(ChainID 44)上已部署的合约。 详见 PRD 第 1 节背景与目标。

⚙️2. 技术栈

层级 技术 / 库 版本 用途说明
框架 Next.js (App Router) ^14 SSR + API Routes,服务端可安全持有私钥
Web3 wagmi v2 + viem ^2 / ^2 合约读写 hooks,类型安全,内置 TanStack Query 缓存
钱包 RainbowKit ^2 MetaMask + WalletConnect 一键集成,支持移动端
UI shadcn/ui + TailwindCSS latest 暗色现代组件库,可自定义主题色(紫蓝渐变)
图标 lucide-react latest 轻量矢量图标库
国际化 next-intl ^3 中文/英文切换,URL 路径前缀方案 /zh / /en
水龙头 ethers.js v6 ^6 仅用于服务端 API Route 签名转账
类型 TypeScript ^5 全项目强类型

📁3. 项目结构

DApp 作为独立子目录放在现有合约项目根目录下:

gs-defi-protocol/
├── contracts/               # 现有合约(不修改)
├── test/                    # 现有测试(不修改)
├── docs/                    # 合约需求文档
└── dapp/                    # ← 新建 DApp 目录
    ├── app/
    │   ├── layout.tsx           # 根布局(Providers 注入)
    │   ├── page.tsx             # 首页 / 仪表盘
    │   ├── lending/
    │   │   └── page.tsx         # 质押借贷页
    │   ├── swap/
    │   │   └── page.tsx         # Swap 兑换页
    │   ├── faucet/
    │   │   └── page.tsx         # 水龙头页
    │   └── api/
    │       └── faucet/
    │           └── route.ts     # 服务端转账 API(持私钥)
    ├── components/
    │   ├── layout/
    │   │   ├── Navbar.tsx
    │   │   ├── Footer.tsx
    │   │   └── LanguageSwitcher.tsx
    │   ├── lending/
    │   │   ├── BorrowForm.tsx       # 开仓表单
    │   │   ├── OrderList.tsx        # 我的订单
    │   │   ├── RepayModal.tsx       # 还款弹窗
    │   │   └── LiquidatePanel.tsx   # 清算面板
    │   ├── swap/
    │   │   ├── SwapCard.tsx
    │   │   └── SlippageSettings.tsx
    │   └── faucet/
    │       └── FaucetCard.tsx
    ├── hooks/
    │   ├── usePledgeLending.ts  # openOrder / repay / liquidate
    │   ├── useSwap.ts           # swap / getAmountsOut
    │   └── useTokenBalance.ts   # GS/GB/GC 余额 + approve
    ├── config/
    │   ├── chains.ts            # gsc_v2_test 网络定义
    │   ├── contracts.ts         # 合约地址常量
    │   └── abis/                # ABI JSON 文件(从 artifacts/ 导出)
    │       ├── PledgeLending.json
    │       ├── TriggerParams.json
    │       ├── UniswapV2Router02.json
    │       └── ERC20.json
    ├── lib/
    │   ├── i18n/
    │   │   ├── zh.json          # 中文翻译
    │   │   └── en.json          # 英文翻译
    │   └── faucet-cooldown.ts   # 服务端冷却时间 Map
    ├── public/
    │   └── logo.svg
    ├── .env.local               # 私钥等敏感配置(不提交 git)
    ├── .env.example             # 环境变量示例模板(提交 git,含所有变量占位符)
    ├── package.json
    ├── tailwind.config.ts
    └── tsconfig.json

🔗4. 合约对接

网络配置(动态加载)

网络参数全部从环境变量读取,部署到不同环境(测试网 / 主网)时只需修改 .env, 无需重新编译代码。所有配置项使用 NEXT_PUBLIC_ 前缀以便客户端可读。

// config/chains.ts
export const appChain = {
  id: Number(process.env.NEXT_PUBLIC_CHAIN_ID),
  name: process.env.NEXT_PUBLIC_CHAIN_NAME!,
  nativeCurrency: {
    name:     process.env.NEXT_PUBLIC_NATIVE_NAME!,
    symbol:   process.env.NEXT_PUBLIC_NATIVE_SYMBOL!,
    decimals: Number(process.env.NEXT_PUBLIC_NATIVE_DECIMALS ?? 18),
  },
  rpcUrls: {
    default: { http: [process.env.NEXT_PUBLIC_RPC_URL!] },
  },
  blockExplorers: {
    default: {
      name: process.env.NEXT_PUBLIC_EXPLORER_NAME!,
      url:  process.env.NEXT_PUBLIC_EXPLORER_URL!,
    },
  },
}
动态加载优势:同一份代码可在多个网络(测试网 / 预发布 / 主网)部署, 切换网络只需调整 CI/CD 的环境变量注入,避免硬编码导致的人为错误。

合约地址(待部署后填入)

合约变量名地址(gsc_v2_test)
GS TokenGS_TOKEN待部署
GB TokenGB_TOKEN待部署
GC TokenGC_TOKEN待部署
PledgeLendingPLEDGE_LENDING待部署
UniswapV2Router02ROUTER待部署
UniswapV2FactoryFACTORY待部署
TriggerParamsTRIGGER_PARAMS待部署
合约需先部署到 gsc_v2_test 网络,部署后将地址填入 config/contracts.ts。 ABI 直接从现有 artifacts/contracts/ 导出,无需重新编译。

核心合约交互一览

功能合约方法类型
PledgeLending(质押借贷)
开仓openPosition(uint256 gsAmount, uint256 cycleDays, LoanType loanType) returns (bytes32 orderId)
LoanType 枚举:GB=0 / GC=1。开仓前需先 approve GS
Write
补仓addCollateral(bytes32 orderId, uint256 gsAmount)
仅借款人可调,需先 approve GS
Write
还款repay(bytes32 orderId)
必须在 [repayDueDate, repayDeadline] 窗口内;需先 approve GB 或 GC
Write
清算liquidate(bytes32 orderId)
任何人可调;触发:担保率<100% 或 当前时间>repayDeadline
Write
订单完整查询(推荐)getOrder(bytes32 orderId) returns (OrderView)
一次返回订单原始数据 + 担保率 + 实时利息 + 应还数量 + 还款状态
Read
用户订单 ID 列表getUserOrders(address user) returns (bytes32[])Read
担保率getCollateralRate(bytes32 orderId) returns (uint256)
1e18 = 100%
Read
GC 实时利息getAccruedInterest(bytes32 orderId) returns (uint256)Read
预估应还数量getEstimatedRepayment(bytes32 orderId) returns (uint256)Read
还款状态getRepayStatus(bytes32 orderId) returns (RepayStatus)
枚举:NotDue / Repayable / Overdue / Repaid / Liquidated
Read
GB 质押参数getGBPledgeParams() returns (uint256[] cycleDays, uint256[] rates)Read
GC 质押参数getGCPledgeParams() returns (uint256[] cycleDays, uint256[] rates)
GB/GC 各自独立的周期 → 质押率映射
Read
TriggerParams(价格 / 利率参数)
GS/GB 汇率getGSGB() returns (uint256)
= EMA(GS/GC,5) ÷ EMA(GB/GC,5)
Read
GS/GC 汇率(EMA-5)getGSGC5() returns (uint256)Read
GC 日利率getGCDailyRate() returns (uint256)
iL = EMA(Rt,180) + K × σr,1e18 = 100%
Read
GB 日利率getGBDailyRate() returns (uint256)
= 最新 Rt
Read
最新 GS/GC 价格latestGsGcPrice() returns (uint256)Read
最新 GB/GC 价格latestGbGcPrice() returns (uint256)Read
UniswapV2Router02 + ERC20
Swap 兑换swapExactTokensForTokens(amountIn, amountOutMin, path, to, deadline)Write
查询兑换量getAmountsOut(amountIn, path) returns (uint256[])Read
授权代币ERC20.approve(spender, amount)Write
查询授权额度ERC20.allowance(owner, spender)Read
查询余额ERC20.balanceOf(address)Read

合约关键枚举(前端需对齐)

// 借出类型
enum LoanType { GB, GC }            // GB=0, GC=1

// 订单状态(合约存储层)
enum OrderStatus { Active, Repaid, Liquidated }

// 还款状态(getRepayStatus 返回,前端展示用)
enum RepayStatus {
  NotDue,       // 未到期(block.timestamp < repayDueDate)
  Repayable,    // 可还款(repayDueDate ≤ now ≤ repayDeadline)
  Overdue,      // 已逾期(now > repayDeadline,可被清算)
  Repaid,       // 已结清
  Liquidated    // 已清算
}

关键业务规则提醒

  • 订单 ID 类型是 bytes32(不是 uint256),TS 用 0x{...} 字符串处理;wagmi 用 Hex 类型
  • 不允许提前还款(BR-11:合约要求 block.timestamp ≥ repayDueDate。UI 在 RepayStatus.NotDue 时必须禁用还款按钮
  • 所有汇率 / 利率均为 1e18 精度1e18 = 100%7e17 = 70%1e16 = 1%。前端展示时除以 1e18 再 ×100
  • 清算 99% 销毁(非"扣除"或进入财库):UI 文案应明确「失败仓位的 99% GS 将被销毁」
  • GB / GC 质押参数独立:两套不同的 cycleDays → pledgeRate 映射,UI 切换 LoanType 时需重新拉取参数
  • 开仓时 gcDailyRate 被固化到订单中,之后利率变化不影响已开仓订单(避免开仓后利率上升导致借款人亏损)

合约 ABI 文件来源

从已编译的 artifacts/contracts/ 直接导出 ABI 到 dapp/config/abis/:

artifacts/contracts/PledgeLending.sol/PledgeLending.json       → PledgeLending.json
artifacts/contracts/TriggerParams.sol/TriggerParams.json       → TriggerParams.json
artifacts/contracts/interfaces/IPledgeLending.sol/...          → IPledgeLending.json
artifacts/contracts/interfaces/ITriggerParams.sol/...          → ITriggerParams.json
artifacts/contracts/uniswap-v2-periphery/.../Router02.json     → UniswapV2Router02.json
artifacts/.../ERC20.json                                       → ERC20.json

🖥️5. 页面设计

5.0 设计语言 & 整体布局

参考 Sky Protocol(MakerDAO 新版)的 DApp 设计语言,采用 左侧固定边栏 + 右侧主内容区的现代布局。首页作为"中央枢纽", 所有复杂操作下沉到子页面,卡片化按功能分组呈现。

🎨 视觉风格

深紫渐变主题(#0d1117 → #1a0533 → #0d1b3e), 高对比文字,半透明卡片边框,悬停微光效果。

📐 布局栅格

桌面端:左 240px 固定边栏 + 右侧弹性主区(最大 1200px)。 移动端:边栏收起为底部 4 图标导航。

🧱 组件原则

卡片即入口:图标 + 标题 + 一句话描述,点击进入子页面。 首页不嵌入复杂表单,避免认知过载。

🔌 空状态

未连接钱包时,左侧 Balances 卡片显示插画 + Connect Wallet 引导; 右侧功能卡片仍可浏览(只读模式)。

整体结构示意

┌──────────────────────────────────────────────────────────────┐
│  ⬡ GS DeFi                    [🌐 GSC V2 ▼]  [Connect Wallet]│  ← 顶栏
├────────────┬─────────────────────────────────────────────────┤
│ 📊 Dashboard│                                                 │
│ 💰 Lending  │     主内容区(按功能分组的卡片网格)           │
│ 🔄 Swap     │                                                 │
│ 🚰 Faucet   │                                                 │
│            │                                                 │
│ ┌────────┐ │                                                 │
│ │Balances│ │                                                 │
│ │ 我的资产│ │                                                 │
│ │  GS:.. │ │                                                 │
│ │  GB:.. │ │                                                 │
│ │  GC:.. │ │                                                 │
│ └────────┘ │                                                 │
│            │                                                 │
│ 🌐 中/EN   │                                                 │
└────────────┴─────────────────────────────────────────────────┘

5.1 首页 / 仪表盘 / (实现 FR-4 + FR-4.2

用户进入后的"中央枢纽"——左侧持久 Balances 卡片显示用户资产, 右侧按功能语义分组(质押借贷 / 兑换交易 / 新手入口 / 协议数据)。 每个分组下面是 1-2 行入口卡片,点击进入子页面执行实际操作。

🌐 GSC V2 ▼ Connect Wallet
📊 Dashboard
💰 Lending
🔄 Swap
🚰 Faucet
Balances
连接钱包查看您的资产
🦊
Connect Wallet
欢迎使用 GS DeFi
Genesis Chain 去中心化金融协议 · 🟢 网络正常
质押 & 借贷
💼 开新仓(Stake & Borrow)
质押 GS 借出 GB / GC
📋 我的订单
管理已开仓位、还款、补仓
兑换交易
🔄 GS ⇄ GC
1 GS = 0.0023 GC
🔄 GB ⇄ GC
1 GB = 1.024 GC
协议数据
12.45M
GS 总质押
8.20M
GB 借出
5.10M
GC 借出

左侧边栏(Sidebar)

右侧主内容区(分组卡片)

5.2 质押借贷页 /lending (实现 FR-1.1 ~ FR-1.5,约束 BR-1 ~ BR-4BR-7BR-10

参考 MakerDAO / Aave 风格,左侧操作面板,右侧订单列表。具体业务规则见 PRD BR-1(质押率 70%)、BR-3(周期)、BR-7(利率来源)。

开新仓(Borrow)流程

选择借出类型
GB(无息)/ GC(有利率)
选择质押周期
360 / 720 / 1080 天
输入质押 GS 数量
自动计算可借出量
授权 GS
ERC20 approve
确认开仓
openPosition()

我的订单列表

订单ID类型质押GS借出量到期日状态操作
#0x1a2b… GB 10,000 GS 7,000 GB 2027-05-15 正常 补仓 还款
#0x3c4d… GC 5,000 GS 3,500 GC 2026-08-10 临近到期 补仓 还款

5.3 Swap 兑换页 /swap (实现 FR-2.1FR-2.2,约束 BR-8BR-9

参考 Uniswap V2 经典界面,居中卡片布局。默认滑点 BR-8,手续费 BR-9

5.4 水龙头页 /faucet (实现 FR-3.1FR-3.2,约束 BR-5BR-6

简洁卡片,降低新用户上手门槛。冷却时间 BR-5,领取量 BR-6

每次领取量(建议):GS 1,000 · GB 500 · GC 500。 具体数量在 .env.local 中配置,方便调整。

🌐7. 国际化实现 (实现 NFR-2

使用 next-intl 实现双语切换,翻译文件存于 lib/i18n/。需求详情见 PRD NFR-2

// lib/i18n/zh.json(示例)
{
  "nav": {
    "home": "首页",
    "lending": "质押借贷",
    "swap": "Swap 兑换",
    "faucet": "水龙头"
  },
  "lending": {
    "borrow": "开仓借款",
    "repay": "还款",
    "liquidate": "清算",
    "pledgeRate": "质押率"
  }
}

🔧8. 水龙头 API 设计 (实现 FR-3.1FR-3.2,约束 BR-5BR-6NFR-4

接口

POST /api/faucet
Content-Type: application/json

// Request
{ "address": "0x1234...abcd" }

// Response 200
{
  "success": true,
  "txHashes": {
    "GS": "0xabc...",
    "GB": "0xdef...",
    "GC": "0x789..."
  },
  "nextClaimAt": "2026-05-16T10:30:00Z"
}

// Response 429 (冷却中)
{ "success": false, "error": "COOLDOWN", "nextClaimAt": "..." }

// Response 400 (地址无效)
{ "success": false, "error": "INVALID_ADDRESS" }

服务端逻辑

校验地址格式
查冷却 Map
address → lastClaimTime
距上次 < 24h?
返回 429
ethers.Wallet 签名
3笔 transfer
更新冷却 Map
返回 txHashes
安全说明:FAUCET_PRIVATE_KEY 仅存在于 .env.local, 通过 process.env 在服务端 API Route 中读取,绝不注入到客户端 bundle。 Next.js 只有不带 NEXT_PUBLIC_ 前缀的变量才会留在服务端。

📱9. 移动端实现 (实现 NFR-3

响应式布局

TailwindCSS 断点(sm/md/lg)实现自适应布局。 卡片在移动端变为单列,表格支持横向滚动。

底部导航栏

移动端隐藏顶部菜单,固定底部导航 4 个图标: 首页 / 借贷 / Swap / 水龙头。

移动端钱包

RainbowKit 内置 WalletConnect v2, 支持 MetaMask Mobile、imToken、TokenPocket 等主流移动端钱包扫码连接。

触摸优化

按钮最小高度 44px,输入框字体 16px(防 iOS 自动缩放), 滑动手势支持下拉刷新余额。

🔑10. 环境变量 (约束 NFR-4

环境变量列表

# dapp/.env.local(不提交 git,本地实际配置)
# dapp/.env.example(提交 git,作为模板供他人参考)

# ── 水龙头钱包(服务端专用,无 NEXT_PUBLIC_ 前缀)
FAUCET_PRIVATE_KEY=0x...              # 水龙头钱包私钥

# ── 每次领取量
FAUCET_GS_AMOUNT=1000                 # 单位:GS(整数)
FAUCET_GB_AMOUNT=500
FAUCET_GC_AMOUNT=500

# ── 网络参数(动态加载,部署不同链时修改这里即可)
NEXT_PUBLIC_CHAIN_ID=44
NEXT_PUBLIC_CHAIN_NAME=Genesis Chain V2 Testnet
NEXT_PUBLIC_NATIVE_NAME=GS
NEXT_PUBLIC_NATIVE_SYMBOL=GS
NEXT_PUBLIC_NATIVE_DECIMALS=18
NEXT_PUBLIC_RPC_URL=https://v2test.genesischain.io
NEXT_PUBLIC_EXPLORER_NAME=GSCScan
NEXT_PUBLIC_EXPLORER_URL=https://v2scan.genesischain.io

# ── 合约地址(部署后填入)
NEXT_PUBLIC_GS_TOKEN=0x...
NEXT_PUBLIC_GB_TOKEN=0x...
NEXT_PUBLIC_GC_TOKEN=0x...
NEXT_PUBLIC_PLEDGE_LENDING=0x...
NEXT_PUBLIC_ROUTER=0x...
NEXT_PUBLIC_FACTORY=0x...
NEXT_PUBLIC_TRIGGER_PARAMS=0x...

.env.example 示例文件(提交 git)

为方便团队协作与新环境部署,在仓库中提供一份 .env.example 模板。 该文件 仅包含变量名和占位符,不含任何真实私钥或敏感地址,可安全提交到 git。 新成员克隆项目后 cp .env.example .env.local 再填充实际值即可。

# dapp/.env.example(提交到 git,作为团队配置模板)

# ── 水龙头钱包(服务端专用)
FAUCET_PRIVATE_KEY=                   # 留空,本地填入实际私钥

# ── 每次领取量
FAUCET_GS_AMOUNT=1000
FAUCET_GB_AMOUNT=500
FAUCET_GC_AMOUNT=500

# ── 网络参数(按部署目标链修改)
NEXT_PUBLIC_CHAIN_ID=44
NEXT_PUBLIC_CHAIN_NAME=Genesis Chain V2 Testnet
NEXT_PUBLIC_NATIVE_NAME=GS
NEXT_PUBLIC_NATIVE_SYMBOL=GS
NEXT_PUBLIC_NATIVE_DECIMALS=18
NEXT_PUBLIC_RPC_URL=https://v2test.genesischain.io
NEXT_PUBLIC_EXPLORER_NAME=GSCScan
NEXT_PUBLIC_EXPLORER_URL=https://v2scan.genesischain.io

# ── 合约地址(部署后填入)
NEXT_PUBLIC_GS_TOKEN=
NEXT_PUBLIC_GB_TOKEN=
NEXT_PUBLIC_GC_TOKEN=
NEXT_PUBLIC_PLEDGE_LENDING=
NEXT_PUBLIC_ROUTER=
NEXT_PUBLIC_FACTORY=
NEXT_PUBLIC_TRIGGER_PARAMS=
安全规则:
  • FAUCET_PRIVATE_KEY 不加 NEXT_PUBLIC_ 前缀,确保只在服务端可见。
  • .env.local 必须加入 .gitignore,永不提交。
  • .env.example 只放占位符 / 默认值,不放任何私钥与生产地址。
  • CI/CD 部署时通过平台 Secret 注入环境变量,不写入仓库。

🗺️11. 开发阶段计划

阶段一:项目初始化 + 基础框架

创建 Next.js 项目、配置 wagmi/RainbowKit、接入 gsc_v2_test 网络、搭建导航栏、语言切换骨架

阶段二:水龙头页

实现 API Route 服务端转账、冷却逻辑、水龙头前端卡片 UI

阶段三:Swap 页

对接 UniswapV2 Router,实现代币兑换、滑点设置、实时汇率展示

阶段四:质押借贷页

实现开仓、订单列表、还款弹窗、清算面板,对接 PledgeLending 合约全生命周期

阶段五:首页仪表盘

汇聚协议统计数据、代币价格、用户资产概览

阶段六:移动端优化 + 国际化完善

完善底部导航、触摸优化、补全中英文翻译、端到端测试

GS DeFi DApp 技术设计文档(TDD)v1.0 · 2026-05-15 · Genesis Chain V2 Testnet
配套产品需求文档:2026-05-15-dapp-requirements.html