项目概述

Dory/Datre 是一个基于推荐人系统的去中心化代币项目,部署在 Arbitrum One 网络上。项目采用三层架构设计:智能合约层、数据索引层和API服务层,实现了完整的推荐人激励生态系统。

核心特性

  • 基于 UUPS 代理模式的可升级 ERC-20 代币
  • 防循环引用的推荐人系统
  • 节点购买与算力分配机制
  • 资金冻结与解冻管理
  • 链下数据索引与API查询服务

技术栈

  • 智能合约: Solidity 0.8.28 + OpenZeppelin
  • 数据索引: The Graph + AssemblyScript
  • API服务: FastAPI + Python
  • 区块链网络: Arbitrum One (L2)

项目地址

  • 合约地址:0x6174B1868c61c05Eb520E06ad312E8dd0Af8C553
  • 网络:Arbitrum One
  • 起始区块:362672657

系统架构

整体架构图

整个系统采用分层架构设计,各组件之间通过事件机制进行通信,确保系统的可扩展性和维护性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   前端应用      │    │   API服务层     │    │   子图索引      │
│   (Web/Mobile) │◄──►│   (FastAPI)     │◄──►│   (The Graph)   │
└─────────────────┘    └─────────────────┘    └─────────────────┘
                                │                       │
                                │                       │
                                ▼                       ▼
                       ┌─────────────────────────────────────┐
                       │         Arbitrum One               │
                       │      (智能合约层)                  │
                       │    ┌─────────────────────────────┐  │
                       │    │      DoryContract          │  │
                       │    │   (UUPS代理模式)           │  │
                       │    └─────────────────────────────┘  │
                       └─────────────────────────────────────┘

系统架构图展示了从用户界面到区块链网络的完整数据流:

  • 前端应用负责用户交互和数据显示
  • API服务层提供RESTful接口和业务逻辑处理
  • 子图索引负责链上数据的实时同步和查询
  • 智能合约层实现核心业务逻辑和状态管理

架构层次说明

智能合约层 (Smart Contract Layer)

负责核心业务逻辑实现,管理代币、推荐人、算力等状态,通过事件机制与外部系统交互,支持 UUPS 代理模式升级。

数据索引层 (Data Indexing Layer)

监听智能合约事件,构建链下数据模型,提供 GraphQL 查询接口,支持复杂关系查询。

API服务层 (API Service Layer)

封装子图查询逻辑,提供 RESTful API 接口,处理数据格式转换,实现业务逻辑封装。

智能合约详解

合约目录结构

项目采用模块化的合约组织结构,为后续添加更多合约做好准备。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
contracts/
├── datre-token/             # Datre Token 合约模块
│   ├── contracts/          # 智能合约源码
│   │   └── Datre.sol      # 主合约文件
│   ├── test/               # 测试文件
│   │   └── Datre.test.js  # 合约测试
│   ├── scripts/            # 部署脚本
│   │   └── deploy.js      # 部署脚本
│   ├── artifacts/          # 编译产物
│   ├── cache/             # 编译缓存
│   ├── hardhat.config.js   # Hardhat 配置
│   ├── package.json        # 合约依赖
│   └── README.md           # 项目说明
├── datre-hook/              # Uniswap v4 Hook 合约模块
│   ├── contracts/          # Hook 合约源码
│   ├── test/               # 测试用例
│   ├── scripts/            # 部署脚本
│   ├── hardhat.config.js   # Hardhat 配置
│   └── package.json        # Hook 依赖
└── datre-distributor/        # 代币分发合约模块
    ├── contracts/          # 分发合约源码
    ├── test/               # 测试用例
    ├── scripts/            # 部署脚本
    ├── hardhat.config.js   # Hardhat 配置
    └── package.json        # 分发合约依赖

** 合约模块说明 三个合约项目相互配合,构成完整的 Datre 生态系统:

  • datre-token/: 发币合约,基于 OpenZeppelin 的可升级 ERC20 代币,提供代币铸造功能
  • datre-hook/: 交易合约,Uniswap v4 Hook 实现特殊的交易规则(5% 手续费 + 买入销毁)
  • datre-distributor/: 分发合约,基于 Merkle Root 验证的按轮次分发系统,调用 datre-token 的 mint 功能
  • 扩展性: 后续可添加更多合约模块(如治理合约、质押合约等)

Distributor 合约详情

Distributor 是一个基于 Merkle Root 验证的代币分发合约,支持按轮次(epoch)分发 Datre Token。

** 核心功能

  • Epoch 管理: 支持多轮次分发,每轮次独立的 Merkle Root
  • Merkle 验证: 使用 Merkle Proof 验证用户领取资格
  • 位图防重: 使用位图防止重复领取
  • 时间窗口: 支持设置领取时间窗口
  • 限额控制: 支持轮次限额和日限额
  • 暂停机制: 支持紧急暂停和恢复
  • 可升级: 基于 UUPS 代理模式,支持合约升级
  • 域隔离: 防止跨链、跨代币、跨合约的重放攻击

