Mini App(草案规范)
从代币发射台,到应用发射台。
Flap 将不只是代币的发射台 —— 借助 Mini App,它也将成为应用的发射台。你只需交付合约;UI 和后端由 Flap 托管,让你的应用持续运行。让你的应用不可停止。
愿景
如今,在 Flap 上构建的一切都从代币开始。Mini App 取消了这一前提。任何拥有智能合约的项目 —— 游戏、工具、发射台,或者 Vault —— 都可以在 Flap 上线,并获得与当今机制代币相同的保障:
- 经过审计的合约,由 Flap 及其合作安全公司审查。
- 由 Flap 托管的 UI,即使创建者离开,应用也不会消失。
- 由 Flap 托管的后端,通过共享基础设施实现,开发者无需自己维持服务器运行。
路线图
| 阶段 | 内容 | 状态 |
|---|---|---|
| 1. Vault + Vault UI | 代币税费流入自定义合约;Flap 在代币页面托管其 UI | 已上线 |
| 2. Mini App v2 开发者预览 | 无需代币的独立应用,使用模板的 Mini App v2 模式构建 | 开发者预览中 |
| 3. Flap 共享索引器(FSI) | 由 Flap 托管的后端:索引并查询任意合约的事件 | Beta —— BNB Chain 测试网 → BNB Chain 主网 → Robinhood Chain |
| 4. Flap Apps 正式上线 | 通过审计的 Mini App 发布在 flap.sh/apps/{slug},并列入 Flap 应用目录 | 即将推出 |
| 5. 更多共享基础设施 | 根据 Mini App 开发者的需求增加新的后端能力 | 持续进行 —— 欢迎告诉我们你的需求 |
草案。 本页介绍 Flap Mini App 的方向与草案规范。边界有意保持开放:草案阶段我们会对每个 Mini App 逐案审核,本页细节在正式发布前可能调整。
目录
- 愿景
- 路线图
- 从代币发射台到应用发射台
- 为什么由 Flap 托管机制
- 什么是 Mini App?
- Vault 与 Mini App 对比
- 架构
- 三个层次
- Flap 共享索引器(FSI)
- 逐步构建 Mini App
- 待定问题
从代币发射台到应用发射台
Flap 最初是一个代币发射台。借助 Vault,代币的税费会流入一个智能合约,开发者在其中实现自定义逻辑;借助 Vault UI,这套逻辑还能拥有自己的界面 —— 并由 Flap 托管。
但 Vault 模型有一个结构性限制:必须先有代币。 先发射代币,然后才有 Vault,然后 Vault 才能有 UI。很多应用并不符合这种形态:
- 工具(看板、计算器、批量工具),本身没有代币;
- 基于 Flap 构建的发射台,帮别人发币,但自己没有代币;
- 游戏,用户直接与其合约交互;
- 任何其他只是拥有一个用户想要交互的智能合约的应用。
这些应用同样有合约、同样需要后端,也同样需要一个用户可以信赖、会一直可用的 UI。Mini App 将 Vault + Vault UI 模型扩展到这些场景。我们的目标是让 Flap 成为应用的发射台,而不仅仅是代币的发射台 —— 并且让 Flap 的基础设施使你的应用不可停止(unstoppable)。
为什么由 Flap 托管机制
几乎所有使用 Vault 的代币都在 Vault 中内置了某种机制 —— 分红、回购、游戏、基于社交证明的资金路由。我们把这类代币称为机制代币(mechanism tokens)。它们最大的痛点是持久性:
- 如果 UI 托管在创建者自己的服务器上,一旦创建者跑路、失去兴趣或不再付费托管,机制就会失效。
- 如果机制依赖自定义后端(索引器、数据库、keeper),后端一宕机,机制就会失效。
- 用户无法判断第三方 UI 背后的合约是否安全。
Flap 消除了这些故障点:
| 风险 | Flap 的应对方式 |
|---|---|
| 合约不安全或过于中心化 | 上架前由 Flap 及其合作安全公司审查并审计合约 |
| UI 消失 | UI 由 Flap 审核、构建并托管 |
| 后端消失 | 后端需求由 Flap 共享基础设施(索引、调度、预言机、验证器)提供,而不是开发者自己运维的服务器 |
开发者只需关注合约本身,并在合约中集成 Flap 的基础设施,使应用尽可能去中心化。用户也不必担心机制会因为服务器宕机或合约无人维护而失效:合约已经过 Flap 及审计合作方的检查,UI 和后端由 Flap 运行。
什么是 Mini App?
在本草案中,Mini App 指:
- UI 和后端都由 Flap 托管的应用。 它发布在 Flap 上自己的 URL(
https://flap.sh/apps/{slug}),存在的前提不需要代币、Vault 或工厂合约。 - 可由任意智能合约支撑。 合约可以是 Vault,但不必是。游戏合约、工具的辅助合约、发射台合约都可以。
- 必须经过审计。 合约必须通过审计。我们与合作安全公司合作,由他们审计你的代码。
就这些。我们没有为 Mini App 能做什么划定硬性边界 —— 草案阶段保持定义开放,逐案审核提交的应用。
Vault 与 Mini App 对比
| Vault + Vault UI | Mini App | |
|---|---|---|
| 是否需要代币 | 是 —— Vault 接收代币的税费 | 否 |
| 合约 | 必须实现 Vault 规范 | 任意合约(也可以是 Vault) |
| 审计 | 可选;获得低风险徽章时必需 | 必需 |
| UI 托管 | 由 Flap 托管在代币的 Vault 面板 | 由 Flap 托管在 flap.sh/apps/{slug} |
| UI 身份 | 绑定到工厂 / Vault / 代币(match.bindings) | 稳定的 artifact ID + 不可变的 slug;无绑定 |
| 后端 | Flap 共享基础设施 | Flap 共享基础设施,包括 Flap 共享索引器 |
| 产品入口 | 代币页面 | Flap 应用目录(Apps catalog) |
一个项目可以同时交付两者:为其代币提供 Vault UI,以及一个独立的 Mini App。两者分别审核、分别发布,但可以复用相同的合约以及大部分 UI 代码。
架构
flowchart LR
U[用户] -->|flap.sh/apps/slug| UI
subgraph Flap["由 Flap 托管"]
UI[Mini App UI<br/>由 Flap Workbench 审核并构建]
FSI[Flap 共享索引器 FSI<br/>索引并提供合约事件数据]
INFRA[其他共享基础设施<br/>Trigger Service、AI Oracle、<br/>X General Verifier、FlapVaultEx]
end
subgraph Chain["链上"]
C[你的已审计合约]
end
UI -->|通过 Flap SDK<br/>读取、模拟、写入| C
C -->|发出 FSI 事件| FSI
UI -->|查询排行榜、<br/>信息流、活动| FSI
C <-->|回调 / 证明| INFRA
所有业务状态以及所有涉及资金流动的规则都存在于你的合约中。合约之外的一切由 Flap 托管。
三个层次
1. 合约 —— 任何通过审计的合约
你的合约可以实现任意逻辑。本草案中唯一的硬性要求是:在 Mini App 发布前,合约必须通过 Flap 合作安全公司之一的审计。请联系 Flap 团队安排审计。
设计合约时请注意:
- 把重要的东西都放在链上。 一切涉及资金的逻辑都必须在链上强制执行、可在链上读取。链下数据(包括 FSI)只是用于展示的缓存,绝不能作为结算依据。
- 优先使用 Flap 共享基础设施,而不是自建服务器。 如果你需要调度器、AI 决策或社交证明校验,请使用现有服务 —— 参见 Vault 开发者快速入门 § 3。这些服务适用于任何合约,不仅限于 Vault。
- 为 UI 需要列出、排序或分页的所有数据发出 FSI 事件(见下文),这样你永远不需要运行自己的索引器。
2. 后端 —— 由 Flap 托管
大多数应用需要后端只有一个原因:把链上事件转换为 UI 可以查询的数据(排行榜、用户历史、信息流、活动日志)。你无需运行自己的索引器、数据库和 API,只需让合约发出一小组标准事件,由 Flap 为你建立索引 —— 这就是 Flap 共享索引器(FSI)。
其他后端需求由现有的 Flap 服务覆盖:
| 如果你需要…… | 使用…… |
|---|---|
| 查询合约数据的列表、排名和历史 | Flap 共享索引器 |
| 调度延迟执行或防 MEV 的回调 | Flap Trigger Service |
| 在链上获得可验证的 LLM/AI 决策 | Flap AI Oracle |
| 验证某个 X 账号发布了指定文本 | X General Verifier |
| 无需编写 DEX 路由即可将任意代币兑换为另一种代币 | FlapVaultEx |
| 在链上生成 AI 视频 | Flap Video Generator |
如果你的应用需要的后端能力上述服务都无法覆盖,请告诉我们 —— 这正是我们希望补充的基础设施。
3. UI —— 由 Flap 托管
Mini App 的 UI 使用 flap-vault-component-template 构建 —— 与 Vault UI 使用同一个模板,采用其独立的 Mini App v2 模式。开发者预览版位于模板的 feat/mini-app-v2 分支 —— 请从 Mini App v2 快速入门 和 分步教程 开始。
要点:
-
受控的业务组件,而不是自由形式的网站。 与 Vault UI 一样,App 是
src/vaults/{slug}/下的一个 React 组件(Component.tsx、manifest.json、VaultABI.ts、i18n.json)。相同的安全边界同样适用:不得直接访问钱包 provider,不得使用未声明的端点,不得有隐藏的交易目标。 -
独立身份。 manifest 声明独立(standalone)模型和不可变的
slug,不绑定任何代币/工厂/Vault:{ "schemaVersion": 2, "mode": "mini-app", "appModel": "standalone", "slug": "my-app", "artifactId": "vaultui_my-app_01ARZ3NDEKTSV4RRFFQ69G5FAV", "name": "My App", "displayTitle": { "en": "My App", "zh": "我的应用" }, "match": { "bindings": [] }, "i18n": ["en", "zh"] } -
经审核的钱包权限。 App 不绑定代币,而是精确声明它可以调用哪些链(
walletChains)以及哪些合约和函数签名(walletContracts),并设定原生币数额和 ERC20 授权的上限。按代币动态变化的目标须通过链上 resolver 解析。超出声明策略的任何交易都会被宿主拒绝。 -
统一 SDK,钱包由宿主提供。 App 通过
useFlapSdk({ chainId })进行读取、模拟、写入和获取回执,使用用户在 Flap 上已连接的钱包。App 不得自行实现钱包连接。 -
检查、测试、打包、审核。
yarn app:scaffold→yarn app:check→yarn app:e2e→yarn app:package→yarn app:verify-package,然后将生成的 ZIP 提交到 Flap App Workbench。Flap 审核、构建后将其发布在https://flap.sh/apps/{slug}。UI 无需任何链上注册。
早期绑定代币的 Mini App 模式(绑定到 8888 代币)已不再用于新开发。新的 Mini App 请使用本文所述的独立 v2 模型。
Flap 共享索引器(FSI)
Beta。 FSI 目前处于 beta 阶段。下文的事件签名为 fsi-v1 模板。上线顺序为 BNB Chain 测试网(97)→ BNB Chain 主网(56)→ Robinhood Chain(4663)。我们暂不公开 FSI 端点 —— 如需访问或参与测试,请联系我们。
合约只要按 FSI 的固定模板发出事件,其数据即可在链下被查询和排序 —— 排行榜、已领取金额、轮次统计、帖子时间线、领取 / 复投 / 销毁日志 —— 开发者无需运行索引器、数据库或查询 API。 Flap 在每条链上运行一个索引器,监听这些模板、存储每一行数据,并通过 HTTP 提供给你的 Mini App UI、Vault UI 或任何第三方。
FSI 适用于任何合约 —— Vault、工厂,或 Mini App 的业务合约 —— 规则完全相同,无需注册。
模板
| 需求 | 模板 | 事件 | 主键 | 存储内容 |
|---|---|---|---|---|
| 某地址当前的余额、积分、已领取金额或份额 | A | FlapSharedIndexerAddrUint | (emitter, ns, key) | 每个地址一个最新值 |
| 同上,但按轮次 / epoch 划分 | B | FlapSharedIndexerUintAddrUint | (emitter, ns, round, account) | 每个轮次 + 账户一个最新值 |
| 必须保留的有序历史:帖子及其更新、公告、回购记录 | L | FlapSharedIndexerFeed | (emitter, ns, id, seq) | 每一条记录,序号由合约分配 |
| 低成本活动日志:领取、复投、销毁、自动领取、单笔交易 | G | FlapSharedIndexerLog | (emitter, txHash, logIndex) | 每一条日志,以其链上位置为键;合约不为其存储任何状态 |
event FlapSharedIndexerAddrUint(bytes32 ns, address key, uint256 value, string data); // A
event FlapSharedIndexerUintAddrUint(bytes32 ns, uint256 round, address account, uint256 value, string data); // B
event FlapSharedIndexerFeed(bytes32 ns, uint256 id, uint256 seq, address actor, address related, uint256 value, string data); // L
event FlapSharedIndexerLog(bytes32 ns, uint256 id, address actor, address related, uint256 amount, uint256 extra); // G| 模板 | topic0 |
|---|---|
| A | 0x6d92bb4f14ee2a433bdba0d337dc697cf6017066878befb1a42edf326092dba3 |
| B | 0x9cdf0eeb5c96afc4a86997e5b946603d187dfc2f1119862ad053c413de36dd28 |
| L | 0x262063c773bcbfa80d8648888e2f288454339e80d08c1c8f78aa6e55c2db75cb |
| G | 0x0483a030f7f57a91177910ed00ae51f19170eb9db2129394c858e7e3bc188aed |
所有参数都不使用 indexed(以节省 gas)。索引器只按 topic0 过滤,其余字段在后端解码。不匹配任何模板的事件会被忽略。
| 字段 | 含义 |
|---|---|
ns | 命名空间(bytes32)。同一合约用不同的 ns 区分多张逻辑表,例如 "points" 和 "claimed"。只需要一张表时可用 bytes32(0)。 |
key(A) | 行主键(地址)。 |
round、account(B) | 轮次 / epoch / ID,以及用户地址。纯轮次统计(轮次总量、全局计数器)使用 account = address(0)。 |
id、seq(L) | 记录所属对象(例如帖子 ID),以及该对象内的序号 —— 由合约分配:首条记录为 0,之后依次为 1、2…… |
id(G) | 所属对象或类型码,用于过滤(某个池子、某个 epoch,0 = 领取 / 1 = 复投)。 |
actor、related(L、G) | 提交者 / 操作者,以及另一个绑定地址(例如受益人)。不使用时传 address(0)。 |
value(A、B、L) | A、B:该行的当前绝对值,可排序。L:排序字段,通常为 block.timestamp。 |
amount、extra(G) | 本次操作的数值(非累计值)。amount 可排序、可过滤;extra 原样返回。 |
data(A、B、L) | 可选字符串,不可排序,原样返回。超过 1024 字节的部分会被截断;建议控制在 256 字节以内。L 中通常为内容 JSON 的 CID。 |
始终适用的两条规则
- 事件必须由你的合约自身发出。 索引器从
log.address取得emitter,因此只有你的合约能写入你合约的数据行。 - FSI 是链下缓存,绝不能作为结算依据。 涉及资金的逻辑(例如用户可领取多少)必须读取链上状态。绝不能依据 FSI 的响应进行结算。
基础合约
Flap 提供一个无依赖的抽象合约 FlapSharedIndexerV1。继承它并使用其辅助函数 —— 它们让"发出绝对值"成为默认行为:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
abstract contract FlapSharedIndexerV1 {
event FlapSharedIndexerAddrUint(bytes32 ns, address key, uint256 value, string data);
event FlapSharedIndexerUintAddrUint(bytes32 ns, uint256 round, address account, uint256 value, string data);
event FlapSharedIndexerFeed(
bytes32 ns, uint256 id, uint256 seq, address actor, address related, uint256 value, string data
);
event FlapSharedIndexerLog(
bytes32 ns, uint256 id, address actor, address related, uint256 amount, uint256 extra
);
function flapSharedIndexerSpecVersion() public pure virtual returns (string memory) {
return "fsi-v1";
}
/// Template A: absolute value for one address.
function _fsiSetAddr(bytes32 ns, address key, uint256 absoluteValue) internal {
emit FlapSharedIndexerAddrUint(ns, key, absoluteValue, "");
}
/// Template B: absolute value for one user in one round. Pass account = address(0) for pure round stats.
function _fsiSetRound(bytes32 ns, uint256 round, address account, uint256 absoluteValue) internal {
emit FlapSharedIndexerUintAddrUint(ns, round, account, absoluteValue, "");
}
/// Template L: seq is unique under the same (ns, id). 0 is the first record. value is block.timestamp.
function _fsiAppend(bytes32 ns, uint256 id, uint256 seq, address actor, address related, string memory data)
internal
{
emit FlapSharedIndexerFeed(ns, id, seq, actor, related, block.timestamp, data);
}
/// Template G: no sequence number required; the contract stores no counter.
function _fsiLog(bytes32 ns, uint256 id, address actor, address related, uint256 amount, uint256 extra)
internal
{
emit FlapSharedIndexerLog(ns, id, actor, related, amount, extra);
}
}flapSharedIndexerSpecVersion() 声明合约所基于的 FSI 版本。开发前请先确认目标链已支持你所用的模板(GET /fsi/versions,见查询 FSI)。
写入语义
模板 A 和 B —— 最新绝对值。 对每个主键,索引器只保留最新值。它不累加增量,也不保留历史。
value必须是状态更新完成后的当前值。先更新存储,再把从存储读回的值写入事件。- 只有链上位置
(block, logIndex)严格更晚的事件才会覆盖已存储的值。更旧的或重复投递的事件不会让数据回滚。同一区块内,logIndex最大的事件生效。 - 写错了只需再发一次正确的当前值即可修正 —— 无需数据迁移。
- 没有删除事件:发出
value = 0即视为清空该行(该行仍占用配额;查询时可用minValue=1过滤掉)。
| 错误(增量) | 正确(绝对值) |
|---|---|
_fsiSetAddr("points", user, 50),其中 50 是本次增量 | _fsiSetAddr("points", user, points[user]) |
原样复用 ERC20 Transfer(from, to, amount) | 为 from 和 to 各发一个事件,值为转账后各自的余额 |
Claimed(user, amount) | _fsiSetAddr("claimed", user, claimed[user]) —— 累计已领取金额 |
模板 L —— 只追加。 每个 (emitter, ns, id, seq) 都是一条独立记录;原始记录和每次更新都会被保留。主键重复时,链上最早出现的记录生效,后来的重复事件会被丢弃 —— 因此追加时务必使用新的 seq。没有删除操作;如需隐藏某个对象,可用模板 B 维护一个标记(例如 ns = "hidden"、round = id、account = address(0)、value = 1)。创建或追加的权限由你的合约控制,索引器不做权限检查。
模板 G —— 只追加,无需序号。 每条日志按 (txHash, logIndex) 只存储一次;一笔交易可以发出多条日志。G 事件不写入任何链上状态,每条约消耗 2,000–3,000 gas。记录无法编辑或删除 —— 如有错误,请按你自定义的约定再发一条更正记录。发生链重组时,被回滚区块中的日志会被删除,并按新的规范链重新写入;合约无需处理。累计值请用 A/B;G 用于记录单次操作的数值。
示例:同时使用四个模板的 Mini App 合约
一个小游戏,记录累计积分(A)、每轮得分与轮次总分(B)、帖子及仅作者可追加的更新(L),以及领取日志(G):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {FlapSharedIndexerV1} from "./FlapSharedIndexerV1.sol";
contract MiniGame is FlapSharedIndexerV1 {
bytes32 constant NS_POINTS = "points";
bytes32 constant NS_SCORE = "score";
bytes32 constant NS_POST = "post";
bytes32 constant NS_CLAIM = "claim";
mapping(address => uint256) public points;
mapping(uint256 => mapping(address => uint256)) public score;
mapping(uint256 => uint256) public totalScore;
uint256 public postCount;
mapping(uint256 => uint256) public updateCount;
mapping(uint256 => address) public authorOf;
function play(uint256 round, uint256 delta) external {
// A: lifetime points — absolute value after the update
points[msg.sender] += delta;
_fsiSetAddr(NS_POINTS, msg.sender, points[msg.sender]);
// B: this user's score this round, plus a pure round stat (account = address(0))
score[round][msg.sender] += delta;
totalScore[round] += delta;
_fsiSetRound(NS_SCORE, round, msg.sender, score[round][msg.sender]);
_fsiSetRound("roundTotal", round, address(0), totalScore[round]);
}
// L: a post (seq 0) followed by ordered updates (seq 1, 2, 3...)
function createPost(string calldata cid) external returns (uint256 postId) {
postId = ++postCount;
authorOf[postId] = msg.sender;
_fsiAppend(NS_POST, postId, 0, msg.sender, address(0), cid);
}
function appendUpdate(uint256 postId, string calldata cid) external {
require(authorOf[postId] == msg.sender, "not author"); // permissions are enforced by the contract
_fsiAppend(NS_POST, postId, ++updateCount[postId], msg.sender, address(0), cid);
}
// G: one activity log per claim — the value of this action, not a cumulative total
function claim(uint256 poolId) external {
uint256 amount = _payout(msg.sender, poolId);
_fsiLog(NS_CLAIM, poolId, msg.sender, address(0), amount, 0);
}
function _payout(address, uint256) internal virtual returns (uint256) {
return 0; // your payout logic
}
}仅凭这些事件,Mini App UI 就能展示全局积分排行榜、每轮排行榜、带完整更新历史的帖子信息流,以及"我的领取记录"列表 —— 无需开发者运行任何后端。
查询 FSI
每条链一个 FSI 实例;路径中不包含链 ID(实例所在的链由 GET /fsi/versions 返回)。地址使用小写十六进制。
GET /fsi/versions
GET /fsi/v1/{emitter}/A/{ns}?sort=value_desc&limit=100&cursor=&minValue=&maxValue=
GET /fsi/v1/{emitter}/A/{ns}/{key}
GET /fsi/v1/{emitter}/B/{ns}?round=7&sort=value_desc&limit=100&cursor=
GET /fsi/v1/{emitter}/B/{ns}/{round}/{account}
GET /fsi/v1/{emitter}/L/{ns}?seq=0&actor=&related=&sort=value_desc&limit=100&cursor=
GET /fsi/v1/{emitter}/L/{ns}/{id}?limit=100&cursor=
GET /fsi/v1/{emitter}/G/{ns}?id=&actor=&related=&minValue=&maxValue=&sort=time_desc&limit=100&cursor=
GET /fsi/v1/{emitter}/G/{ns}/{id}?limit=100&cursor=
GET /fsi/v1/{emitter}/G?ns=&id=&actor=&related=&sort=time_desc&limit=100&cursor=
GET /fsi/v1/by-key/{address}?templateId=A|B|L|G
主要行为:
{ns}可以是可读字符串(例如points,编码为左对齐、右侧补零的bytes32),也可以是0x前缀的 64 位十六进制bytes32。limit默认 100,最大 1000。使用返回的nextCursor翻页。列表可能会缓存几秒。- A / B 支持按
value_desc(默认)、value_asc、updated_desc或key排序。只有按值排序的列表才返回rank(B 必须带round);值相同的行排名相同。若某个 key 从未写入,单行查询返回404 {"error":"not_found"}。 - L 列表支持
seq=0(只返回每个对象的首条记录,例如帖子列表)以及actor/related过滤。详情接口按seq升序返回某个对象的全部记录。 - G 列表可按
id/actor/related过滤,用minValue/maxValue过滤amount,并支持按time_desc(默认)、time_asc、value_desc或value_asc排序。GET /fsi/v1/{emitter}/G会把同一合约的所有ns合并为一条活动流。 - by-key 返回某个地址在所有 emitter 下的数据行(L 和 G 支持
role=actor|related|both)。 - 数值字段(
value、amount、extra、id、round)以十进制字符串返回,以保留uint256精度。每一行都带有blockNumber、logIndex、txHash和updatedAt,便于调用方判断数据新鲜度。 - REST 只返回已确认的数据(
confirmed: true)。短暂的链重组期间,结果可能暂时回到分叉前的值,随后自动恢复。
示例 —— 积分排行榜(GET /fsi/v1/{emitter}/A/points):
{
"emitter": "0xabc...",
"template": "A",
"ns": "points",
"items": [
{
"emitter": "0xabc...", "ns": "points", "key": "0x111...", "value": "12345", "data": "",
"blockNumber": 123, "logIndex": 4, "txHash": "0x...", "updatedAt": 1760000000,
"confirmed": true, "trusted": false, "rank": 1
}
],
"nextCursor": "..."
}索引范围、trusted 与配额
- 任何发出匹配事件的合约都会被索引。 无需在 VaultPortal 注册 —— Vault 与非 Vault 合约(工厂统计、Mini App 业务合约)遵循相同规则。
- 每一行都带有
trusted标记:emitter 是在 VaultPortal 注册的 Vault 时为true,否则为false。它仅供参考 —— 不影响索引、查询、排序或配额。如果 Vault 在发出事件之后才完成注册,其已有数据不变,只是查询结果中的trusted随之变为true。
草案默认配额(对所有 emitter 相同;测试网阶段可能调整):
| 限制 | 默认值 |
|---|---|
data 长度 | 超过 1024 字节截断 |
每个 emitter 的 ns 数量 | 32 |
每个 (emitter, ns) 的行数 | 1,000,000 |
超过 ns 数量或行数上限的事件会被丢弃;命名空间写满后,已有的 A/B 键仍可更新,但新键会被拒绝。L 和 G 的行只增不减 —— 高频合约请按类型或时间段拆分到多个命名空间,同时注意每个 emitter 最多 32 个 ns。
版本
- FSI 整体按版本发布(
fsi-v1、fsi-v2……)。每个版本都是前一版本的超集。 - 模板一经发布即冻结:事件签名、表结构和写入规则都不再改变。新行为以新模板的形式发布。
/fsi/v1/...路径长期可用,合约永远无需迁移。 fsi-v1= 模板 A、B、L、G。后续模板(复合键、两个地址之间的关系、双指标行、显式删除事件)已预留,但暂未索引。
逐步构建 Mini App
- 尽早与我们沟通。 草案范围是逐案审核的,请在投入大量开发之前先与 Flap 团队分享你的想法。
- 编写合约。 所有涉及资金的规则都放在链上。集成 Flap 共享基础设施,而不是自建服务。
- 发出 FSI 事件,覆盖 UI 需要的每一个列表、排名或历史。A/B 使用绝对值,L/G 使用只追加的记录。
- 测试:编写集成测试,并在 BNB Chain 测试网或 Robinhood 测试网上测试(https://testnet.flap.sh)。
- 完成审计。 你的合约必须通过 Flap 合作安全公司之一的审计。
- 构建 UI:使用模板的独立 Mini App v2 模式 —— 生成脚手架,基于
useFlapSdk({ chainId })实现,声明walletChains/walletContracts,并通过app:check和app:e2e。 - 打包并提交:将
yarn app:package生成的 ZIP 提交到 Flap App Workbench,同时提供 slug、显示标题、功能简介、端点声明以及合约升级权限说明。 - 上线。 审核通过后,Flap 将 App 发布在
https://flap.sh/apps/{slug}。
待定问题
本草案有意保留了若干开放问题,我们会在首批 Mini App 审核过程中逐步确定:
- 范围。 目前还没有对 Mini App 可以是什么设定硬性边界;每个提交都逐案审核。
- 合约可升级性。 经过验证的 Vault 必须采用 beacon 代理模式,升级权限由 Flap Guardian 持有,以便修复审计发现的问题。Mini App 合约是否遵循同样的规则仍在讨论中。
- 非 Vault 合约的审计流程与费用。
- FSI 的
trusted范围。 目前trusted只标记在 VaultPortal 注册的 Vault;通过审计的 Mini App 合约是否也会被标记,仍待确定。 - FSI 尚待确认的细节:
data保持string还是改为bytes(这会改变 A/B/L 的签名,必须在签名冻结前确定)、是否开放 A/B 的历史版本(time-travel)查询,以及默认配额是否够用。 - 实时更新。 计划提供 WebSocket 推送尚未确认的值,让 UI 可以立即显示"确认中"状态;涉及资金的逻辑仍必须读取链上状态。
欢迎就以上任何问题提供反馈 —— 请联系 Flap 团队。