⚠️ 重要声明:本文仅为海外前沿 AI + 分布式技术架构科普,文中技术方案仅作技术研究演示,不构成任何产品试用、项目参与、投资建议。
引言
在创新密集的 Web3 赛道,去中心化社交经历了早期产品的探索与迭代,不少应用暴露出中心化平台带来的数据垄断问题。有一款搭建在 BNB Chain 之上的海外产品 MetYa,尝试结合 AI 大模型与分布式身份体系,探索全新的社交产品形态。
它瞄准相亲约会这一强需求场景,用 AI 大模型 + DePIN 硬件 + 去中心化身份思想重构社交产品,我们把这类技术组合的赛道称为 DatingFi(约会场景分布式应用)。
今天,我们用纯技术与产品工程视角,全方位拆解 MetYa 的底层逻辑、应用场景与核心代码实现。
一、行业痛点:传统约会软件的 "三座大山"
无论是传统的 Tinder、Soul,还是各类 Web2 相亲平台,都面临着无法根除的产品痛点:
- 虚假账号泛滥: 机器人账号横行,真人身份核验成本极高。
- 中心化平台垄断: 用户贡献了核心的注意力和社交数据,数据所有权完全归属平台,平台拥有全部数据处置权。
- 跨国交互与信任摩擦: 全球化交友场景下,跨语言沟通门槛高,用户隐私数据容易被平台集中收集。
MetYa 的解法是:产品体验由 AI 大模型提升,用户身份与数据主权依靠分布式身份体系实现。
二、核心落地场景:三大板块构建生态闭环
MetYa 并不是空中楼阁的概念项目,底层应用场景已经形成紧密的 "社交 ↔ 权限 ↔ 硬件算力" 闭环。
1. MeAi & DatingFi:AI 伴侣与全双工匹配
MetYa 拥有自研社交大语言模型(LLM),支持全双工交互(Full-Duplex Interaction) 。用户可以像跟真人视频一样,随时打断、随时插话,和 AI 虚拟伴侣实时对话。内置毫秒级 AI 跨语言同传,打破全球异国用户的跨语言壁垒。用户完成真人身份核验、保持社交活跃,可解锁平台更多高级功能权限。
2. DePIN 智能硬件节点
大模型的流媒体传输和推理计算开销巨大。MetYa 推出智能物理硬件盒子,采用 DePIN(去中心化物理基础设施网络) 模式。用户部署硬件,贡献本地闲置算力与带宽,参与社交大模型的分布式推理任务,以此解锁平台高级功能权限,降低中心化服务器的算力成本。
三、硬核技术架构:Solidity 0.8.28 + OpenZeppelin V5 极客实现
对于开发者而言,MetYa 的核心资产流转逻辑设计非常优雅。以下我们基于最新工业级标准 Solidity 0.8.28 与 OpenZeppelin V5 规范,还原其社交生态中的两大核心完备智能合约。
1. 项目代币:MetYaToken.sol(MY)
js
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MetYaToken is ERC20Capped, ERC20Burnable, Pausable, Ownable {
// V5 自定义错误,代替 require 以节省 Gas
error MintAmountExceedsCap();
error ContractIsPaused();
constructor(
uint256 maxSupply,
address poolTreasury,
address poolTeam
)
ERC20("MetYa Token", "MET")
ERC20Capped(maxSupply * 10**18)
Ownable(msg.sender)
{
// 部署时进行初始生态分配(假设总额的 40% 给国库,10% 给团队)
_mint(poolTreasury, (maxSupply * 40 / 100) * 10**18);
_mint(poolTeam, (maxSupply * 10 / 100) * 10**18);
}
// 允许所有者(或后续的挖矿/社交奖池合约)铸造代币
function mint(address to, uint256 amount) external onlyOwner whenNotPaused {
if (totalSupply() + amount > cap()) {
revert MintAmountExceedsCap();
}
_mint(to, amount);
}
// 紧急暂停机制
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
// 严格遵循 OpenZeppelin V5 的唯一级联更新钩子
function _update(address from, address to, uint256 value)
internal
override(ERC20, ERC20Capped)
whenNotPaused
{
super._update(from, to, value);
}
}
2. 不可转让身份凭证:MetYaIdentityNFT.sol (SBT)
在 DatingFi 生态中,用户的真人核验状态或高级 AI 伴侣资产,必须是不可转让的灵魂绑定凭证(SBT) ,防止账号买卖与冒名顶替。
js
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MetYaIdentityNFT is ERC721URIStorage, Ownable {
uint256 private _nextTokenId;
// V5 自定义错误
error TokenTransferProhibited();
error InvalidAddress();
event IdentityMinted(address indexed user, uint256 indexed tokenId, string tokenURI);
constructor() ERC721("MetYa Profile NFT", "METPROFILE") Ownable(msg.sender) {}
// 为用户铸造社交身份凭证
function mintIdentity(address user, string calldata tokenURI) external onlyOwner returns (uint256) {
if (user == address(0)) {
revert InvalidAddress();
// revert(InvalidAddress());
}
uint256 tokenId = _nextTokenId;
_nextTokenId++; // 原生自增,替代已废弃的 Counters 库
_safeMint(user, tokenId);
_setTokenURI(tokenId, tokenURI);
emit IdentityMinted(user, tokenId, tokenURI);
return tokenId;
}
// 重写 V5 的 _update 函数以锁定转账,使其变成"灵魂绑定(SBT)"
function _update(address to, uint256 tokenId, address auth) internal override returns (address) {
address from = super._update(to, tokenId, auth);
// 允许铸造(from==0)和销毁(to==0),但禁止任何用户间的二手转让
if (from != address(0) && to != address(0)) {
revert TokenTransferProhibited();
}
return from;
}
}
3. 5% 自动分账打赏与质押池:MetYaSocialPool.sol
实现 DatingFi 社交打赏核心,当 A 用户打赏 B 用户时,合约零信任自动扣除 5% 划归平台国库用于生态维护,95% 瞬间到达被打赏人钱包;同时包含社交质押增信逻辑。
js
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MetYaSocialPool is ReentrancyGuard, Ownable {
IERC20 public immutable metToken;
address public platformTreasury;
uint256 public constant FEE_PERCENT = 5; // 5% 平台抽成
struct StakeInfo {
uint256 amount;
uint256 startTime;
}
mapping(address => StakeInfo) public stakes;
// 自定义错误
error AmountMustBeGreaterThanZero();
error InsufficientBalance();
error NoActiveStake();
error TreasuryAddressCannotBeZero();
// 核心事件
event Staked(address indexed user, uint256 amount);
event Unstaked(address indexed user, uint256 amount, uint256 reward);
event Tipped(address indexed sender, address indexed receiver, uint256 grossAmount, uint256 netAmount, uint256 fee);
constructor(address _metToken, address _platformTreasury) Ownable(msg.sender) {
if (_platformTreasury == address(0)) revert TreasuryAddressCannotBeZero();
metToken = IERC20(_metToken);
platformTreasury = _platformTreasury;
}
// 1. 社交质押功能
function stake(uint256 amount) external nonReentrant {
if (amount == 0) revert AmountMustBeGreaterThanZero();
if (metToken.balanceOf(msg.sender) < amount) revert InsufficientBalance();
// 转移代币到本合约(需用户事先对本合约授权 approve)
metToken.transferFrom(msg.sender, address(currentContract()), amount);
// 如果之前已经有质押,先结算并累加
StakeInfo storage userStake = stakes[msg.sender];
if (userStake.amount > 0) {
uint256 pendingReward = calculateReward(msg.sender);
userStake.amount += amount;
// 简单逻辑:追加质押会重置时间,实际中可按加权计算
userStake.startTime = block.timestamp;
if (pendingReward > 0) {
metToken.transfer(msg.sender, pendingReward);
}
} else {
stakes[msg.sender] = StakeInfo({
amount: amount,
startTime: block.timestamp
});
}
emit Staked(msg.sender, amount);
}
// 2. 解除质押并领取本金与奖励
function unstake() external nonReentrant {
StakeInfo memory userStake = stakes[msg.sender];
if (userStake.amount == 0) revert NoActiveStake();
uint256 reward = calculateReward(msg.sender);
uint256 totalPayout = userStake.amount + reward;
delete stakes[msg.sender]; // 防重入的清空操作
metToken.transfer(msg.sender, totalPayout);
emit Unstaked(msg.sender, userStake.amount, reward);
}
// 3. 社交打赏(带 5% 自动抽成)
function tipUser(address receiver, uint256 amount) external nonReentrant {
if (amount == 0) revert AmountMustBeGreaterThanZero();
if (receiver == address(0)) revert TreasuryAddressCannotBeZero();
if (metToken.balanceOf(msg.sender) < amount) revert InsufficientBalance();
uint256 fee = (amount * FEE_PERCENT) / 100;
uint256 netAmount = amount - fee;
// 1. 扣除发送方的代币
metToken.transferFrom(msg.sender, address(currentContract()), amount);
// 2. 将 5% 路由至平台国库
metToken.transfer(platformTreasury, fee);
// 3. 将 95% 路由至被打赏人
metToken.transfer(receiver, netAmount);
emit Tipped(msg.sender, receiver, amount, netAmount, fee);
}
// 4. 辅助函数:根据时间计算利息(此处仅为演示用简易年化算法)
function calculateReward(address user) public view returns (uint256) {
StakeInfo memory userStake = stakes[user];
if (userStake.amount == 0) return 0;
uint256 duration = block.timestamp - userStake.startTime;
// 假设基础奖励率为:每秒释放质押资产的 0.00001%
return (userStake.amount * duration * 1) / 10000000;
}
// 5. 修改平台国库地址
function setPlatformTreasury(address _newTreasury) external onlyOwner {
if (_newTreasury == address(0)) revert TreasuryAddressCannotBeZero();
platformTreasury = _newTreasury;
}
// 替代 address(this) 的内部帮助函数以避开高版本下的某种限制
function currentContract() internal view returns (address) {
return address(this);
}
}
四、生态权限机制
产品运转的核心,是功能权限分配机制,没有金融通缩与资产增值逻辑:
- 高级功能解锁: 解锁更高级 AI 伴侣、高并发跨国音视频实时翻译,需要获得对应的平台功能访问权限。
- 社交行为权重: 用户在平台产生有效社交交互,链上记录行为数据,提升约会广场匹配权重。
- 分布式算力贡献: 用户部署 DePIN 硬件节点贡献算力带宽,可兑换平台高级功能权限。
五、完备测试脚本
- 完备测试脚本:MetYa Ecosystem Protocol Full Integration
- 初始分配验证:代币应根据配额正确分发到国库与团队
- SBT身份验证:成功铸造身份且任何二手转让行为均被拦截报错
- 社交打赏功能:打赏应自动执行 5% 平台扣费并精确分账
- 社交质押逻辑:用户质押并随时间获取利息收益
- 权限拦截验证:非所有者尝试修改国库地址或暂停代币应被拦截
js
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { parseEther, getAddress } from "viem";
import { network } from "hardhat";
describe("MetYa Ecosystem Protocol Full Integration", function () {
// 部署脚手架 Fixture
async function deployFixture() {
// ⭐ 修复警告:根据 Hardhat 3 规范改用 getOrCreate() 或 connect() 对应的规范,这里保持 viem 兼容
const { viem } = await (network as any).connect();
const [owner, treasury, team, userA, userB] = await viem.getWalletClients();
const publicClient = await viem.getPublicClient();
const testClient = await viem.getTestClient();
const maxSupply = 1000000000n; // 10 亿最大发行量
// 1. 部署核心代币 MetYaToken (传入10亿,国库地址,团队地址)
const metToken = await viem.deployContract("MetYaToken", [
maxSupply,
treasury.account.address,
team.account.address,
]);
// 2. 部署社交身份凭证 MetYaIdentityNFT (SBT)
const metNFT = await viem.deployContract("MetYaIdentityNFT");
// 3. 部署业务池与社交互利合约 MetYaSocialPool (传入代币地址和国库地址)
const socialPool = await viem.deployContract("MetYaSocialPool", [
metToken.address,
treasury.account.address,
]);
return {
metToken,
metNFT,
socialPool,
owner,
treasury,
team,
userA,
userB,
publicClient,
maxSupply,
testClient,
};
}
it("初始分配验证:代币应根据配额正确分发到国库与团队", async function () {
const { metToken, treasury, team, maxSupply } = await deployFixture();
const expectedTreasuryBal = (maxSupply * 40n / 100n) * parseEther("1");
const expectedTeamBal = (maxSupply * 10n / 100n) * parseEther("1");
const treasuryBalance = await metToken.read.balanceOf([treasury.account.address]);
const teamBalance = await metToken.read.balanceOf([team.account.address]);
assert.equal(treasuryBalance, expectedTreasuryBal, "国库初始代币(40%)不匹配");
assert.equal(teamBalance, expectedTeamBal, "团队初始代币(10%)不匹配");
});
it("SBT身份验证:成功铸造身份且任何二手转让行为均被拦截报错", async function () {
const { metNFT, owner, userA, userB } = await deployFixture();
const tokenURI = "ipfs://metya-user-identity-profile-metadata";
// 1. 平台方(owner)为 userA 铸造社交身份凭证
const tx = await metNFT.write.mintIdentity([userA.account.address, tokenURI], {
account: owner.account,
});
assert.ok(tx, "身份 NFT 铸造失败");
const ownerOfToken = await metNFT.read.ownerOf([0n]);
assert.equal(getAddress(ownerOfToken), getAddress(userA.account.address), "NFT 拥有者不匹配");
// 2. 拦截测试:验证 userA 无法将该 SBT 转让给 userB (触发 TokenTransferProhibited 自定义错误)
await assert.rejects(
async () => {
await metNFT.write.transferFrom([userA.account.address, userB.account.address, 0n], {
account: userA.account,
});
},
/TokenTransferProhibited/,
"SBT 应该禁止用户间进行二手转让"
);
});
it("社交打赏功能:打赏应自动执行 5% 平台扣费并精确分账", async function () {
const { metToken, socialPool, treasury, userA, userB } = await deployFixture();
const tipAmount = parseEther("100"); // 打赏 100 MET
// 1. 从国库中划转一部分代币给 userA 用于打赏测试
await metToken.write.transfer([userA.account.address, tipAmount], { account: treasury.account });
// 2. userA 授权给社交业务池合约
await metToken.write.approve([socialPool.address, tipAmount], { account: userA.account });
// 获取打赏前国库与被接收人的余额
const treasuryBalBefore = await metToken.read.balanceOf([treasury.account.address]);
const receiverBalBefore = await metToken.read.balanceOf([userB.account.address]);
// 3. userA 触发社交打赏给 userB
await socialPool.write.tipUser([userB.account.address, tipAmount], { account: userA.account });
// 计算预期值:5% 费率
const expectedFee = (tipAmount * 5n) / 100n; // 5 MET
const expectedNet = tipAmount - expectedFee; // 95 MET
const treasuryBalAfter = await metToken.read.balanceOf([treasury.account.address]);
const receiverBalAfter = await metToken.read.balanceOf([userB.account.address]);
assert.equal(treasuryBalAfter - treasuryBalBefore, expectedFee, "平台国库没有正确收到5%手续费");
assert.equal(receiverBalAfter - receiverBalBefore, expectedNet, "打赏接收者未收到95%的净代币额");
assert.equal(await metToken.read.balanceOf([userA.account.address]), 0n, "打赏发送方余额扣除不正确");
});
it("社交质押逻辑:用户质押并随时间获取利息收益", async function () {
const { metToken, socialPool, treasury, userA, testClient } = await deployFixture();
const stakeAmount = parseEther("1000");
// 1. 初始向 userA 注入测试代币并向合约注入挖矿奖池资金
await metToken.write.transfer([userA.account.address, stakeAmount], { account: treasury.account });
await metToken.write.transfer([socialPool.address, parseEther("50000")], { account: treasury.account });
// 2. 授权并执行质押
await metToken.write.approve([socialPool.address, stakeAmount], { account: userA.account });
await socialPool.write.stake([stakeAmount], { account: userA.account });
// 验证质押结构体数据
const stakeInfo = await socialPool.read.stakes([userA.account.address]);
assert.equal(stakeInfo[0], stakeAmount, "质押本金记录不匹配");
// 3. 模拟时间流逝(快进 1000 秒)
await testClient.increaseTime({ seconds: 1000 });
await testClient.mine({ blocks: 1 });
// 4. 读取链上预期利息(用于确保存储池确实在产生利息)
const pendingRewardBefore = await socialPool.read.calculateReward([userA.account.address]);
assert.ok(pendingRewardBefore > 0n, "随时间流逝应产生质押收益利息");
// 5. 执行解除质押
await socialPool.write.unstake({ account: userA.account });
// ✅ 修复核心:由于写操作交易打包上链时时间戳会动态向前滚动 1 秒(变成1001秒结算)
// 我们只需要验证最终拿到的总额【大于或等于】当初 1000 秒时预测的利息总和,且误差保持在 1 秒利息之内
const finalBalance = await metToken.read.balanceOf([userA.account.address]);
const expectedMinimum = stakeAmount + pendingRewardBefore;
assert.ok(finalBalance >= expectedMinimum, "解除质押后拿到的金额少于预期利息值");
// 允许 1~2 秒的极小时间块清算冗余偏差
const variance = finalBalance - expectedMinimum;
assert.ok(variance < parseEther("0.1"), "利息偏差超出合理区块开采时间范围");
});
it("权限拦截验证:非所有者尝试修改国库地址或暂停代币应被拦截", async function () {
const { metToken, socialPool, userA, userB } = await deployFixture();
// 1. 非 Owner 尝试修改社交池的国库地址
await assert.rejects(
async () => {
await socialPool.write.setPlatformTreasury([userB.account.address], { account: userA.account });
},
/OwnableUnauthorizedAccount/,
"非所有者不应被允许修改平台国库地址"
);
// 2. 非 Owner 尝试暂停代币合约
await assert.rejects(
async () => {
await metToken.write.pause({ account: userA.account });
},
/OwnableUnauthorizedAccount/,
"非所有者不应被允许暂停代币合约交易"
);
});
});
六、平台合规性与安全性剖析
合规与安全永远是底线。MetYa 在隐私安全方向的设计值得参考:
- 定位: 产品核心是 AI 社交工具与分布式身份系统,链上合约仅用于身份确权、社交行为存证、功能权限管理,不涉及任何金融资产。
- 隐私安全防护: 用户聊天敏感数据、生物特征数据不上中心化服务器,通过边缘计算 + 零知识证明(ZKP)在节点本地加密,符合国际 GDPR 隐私规范。
总结:去中心化社交的破局示范
MetYa 的探索证明一件事:AI + 分布式社交产品,核心是对人类底层刚需(约会、陪伴、社交数据主权)的洞察。它用 AI 提升产品体验,用 Solidity 合约实现用户身份确权,用 DePIN 分布式算力降低服务成本。 这是 AI 与区块链技术融合在社交场景的技术探索,为同类分布式应用提供一份工程实现参考。