** 安全特性

  • 域隔离: 叶子节点包含代币地址、链ID、分发器地址,防止重放攻击
  • 权限控制: 只有 owner 可以设置 Merkle Root 和暂停合约
  • 重入保护: 所有外部调用都使用 nonReentrant 修饰符
  • 限额保护: 支持轮次和日限额,防止超发
  • 紧急撤权: 支持紧急暂停和代币权限撤销

** 与 datre-token 发币合约的集成

  • 调用 datre-token 的 mint 函数直接铸造代币给用户
  • 需要 datre-token 提供 minter 权限管理(setMinter/isMinter)
  • 支持紧急撤权:同时撤销 minter 权限和暂停分发器
  • 升级时保持权限不变,确保连续性
  • 不持有大额库存,按需铸造,降低安全风险

合约基本信息

合约采用UUPS代理模式,支持升级和扩展功能。

1
2
3
4
5
6
7
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract DoryContract is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
    string public constant VERSION = "1.0.0";
    // ... 其他代码
}

DatreHook 合约

DatreHook 是为 DATRE 代币设计的 Uniswap v4 Hook,实现了特殊的交易规则。

* 交易规则

  • 卖出 DATRE→USDC: 从 DATRE 输入中扣除 5% 手续费给 feeRecipient,剩余 95% 进入池子成交
  • 买入 DATRE←USDC: 从 USDC 输入中扣除 5% 手续费给 feeRecipient,剩余 95% 进入池子成交,产出的 DATRE 全额销毁到黑洞地址,并按 datreOut 给买家铸造算力

* 核心特性

  • 规则在 Hook 内强制执行,任何 Router/聚合器都无法绕过
  • 支持 EIP-55 校验和地址格式
  • 完整的权限控制和事件记录
  • 支持 CREATE2 部署以满足 Hook 权限位要求

* 合约接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
contract SimpleDatreHook is Ownable {
    uint256 public constant BPS_DENOM = 10000;
    uint256 public feeBps = 500; // 5%

    address public immutable feeRecipient;
    address public immutable blackhole;
    address public immutable power;
    IERC20 public immutable datre;
    IERC20 public immutable usdc;

    function simulateSellDatre(uint256 datreAmount) external;
    function simulateBuyDatre(uint256 usdcAmount, address user) external;
    function setFeeBps(uint256 _feeBps) external onlyOwner;
}

* 部署说明

  • 测试网部署: 直接使用 deploy.js 脚本部署,enforceHookPerms 设置为 false
  • 主网部署: 使用 findSalt.js 脚本找到满足 Hook 权限位的 CREATE2 地址,然后使用该盐值部署
  • 参数配置: feeRecipient(手续费接收地址)、blackhole(销毁地址)、power(算力合约地址)

* 事件说明

  • FeeTaken: 手续费收取事件,记录收取的代币和数量
  • BurnedOnBuy: DATRE 销毁事件,记录销毁的数量
  • PowerMinted: 算力铸造事件,记录用户地址和铸造的算力数量
  • FeeUpdated: 手续费比例更新事件
  • FeeRecipientUpdated: 手续费接收地址更新事件
  • BlackholeUpdated: 销毁地址更新事件
  • PowerUpdated: 算力合约地址更新事件
  • PoolTokenOrderSet: 池子代币顺序设置事件

* 核心功能

  • beforeSwap: 在交易前收取 5% 手续费,支持 DATRE→USDC 和 USDC→DATRE 两个方向
  • afterSwap: 在买入 DATRE 后销毁所有产出的 DATRE 并铸造算力给用户
  • 池子代币顺序检测: 自动检测并记录池子中 DATRE 和 USDC 的顺序
  • hookData 解析: 从 hookData 中提取真实用户地址,支持聚合器路径

* 安全考虑

  • Hook 规则在合约内强制执行,无法被外部绕过
  • 支持 EIP-55 校验和地址格式,防止地址混淆攻击
  • 完整的权限控制,只有 owner 可以修改关键参数
  • 事件记录完整,便于审计和追踪
  • 支持 CREATE2 部署以满足 Hook 权限位要求

核心数据结构

UserInfo 结构

UserInfo结构定义了用户的基本信息,包括推荐人关系、算力值和时间戳等。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
struct UserInfo {
    address referrer;      // 推荐人地址
    uint256 power;         // 算力值
    uint256 joinTime;      // 加入时间
    uint256 updateTime;    // 最后更新时间
    uint256 reserved1;     // 保留字段1
    uint256 reserved2;     // 保留字段2
    bytes32 reserved3;     // 保留字段3
    bool reserved4;        // 保留字段4
}

NodeConfig 结构

NodeConfig结构定义了节点的配置信息,包括价格、算力和库存状态。

1
2
3
4
5
6
struct NodeConfig {
    uint256 price;         // 节点价格 (USDT, 6位小数)
    uint256 power;         // 节点算力
    uint256 remaining;     // 剩余数量
    bool isActive;         // 是否开放购买
}

FreezeRecord 结构

FreezeRecord结构记录了资金冻结的详细信息,支持临时和永久冻结。

1
2
3
4
5
6
7
struct FreezeRecord {
    uint256 amount;        // 冻结金额
    uint256 timestamp;     // 冻结时间
    uint256 expireTime;    // 到期时间 (0表示永久冻结)
    string reason;         // 冻结原因
    bool isActive;         // 是否激活状态
}

