Web3 AI API 平台设计:Credits、Points 与链上 Vault 合约

面向区块链用户的 AI API 平台,核心是弄清楚什么适合上链、什么必须留在链下。

Credits 与 Points 的区别

两者经常被混淆,但定位完全不同:

项目CreditsPoints
是否可消费是(API 扣费)
是否能提现
是否等价美元基本是不是
是否实时扣减一般不扣
后续用途付费 API 使用Token 空投、VIP 等级、DAO 治理

Credits 是”钱”,用来实时扣 API 费用。充值 100 USDT → 获得 100 credits。

Points 是”生态贡献值”,记录用户长期行为,为后续 Token 发行、空投、质押做准备。首版只记录,不承诺兑换比例。

Points 推荐规则(防刷)

充值不给 points(容易刷)
实际 API 消费给 points:1 USD 消费 = 1 point
邀请用户消费后返 points:被邀请人消费 100 USD → 邀请人 +10 points

提现时扣减对应 points,避免”充值即拿 points 然后提现”的空刷。

链上 vs 链下的边界

AI API 平台天然偏中心化,因为每秒有大量 streaming、token 级扣费、fallback 重试。

内容位置原因
充值记录链上不可篡改,用户可自证
钱包资金链上透明储备
Credits 余额链下高频修改,链上 Gas 不可接受
API token 计费链下streaming 无法逐 token 上链
Fallback/retry 逻辑链下毫秒级响应要求

链上充值的意义不是”绝对无法改余额”,而是让作恶留下公开证据:用户可以用 tx hash 证明确实充值了,平台无法赖账。

Vault 合约设计

首版只需要一个极简充值金库,不需要 upgradeable proxy、自动提现或 token 合约。

核心功能

  • 接收 BNB(depositBNB
  • 接收 ERC20(USDT/USDC,depositToken
  • 每笔充值绑定 orderId,供后端对账
  • 管理员提现(用 Safe 多签作为 owner)
  • Pausable + ReentrancyGuard

orderId 的必要性

不用 orderId 的话,用户直接 transfer 到合约,后端无法知道这笔钱对应哪个平台账户的哪个充值订单,难以补单和退款。

Solidity 实现

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";

contract VibeVault is Ownable, ReentrancyGuard, Pausable {
    using SafeERC20 for IERC20;

    mapping(address => bool) public allowedTokens;

    event DepositBNB(address indexed user, uint256 amount, bytes32 indexed orderId);
    event DepositToken(address indexed user, address indexed token, uint256 amount, bytes32 indexed orderId);
    event WithdrawToken(address indexed token, address indexed to, uint256 amount);
    event WithdrawBNB(address indexed to, uint256 amount);

    constructor(address initialOwner) Ownable(initialOwner) {}

    function setAllowedToken(address token, bool allowed) external onlyOwner {
        allowedTokens[token] = allowed;
    }

    function pause() external onlyOwner { _pause(); }
    function unpause() external onlyOwner { _unpause(); }

    function depositBNB(bytes32 orderId) external payable whenNotPaused {
        require(msg.value > 0, "invalid amount");
        emit DepositBNB(msg.sender, msg.value, orderId);
    }

    function depositToken(
        address token, uint256 amount, bytes32 orderId
    ) external whenNotPaused nonReentrant {
        require(allowedTokens[token], "token not allowed");
        require(amount > 0, "invalid amount");
        IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
        emit DepositToken(msg.sender, token, amount, orderId);
    }

    function withdrawToken(address token, address to, uint256 amount) external onlyOwner nonReentrant {
        require(to != address(0), "invalid to");
        IERC20(token).safeTransfer(to, amount);
        emit WithdrawToken(token, to, amount);
    }

    function withdrawBNB(address payable to, uint256 amount) external onlyOwner nonReentrant {
        require(to != address(0), "invalid to");
        (bool success,) = to.call{value: amount}("");
        require(success, "transfer failed");
        emit WithdrawBNB(to, amount);
    }
}

部署时 initialOwner 传入 Safe 多签地址,不要传 EOA 钱包,否则单私钥泄露即丢失全部资金。

链下 Credits 账本设计

Credits 必须在链下用数据库管理,但要做到可审计:

users: wallet_address, credits, frozen_credits, points
api_keys: user_id, key_hash, status
requests: user_id, model_tier, input_tokens, output_tokens, actual_cost, status
balance_logs: user_id, type(deposit/consume/refund/withdraw), amount, balance_before, balance_after, request_id

预扣费防并发刷余额

streaming 请求可能持续数分钟,必须先冻结余额:

请求开始 → 冻结 1 USD(frozen_credits += 1)
请求结束 → 按实际 token 数结算
实际费用 0.23 USD → 真正扣 0.23,解冻 0.77
上游失败 → 全部解冻,不扣费

Credits 存储用整数

内部:1 credit = 1,000,000 units(类似 USDC 的 6 位精度)
不要用 float,浮点误差会导致账单对不上

BSC 充值确认数建议

资产建议确认数
BNB6~12
USDT12
USDC12

后端监听 DepositBNBDepositToken 事件,达到确认数后入账 credits。

降低管理员作恶风险

  • Safe 多签(2/3):单私钥泄露无法提币
  • 热钱包只放小额:大额资金存 Safe
  • 每 6 小时上链余额 Merkle Root:用户可验证平台没有偷改余额
  • balance_logs 不可删除:所有扣费操作可完整审计