核心功能模块

推荐人系统

  • 设置推荐人

    设置推荐人函数实现了防循环引用的推荐关系建立。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    function setReferrer(address referrer) external {
        require(referrer != address(0), "Referrer cannot be zero address");
        require(referrer != msg.sender, "Cannot refer yourself");
        require(_userInfos[msg.sender].referrer == address(0), "Referrer already set");
        require(!_hasCircularReference(msg.sender, referrer), "Circular reference detected");
    
        _userInfos[msg.sender] = UserInfo({
            referrer: referrer,
            power: 0,
            joinTime: block.timestamp,
            updateTime: block.timestamp,
            reserved1: 0,
            reserved2: 0,
            reserved3: bytes32(0),
            reserved4: false
        });
    
        emit ReferrerSet(msg.sender, referrer, block.timestamp);
        emit ReferralRelationshipEstablished(referrer, msg.sender, block.timestamp);
    }
    
  • 防循环引用检查

    防循环引用检查函数确保推荐关系不会形成闭环。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    function _hasCircularReference(address account, address newReferrer) private view returns (bool) {
        address current = newReferrer;
        uint256 maxDepth = 100;
    
        for (uint256 i = 0; i < maxDepth; i++) {
            address currentReferrer = _userInfos[current].referrer;
            if (currentReferrer == address(0)) {
                break;
            }
            if (currentReferrer == account) {
                return true;
            }
            current = currentReferrer;
        }
    
        return false;
    }
    

节点购买系统

  • 节点配置初始化

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    function _initializeNodeConfigs() private {
        // 大节点配置: 5000 USDT, 算力5000, 剩余100个
        _nodeConfigs[NodeType.BigNode] = NodeConfig({
            price: 5000 * 10**6,        // 5000 USDT (6位小数)
            power: 5000,                // 算力5000
            remaining: 100,             // 剩余100个
            isActive: true              // 开放购买
        });
    
        // 小节点配置: 1000 USDT, 算力1000, 剩余300个
        _nodeConfigs[NodeType.SmallNode] = NodeConfig({
            price: 1000 * 10**6,        // 1000 USDT (6位小数)
            power: 1000,                // 算力1000
            remaining: 300,             // 剩余300个
            isActive: true              // 开放购买
        });
    }
    
  • 节点购买函数

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    function purchaseNode(NodeType nodeType) external {
        _purchaseNodeInternal(msg.sender, nodeType);
    }
    
    function purchaseNodeWithPermit(
        NodeType nodeType,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        // 使用 permit 机制,无需提前 approve
        IERC20Permit(usdtToken).permit(msg.sender, address(this), _nodeConfigs[nodeType].price, deadline, v, r, s);
        _purchaseNodeInternal(msg.sender, nodeType);
    }
    

资金冻结系统

  • 冻结资金

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    function freezeFunds(
        address account,
        uint256 amount,
        uint256 duration,
        string memory reason
    ) external onlyOwner {
        require(account != address(0), "Account cannot be zero address");
        require(amount > 0, "Amount must be greater than 0");
        require(duration > 0, "Duration must be greater than 0");
    
        uint256 freezeId = _nextFreezeId[account]++;
        uint256 expireTime = duration == type(uint256).max ? 0 : block.timestamp + duration;
    
        _freezeRecords[account][freezeId] = FreezeRecord({
            amount: amount,
            timestamp: block.timestamp,
            expireTime: expireTime,
            reason: reason,
            isActive: true
        });
    
        _totalFrozenAmount[account] += amount;
    
        emit FundsFreezed(account, freezeId, amount, reason);
    }
    
  • 自动解冻检查

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    function checkAndUnfreezeExpired(address account) public {
        uint256 totalUnfrozen = 0;
    
        for (uint256 i = 0; i < _nextFreezeId[account]; i++) {
            FreezeRecord storage record = _freezeRecords[account][i];
    
            if (record.isActive && record.expireTime > 0 && record.expireTime <= block.timestamp) {
                record.isActive = false;
                totalUnfrozen += record.amount;
    
                emit FundsAutoUnfreezed(account, i, record.amount);
            }
        }
    
        if (totalUnfrozen > 0) {
            _totalFrozenAmount[account] -= totalUnfrozen;
        }
    }
    

事件系统

推荐人相关事件

1
2
3
event ReferrerSet(address indexed account, address indexed referrer, uint256 timestamp);
event ReferrerModified(address indexed account, address indexed oldReferrer, address indexed newReferrer, uint256 timestamp);
event ReferralRelationshipEstablished(address indexed referrer, address indexed referee, uint256 timestamp);

算力相关事件

1
event PowerUpdated(address indexed account, uint256 oldPower, uint256 newPower, uint256 timestamp);

节点相关事件

1
2
event NodePurchased(address indexed buyer, NodeType indexed nodeType, uint256 usdtAmount, uint256 powerGained, uint256 timestamp);
event NodeConfigUpdated(NodeType indexed nodeType, uint256 price, uint256 power, uint256 remaining, bool isActive);

冻结相关事件

1
2
3
event FundsFreezed(address indexed account, uint256 indexed freezeId, uint256 amount, string reason);
event FundsUnfreezed(address indexed account, uint256 indexed freezeId, uint256 amount);
event FundsAutoUnfreezed(address indexed account, uint256 indexed freezeId, uint256 amount);

权限控制

所有者权限

  • 代币铸造与销毁
  • 用户算力设置
  • 推荐人修改
  • 资金冻结与解冻
  • 节点配置更新
  • 合约升级

用户权限

  • 设置推荐人(一次性)
  • 购买节点
  • 查询个人信息
  • 查询推荐路径

Gas 优化策略

  1. **移除链上统计**:推荐人数量等统计信息通过事件在链下构建
  2. **批量操作**:支持批量设置算力等操作
  3. **存储优化**:使用紧凑的数据结构,预留扩展字段
  4. **事件驱动**:通过事件机制减少链上存储和计算

子图索引系统

子图配置

基本信息

  • 名称:dory-subgraph-v0.0.7
  • 版本:0.0.7
  • 网络:arbitrum-one
  • 合约地址:0x6174B1868c61c05Eb520E06ad312E8dd0Af8C553
  • 起始区块:362672657

数据源配置

1
2
3
4
5
6
7
8
dataSources:
  - kind: ethereum
    name: DoryContract
    network: arbitrum-one
    source:
      address: "0x6174B1868c61c05Eb520E06ad312E8dd0Af8C553"
      abi: DoryContract
      startBlock: 362672657

数据模型设计

User 实体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
type User @entity {
  id: ID!                    # 用户地址
  address: Bytes!            # 用户地址
  power: BigInt!             # 用户算力
  referreeCount: BigInt!     # 直接被推荐人数量
  bindingTime: BigInt!       # 绑定时间

  # 推荐关系
  referrees: [User!]! @derivedFrom(field: "referrer")
  referrer: User

  # 推荐关系记录
  referralRelationships: [ReferralRelationship!]! @derivedFrom(field: "referee")
  referrerRelationships: [ReferralRelationship!]! @derivedFrom(field: "referrer")
}

ReferralRelationship 实体

1
2
3
4
5
6
7
type ReferralRelationship @entity {
  id: ID!                    # 推荐关系ID
  referrer: User!            # 推荐人
  referee: User!             # 被推荐人
  timestamp: BigInt!         # 绑定时间
  isActive: Boolean!         # 是否活跃
}

事件处理器

推荐人设置处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
export function handleReferrerSet(event: ReferrerSet): void {
  let user = getOrCreateUser(event.params.account);
  let referrer = getOrCreateUser(event.params.referrer);

  // 检查是否已经存在推荐关系,避免重复计算
  if (user.referrer == null) {
    // 设置推荐人和绑定时间
    user.referrer = referrer.id;
    user.bindingTime = event.params.timestamp;
    user.save();

    // 更新推荐人的直接推荐人数量
    updateReferreeCount(referrer, true);
  }

  // 创建推荐关系记录
  let relationship = new ReferralRelationship(
    event.transaction.hash.toHexString() + "-" + event.logIndex.toString()
  );
  relationship.referrer = referrer.id;
  relationship.referee = user.id;
  relationship.timestamp = event.params.timestamp;
  relationship.isActive = true;
  relationship.save();
}

推荐人修改处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
export function handleReferrerModified(event: ReferrerModified): void {
  let user = getOrCreateUser(event.params.account);
  let oldReferrer: User | null = null;
  let newReferrer: User | null = null;

  // 获取旧推荐人
  if (user.referrer != null) {
    oldReferrer = User.load(user.referrer!);
  }

  // 设置新推荐人
  if (!event.params.newReferrer.equals(Address.zero())) {
    newReferrer = getOrCreateUser(event.params.newReferrer);
    user.referrer = newReferrer.id;
  } else {
    user.referrer = null;
  }

  user.save();

  // 创建新的推荐关系(如果新推荐人不为空)
  if (!event.params.newReferrer.equals(Address.zero())) {
    let relationship = new ReferralRelationship(
      event.transaction.hash.toHexString() + "-" + event.logIndex.toString()
    );
    relationship.referrer = newReferrer!.id;
    relationship.referee = user.id;
    relationship.timestamp = event.params.timestamp;
    relationship.isActive = true;
    relationship.save();
  }

  // 更新旧推荐人的直接推荐人数量
  if (oldReferrer) {
    updateReferreeCount(oldReferrer, false);
  }

  // 更新新推荐人的直接推荐人数量
  if (newReferrer) {
    updateReferreeCount(newReferrer, true);
  }
}

算力更新处理

1
2
3
4
5
export function handlePowerUpdated(event: PowerUpdated): void {
  let user = getOrCreateUser(event.params.account);
  user.power = event.params.newPower;
  user.save();
}

工具函数

用户创建与获取

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function getOrCreateUser(address: Address): User {
  let user = User.load(address.toHexString());
  if (!user) {
    user = new User(address.toHexString());
    user.address = address;
    user.power = BigInt.fromI32(0);
    user.referreeCount = BigInt.fromI32(0);
    user.bindingTime = BigInt.fromI32(0);
    user.save();
  }
  return user;
}

推荐人数量更新

1
2
3
4
5
6
7
8
function updateReferreeCount(user: User, increment: boolean): void {
  if (increment) {
    user.referreeCount = user.referreeCount.plus(BigInt.fromI32(1));
  } else {
    user.referreeCount = user.referreeCount.minus(BigInt.fromI32(1));
  }
  user.save();
}

数据统计与验证

推荐人数量重新计算

1
2
3
4
5
function recalculateReferreeCount(user: User): void {
  // 重置计数
  user.referreeCount = BigInt.fromI32(0);
  user.save();
}

推荐人数量验证

1
2
3
4
5
function validateReferreeCount(user: User): void {
  // 这里可以添加验证逻辑
  // 如果发现数量不匹配,可以重新计算
  user.save();
}

API 服务系统

技术架构

框架选择

  • **Web框架**:FastAPI
  • **异步支持**:httpx (异步HTTP客户端)
  • **数据验证**:Pydantic
  • **区块链交互**:Web3.py
  • **环境配置**:python-dotenv

项目结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
datre/
├── contracts/                    # 智能合约目录
│   ├── datre-token/             # Datre Token 合约
│   │   ├── Datre.sol           # 主合约文件 (ERC-20 + 推荐系统)
│   │   ├── artifacts/          # 编译产物
│   │   ├── hardhat.config.js   # Hardhat 配置
│   │   └── package.json        # 合约依赖
│   └── datre-hook/              # Uniswap v4 Hook 合约
│       ├── contracts/          # Hook 合约源码
│       ├── test/               # 测试用例
│       ├── scripts/            # 部署脚本
│       ├── hardhat.config.js   # Hardhat 配置
│       └── package.json        # Hook 依赖
├── ledger/                     # 数据同步服务
│   ├── program/                # 同步程序
│   │   ├── core/              # 核心模块
│   │   ├── sync/              # 同步模块
│   │   └── run.py             # 启动脚本
│   ├── config/                # 配置文件
│   └── database/              # 数据库初始化
├── service/                    # API 服务
│   ├── program/               # API 程序
│   │   ├── app.py            # FastAPI 应用
│   │   ├── db.py             # 数据库操作
│   │   └── run.py            # 启动脚本
│   └── config/               # 配置文件
├── subgraph/                   # 子图索引
│   ├── src/                  # 映射源码
│   ├── abis/                 # 合约 ABI
│   └── schema.graphql        # 数据模式
└── README.org                 # 项目文档

API 接口设计

用户信息查询接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@app.get("/v1/user", response_model=UserResponse)
async def get_user(address: str = Query(..., description="钱包地址")):
    """
    获取用户信息

    Args:
        address: 钱包地址(需要小写)

    Returns:
        用户信息,包括推荐人列表和绑定时间
    """

请求参数

  • address: 钱包地址(字符串,需要小写格式)

响应模型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class UserResponse(BaseModel):
    code: int = 0
    msg: str = "success"
    data: Optional[dict] = None

class User(BaseModel):
    id: str
    address: str
    power: float
    level: int = 0
    smallCommunityPower: float = 0.0      # 小区算力
    communityPower: float = 0.0           # 社区算力
    maxCommunityPower: float = 0.0        # 大区算力
    powerDecay: int = 0                   # 算力衰减
    expectedReward: float = 0.0           # 预计收益
    referreeCount: int
    referrees: List[Referree]

class Referree(BaseModel):
    id: str
    address: str
    power: float
    bindingTime: int
    communityPower: float  # 社区算力

核心业务逻辑

地址处理与验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 处理地址格式
address = address.lower()

# 添加0x前缀(如果没有)
if not address.startswith('0x'):
    address = '0x' + address

# 验证地址格式
if not Web3.is_address(address):
    return UserResponse(code=0, msg="无效的钱包地址格式", data={})

GraphQL 查询构建

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
query = """
{
  user(id: "%s") {
    id
    address
    power
    referreeCount
    referrees(first: 100) {
      id
      address
      power
      bindingTime
    }
  }
}
""" % address

数据格式转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 转换数据格式
referrees = []
for ref in user_data.get("referrees", []):
    ref_power = float(ref["power"])
    referrees.append(Referree(
        id=Web3.to_checksum_address(ref["id"]),
        address=Web3.to_checksum_address(ref["address"]),
        power=ref_power,
        bindingTime=int(ref["bindingTime"]),
        communityPower=ref_power  # 社区算力等于本人算力
    ))

# 按照绑定时间倒序排列
referrees.sort(key=lambda x: x.bindingTime, reverse=True)

配置管理

环境变量配置

1
2
3
4
5
6
# 加载环境变量
load_dotenv()

# 配置
SUBGRAPH_URL = os.getenv("SUBGRAPH_URL", "https://api.studio.thegraph.com/query/117650/dory/version/latest")
API_KEY = os.getenv("API_KEY", "3775070ab8c4f893227a8b8e42f00818")

CORS 配置

1
2
3
4
5
6
7
8
# 添加 CORS 中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

错误处理

HTTP 异常处理

1
2
3
4
5
if response.status_code != 200:
    raise HTTPException(
        status_code=500,
        detail=f"Subgraph request failed: {response.status_code}"
    )

GraphQL 错误处理

1
2
3
4
5
6
7
# 检查是否有错误
if "errors" in data:
    error_msg = data["errors"][0]["message"] if data["errors"] else "Unknown error"
    raise HTTPException(
        status_code=400,
        detail=f"GraphQL error: {error_msg}"
    )

用户不存在处理

1
2
3
4
5
6
7
# 检查用户是否存在
if not data.get("data", {}).get("user"):
    return UserResponse(
        code=0,
        msg="error",
        data={}
    )

部署配置

服务器配置

1
2
3
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8911)

依赖管理

1
2
3
4
5
6
7
# requirements.txt
fastapi
uvicorn
httpx
pydantic
python-dotenv
web3

数据流与事件处理

完整数据流

1
2
3
4
5
6
7
8
用户操作 → 智能合约 → 事件触发 → 子图监听 → 数据索引 → API查询 → 前端展示
    │           │           │           │           │           │
    │           │           │           │           │           └── 用户界面
    │           │           │           │           └── 业务逻辑处理
    │           │           │           └── GraphQL查询
    │           │           └── 实体创建/更新
    │           └── 状态变更
    └── 交易提交

事件处理流程

推荐人设置流程

  1. 用户调用 `setReferrer(address referrer)`
  2. 合约验证参数并设置推荐关系
  3. 触发 `ReferrerSet` 和 `ReferralRelationshipEstablished` 事件
  4. 子图监听事件并调用相应处理器
  5. 创建/更新 User 实体和 ReferralRelationship 实体
  6. 更新推荐人数量统计

节点购买流程

  1. 用户调用 `purchaseNode(NodeType nodeType)`
  2. 合约验证USDT余额和节点配置
  3. 转移USDT并分配算力
  4. 触发 `NodePurchased` 事件
  5. 子图监听事件并更新用户算力

算力更新流程

  1. 管理员调用 `setPower(address account, uint256 newPower)`
  2. 合约更新用户算力
  3. 触发 `PowerUpdated` 事件
  4. 子图监听事件并更新用户实体

数据一致性保证

事件顺序性

  • 使用 `event.transaction.hash + event.logIndex` 作为关系ID
  • 确保事件处理的顺序性和唯一性

状态同步

  • 实时监听合约事件
  • 自动更新相关实体状态
  • 支持数据重新同步和修复

错误恢复

  • 提供数据验证和修复函数
  • 支持手动重新计算统计数据
  • 异常情况的日志记录和告警

部署与运维

智能合约部署

部署环境要求

  • Solidity 编译器版本:^0.8.28
  • OpenZeppelin 合约库
  • 支持 UUPS 代理的网络
  • 足够的 Gas 费用

部署步骤

  1. 编译合约代码
  2. 部署代理合约
  3. 初始化合约参数
  4. 验证合约功能
  5. 设置权限和配置

升级流程

  1. 部署新的实现合约
  2. 调用 `upgradeToAndCall` 函数
  3. 验证升级后的功能
  4. 更新前端配置

子图部署

环境准备

  • 安装 Graph CLI
  • 配置 Graph Studio 账户
  • 准备 ABI 文件

部署命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 代码生成
graph codegen

# 构建子图
graph build

# 部署到 Graph Studio
graph deploy dory

# 本地测试
graph create --node http://localhost:8020 dory
graph deploy --node http://localhost:8020 --ipfs http://localhost:5001 dory

配置更新

  • 修改 `subgraph.yaml` 配置
  • 更新事件处理器
  • 重新部署子图

API 服务部署

环境要求

  • Python 3.8+
  • 虚拟环境管理
  • 依赖包安装

部署步骤

  1. 创建虚拟环境
  2. 安装依赖包
  3. 配置环境变量
  4. 启动服务

服务管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 安装依赖
pip install -r requirements.txt

# 启动服务
python main.py

# 后台运行
nohup python main.py > app.log 2>&1 &

# 停止服务
pkill -f "python main.py"

监控与维护

系统监控

  • 合约事件监听状态
  • 子图同步状态
  • API 服务响应时间
  • 数据库连接状态

日志管理

  • 合约调用日志
  • 子图处理日志
  • API 访问日志
  • 错误和异常日志

性能优化

  • 数据库查询优化
  • 缓存策略实施
  • 负载均衡配置
  • 资源使用监控

安全考虑

智能合约安全

权限控制

  • 所有者权限限制
  • 函数访问控制
  • 升级权限管理

输入验证

  • 地址格式验证
  • 数值范围检查
  • 循环引用检测

重入攻击防护

  • 使用 OpenZeppelin 的安全库
  • 状态变更顺序控制
  • 外部调用安全处理

数据安全

访问控制

  • API 接口认证
  • 子图访问限制
  • 数据权限管理

数据完整性

  • 事件数据验证
  • 状态一致性检查
  • 异常情况处理

隐私保护

  • 敏感信息脱敏
  • 访问日志记录
  • 数据泄露防护

网络安全

传输安全

  • HTTPS 协议使用
  • API 密钥管理
  • 请求频率限制

防护措施

  • DDoS 攻击防护
  • SQL 注入防护
  • XSS 攻击防护

测试策略

智能合约测试

单元测试

  • 函数功能测试
  • 边界条件测试
  • 异常情况测试

集成测试

  • 合约交互测试
  • 事件触发测试
  • 升级流程测试

安全测试

  • 权限控制测试
  • 攻击向量测试
  • 漏洞扫描测试

子图测试

功能测试

  • 事件处理测试
  • 数据模型测试
  • 查询接口测试

性能测试

  • 大量数据处理
  • 并发查询测试
  • 响应时间测试

API 测试

接口测试

  • 参数验证测试
  • 响应格式测试
  • 错误处理测试

负载测试

  • 并发请求测试
  • 压力测试
  • 稳定性测试

性能优化

智能合约优化

Gas 优化

  • 存储布局优化
  • 函数逻辑简化
  • 批量操作支持

计算优化

  • 算法复杂度控制
  • 循环次数限制
  • 外部调用优化

子图优化

查询优化

  • 索引策略优化
  • 查询复杂度控制
  • 缓存机制实施

存储优化

  • 数据模型优化
  • 存储结构优化
  • 清理策略实施

API 优化

响应优化

  • 异步处理优化
  • 缓存策略优化
  • 数据库查询优化

资源优化

  • 连接池管理
  • 内存使用优化
  • 并发处理优化

扩展性设计

水平扩展

负载均衡

  • 多实例部署
  • 负载分发策略
  • 健康检查机制

数据库扩展

  • 读写分离
  • 分片策略
  • 缓存层设计

垂直扩展

资源升级

  • CPU 和内存升级
  • 存储容量扩展
  • 网络带宽提升

架构优化

  • 微服务拆分
  • 消息队列引入
  • 分布式架构设计

功能扩展

新特性添加

  • 模块化设计
  • 插件机制
  • 配置化扩展

集成扩展

  • 第三方服务集成
  • 多链支持
  • 跨平台兼容

故障处理与恢复

常见故障类型

智能合约故障

  • 合约升级失败
  • 权限配置错误
  • 逻辑错误

子图故障

  • 同步中断
  • 数据不一致
  • 查询失败

API 服务故障

  • 服务崩溃
  • 数据库连接失败
  • 外部依赖异常

故障检测

监控告警

  • 健康检查
  • 性能指标监控
  • 异常事件告警

日志分析

  • 错误日志分析
  • 性能日志分析
  • 访问日志分析

故障恢复

自动恢复

  • 服务重启
  • 连接重试
  • 状态同步

手动恢复

  • 数据修复
  • 配置恢复
  • 服务重建

灾难恢复

备份策略

  • 数据备份
  • 配置备份
  • 代码备份

恢复流程

  • 恢复优先级
  • 恢复步骤
  • 验证流程

开发指南

开发环境搭建

环境要求

  • Node.js 16+
  • Python 3.8+
  • Solidity 0.8.28+
  • Git

合约开发环境

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 进入合约目录
cd contracts/datre-token

# 安装合约依赖
npm install

# 编译合约
npm run compile

# 运行测试
npm run test

DatreHook 开发环境

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 进入 Hook 合约目录
cd contracts/datre-hook

# 安装依赖
npm install

# 编译合约
npm run compile

# 运行测试
npm test

# 部署合约
npm run deploy

# 查找 CREATE2 盐(用于主网部署)
npm run find-salt

添加新合约模块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 创建新合约目录
mkdir contracts/new-contract

# 初始化新合约
cd contracts/new-contract
npm init -y

# 添加 Hardhat 依赖
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox

# 创建 hardhat.config.js 配置文件
# 添加合约源码和测试文件

工具安装

1
2
3
4
5
6
7
8
# 安装 Node.js 依赖
npm install -g @graphprotocol/graph-cli

# 安装 Python 依赖
pip install fastapi uvicorn httpx pydantic python-dotenv web3

# 安装 Solidity 编译器
npm install -g solc

代码规范

Solidity 规范

  • 使用 SPDX 许可证标识
  • 遵循 Solidity 风格指南
  • 添加完整的 NatSpec 注释
  • 使用安全的 OpenZeppelin 库

TypeScript 规范

  • 使用 TypeScript 严格模式
  • 遵循 ESLint 规则
  • 添加类型注解
  • 使用 async/await 语法

Python 规范

  • 遵循 PEP 8 代码风格
  • 使用类型注解
  • 添加文档字符串
  • 使用虚拟环境

测试规范

测试覆盖率

  • 智能合约测试覆盖率 > 90%
  • 子图处理器测试覆盖率 > 80%
  • API 接口测试覆盖率 > 85%

测试类型

  • 单元测试
  • 集成测试
  • 端到端测试
  • 性能测试

部署规范

环境管理

  • 开发环境
  • 测试环境
  • 生产环境

版本管理

  • 语义化版本控制
  • 变更日志维护
  • 回滚策略制定

常见问题与解决方案

智能合约问题

推荐人设置失败

问题描述**:用户无法设置推荐人

可能原因**:

  • 推荐人地址为零地址
  • 用户已设置过推荐人
  • 存在循环引用
  • Gas 费用不足

解决方案**:

  • 检查推荐人地址格式
  • 确认用户未设置过推荐人
  • 检查推荐链是否存在循环
  • 增加 Gas 费用

节点购买失败

问题描述**:用户无法购买节点

可能原因**:

  • USDT 余额不足
  • 节点数量已售罄
  • 节点购买功能未开启
  • 权限不足

解决方案**:

  • 检查 USDT 余额
  • 确认节点库存状态
  • 检查节点配置状态
  • 联系管理员

子图问题

数据同步失败

问题描述**:子图数据与链上数据不一致

可能原因**:

  • 子图服务中断
  • 事件处理失败
  • 网络连接问题
  • 配置错误

解决方案**:

  • 检查子图服务状态
  • 查看事件处理日志
  • 检查网络连接
  • 验证配置参数

查询响应慢

问题描述**:GraphQL 查询响应时间过长

可能原因**:

  • 数据量过大
  • 查询复杂度高
  • 索引缺失
  • 资源不足

解决方案**:

  • 优化查询语句
  • 添加适当索引
  • 实施分页查询
  • 增加计算资源

API 问题

服务无响应

问题描述**:API 服务无法访问

可能原因**:

  • 服务进程崩溃
  • 端口被占用
  • 防火墙阻止
  • 依赖服务异常

解决方案**:

  • 检查服务进程状态
  • 确认端口占用情况
  • 检查防火墙配置
  • 验证依赖服务

数据格式错误

问题描述**:API 返回数据格式不正确

可能原因**:

  • 子图数据异常
  • 数据转换错误
  • 模型定义不匹配
  • 编码问题

解决方案**:

  • 检查子图数据
  • 验证数据转换逻辑
  • 更新数据模型
  • 检查编码设置

未来规划

功能扩展

多链支持

  • 支持以太坊主网
  • 支持其他 L2 网络
  • 跨链数据同步
  • 多链资产管理

高级推荐系统

  • 多级推荐奖励
  • 动态奖励算法
  • 推荐人等级系统
  • 社区治理机制

治理功能

  • DAO 投票系统
  • 提案管理
  • 参数调整
  • 社区决策

技术升级

性能优化

  • 批量处理优化
  • 缓存机制优化
  • 数据库查询优化
  • 网络传输优化

安全增强

  • 多重签名支持
  • 时间锁机制
  • 紧急暂停功能
  • 安全审计增强

可扩展性

  • 微服务架构
  • 消息队列系统
  • 分布式存储
  • 负载均衡

生态建设

开发者工具

  • SDK 开发
  • API 文档完善
  • 开发模板
  • 测试工具

社区建设

  • 开发者社区
  • 用户社区
  • 技术论坛
  • 培训资源

合作伙伴

  • 钱包集成
  • DEX 集成
  • 数据分析平台
  • 安全审计机构

附录

相关链接

官方资源

  • 项目官网:[待补充]
  • 技术文档:[待补充]
  • GitHub 仓库:[待补充]
  • 社区论坛:[待补充]

第三方资源

术语表

区块链术语

  • **L2 (Layer 2)**:第二层扩展解决方案,如 Arbitrum
  • **Gas**:以太坊网络交易费用
  • **智能合约**:在区块链上自动执行的程序
  • **事件 (Event)**:智能合约发出的日志信息

推荐系统术语

  • **推荐人 (Referrer)**:邀请其他用户加入的用户
  • **被推荐人 (Referee)**:被邀请加入的用户
  • **推荐链 (Referral Chain)**:推荐关系的层级结构
  • **算力 (Power)**:用户在系统中的权重值

技术术语

  • **子图 (Subgraph)**:The Graph 协议的数据索引
  • **UUPS 代理**:可升级的智能合约模式
  • **GraphQL**:数据查询语言
  • **API**:应用程序编程接口

版本历史

v1.0.0 (当前版本)

  • 基础推荐人系统
  • 节点购买功能
  • 算力管理
  • 资金冻结系统
  • 子图索引
  • API 服务

计划版本

  • v1.1.0:多级推荐奖励
  • v1.2.0:治理功能
  • v2.0.0:多链支持

贡献指南

代码贡献

  • Fork 项目仓库
  • 创建功能分支
  • 提交代码变更
  • 创建 Pull Request

文档贡献

  • 完善技术文档
  • 添加使用示例
  • 翻译文档内容
  • 更新 API 文档

测试贡献

  • 编写测试用例
  • 发现和报告 Bug
  • 性能测试
  • 安全测试

许可证信息

  • 智能合约:MIT License
  • 子图代码:MIT License
  • API 服务:MIT License
  • 文档内容:MIT License

文档维护

更新记录

  • 2024-01-01:初始版本创建
  • [后续更新记录]

维护团队

  • 技术负责人:[待补充]
  • 文档维护:[待补充]
  • 社区支持:[待补充]

反馈渠道

  • 技术问题:GitHub Issues
  • 文档建议:文档仓库 Issues
  • 社区讨论:Discord/Telegram
  • 邮件联系:[待补充]

<div style=“text-align: center; margin-top: 50px; color: #666; font-size: 12px;"> <p>本文档由 Dory/Datre 项目团队维护</p> <p>最后更新时间:2024年</p> <p>文档版本:v1.0.0</p> </div>