FLAPixel 金库是如何工作的?

FLAPixel 是一个Flap 税收金库的 ERC-721 NFT 金库工厂。Token 交易产生的 BNB 税收进入 Vault 后,会按照创建金库时填写的比例拆成两部分:

  • 分红池:给当前 NFT 持有人领取 BNB。
  • 地板价池:托底 NFT,用户可以把 NFT 卖回金库并销毁。

NFT 图像是链上生成的。每张 NFT 在合成时固定自己的图像 seed,只有所有者地址跑马灯会读取当前 owner,因此转账NFT后跑马灯地址会变化。

核心流程

  1. 用户买卖 Token,Flap 税收流程把 BNB 分到 Vault。
  2. Vault 的 receive() 收到 BNB。
  3. Vault 按 dividendSharePercent / 分红比例floorSharePercent / 地板价比例 拆分税收。
  4. 有 NFT 时,分红部分进入分红账本;每个地址按当前 NFT 余额作为权重。
  5. 地板价部分进入 floorPool / 地板价池,用于用户卖回 NFT。
  6. 用户用 Token 合成 NFT,Token 会转入 DEAD 地址销毁。
  7. 用户可以调用 claim() 领取自己当前地址下所有 NFT 权重对应的 BNB 分红。
  8. 用户可以调用 sellToVault(tokenIds) 按当前地板价批量卖给金库,卖回的 NFT 会被销毁。

创建参数说明

参数含义填写建议
NFTNameNFT 系列名称例如 FLAPixel NFT
NFTSymbolNFT 符号例如 FPIX
maxNFTSupply / NFT 最大总量NFT 最大总量必须大于 0,且满足兑换总量限制
initialFloorNFTs / 首次税收托底 NFT 数量首次税收托底 NFT 数量必须 >= 1,且 <= maxNFTSupply / NFT 最大总量
tokenCostPerNFT / 每张 NFT 消耗 Token 数量每张 NFT 需要消耗多少 Token用户支付后 Token 会销毁
dividendSharePercent / 分红比例进入 NFT 分红池的税收比例0-100,例如 50 代表 50%
floorSharePercent / 地板价比例进入 NFT 地板价池的税收比例0-100,必须和分红比例相加等于 100
artSchemaNFT 图像 schema 合约填 零地址使用默认 FLAPixel;填外部合约地址使用自定义图像

关键限制

比例公式:

dividendSharePercent + floorSharePercent = 100

进入 NFT 分红池的税收比例 + 进入 NFT 地板价池的税收比例 = 100%

兑换上限公式:

maxNFTSupply * tokenCostPerNFT <= 1,000,000,000

NFT 最大总量 * 每张 NFT 消耗的 Token 数量 <= Token 总量 1,000,000,000

示例只是在说明上限,不代表必须把 10 亿 Token 全部用满。只要乘积小于或等于 10 亿都允许。

NFT 总量每张消耗 Token总消耗是否允许
1001,000100,000✅ 允许
1,00010,00010,000,000✅ 允许
20,00050,0001,000,000,000✅ 允许
20,000100,0002,000,000,000❌ 不允许
100,000,000101,000,000,000✅ 允许
100,000,001101,000,000,010❌ 不允许

initialFloorNFTs / 首次税收托底 NFT 数量

地板价公式:

floorPrice = floorPool / max(activeNFTSupply, initialFloorNFTs - totalBurned)

NFT 地板价 = 地板价池里的 BNB / max(当前还存在的 NFT 数量, 首次税收托底 NFT 数量 - 已通过金库卖回销毁的 NFT 数量)

它的作用是防止早期 NFT 数量很少时,第一张 NFT 直接吃掉过高地板价。

例如:

  • maxNFTSupply / NFT 最大总量 = 1000
  • initialFloorNFTs / 首次税收托底 NFT 数量 = 100
  • floorPool / 地板价池 = 10 BNB
  • 当前只铸造了 1 张 NFT

此时不是:

floorPrice = 10 BNB / 1

NFT 地板价 = 地板价池里的 10 BNB / 当前 1 张 NFT

而是:

floorPrice = 10 BNB / 100 = 0.1 BNB

NFT 地板价 = 地板价池里的 10 BNB / 首次税收托底 100 张 NFT = 0.1 BNB

通过 sellToVault(tokenIds) 卖回并销毁的 NFT 会消耗对应托底份额。例如 initialFloorNFTs / 首次税收托底 NFT 数量 = 100,已经通过金库卖回销毁 10 张,则剩余托底份额是 90。

等当前还存在的 NFT 数量超过剩余托底份额后,会按当前还存在的 NFT 数量计算。

玩法模式

这套金库不是单纯"发一张 NFT",而是把 NFT 做成项目税收的一层可视化权益。创建者可以决定税收更偏向即时分红,还是更偏向 NFT 地板价托底。

默认最容易理解的是 50% / 50%:一半进入分红,一半进入地板价池。这样 NFT 持有人既能看到 BNB 收益,也能看到金库对 NFT 的托底在积累。

50% 分红,50% NFT 地板价

填写:

dividendSharePercent / 分红比例 = 50
floorSharePercent / 地板价比例 = 50
  • 一半税收进入 NFT 分红池,当前 NFT 持有人可以领取 BNB。
  • 一半税收进入 NFT 地板价池,逐步形成卖回金库的托底价格。
  • NFT 不是只看图片,而是同时承载分红、地板价、销毁退出和纯链上图像。

交易越活跃,Vault 收到的 BNB 越多,NFT 的分红和地板价池都会同步增长。用户持有 NFT 时可以领取收益;需要退出时,也可以按金库地板价卖回并销毁。

100% 分红,0% NFT 地板价

填写:

dividendSharePercent / 分红比例 = 100
floorSharePercent / 地板价比例 = 0
  • 所有进入 Vault 的 BNB 税收都分给 NFT 持有人。
  • 每张 NFT 是一份分红权重。
  • 一个人持有 10 张 NFT,就有 10 份分红。
  • NFT 更接近"BNB 收益凭证"。

如果还没有任何 NFT 被铸造,分红税不会留给第一张 NFT 直接领取,而是进入 buyback burn 队列,通过 Flap Portal 回购销毁 Token。

0% 分红,100% NFT 地板价

填写:

dividendSharePercent / 分红比例 = 0
floorSharePercent / 地板价比例 = 100

玩法:

  • 所有进入 Vault 的 BNB 税收都进入 NFT 地板价池。
  • NFT 持有人没有 BNB 分红可领。
  • NFT 的价值主要体现在可卖回金库的地板价。
  • 用户卖给金库后,NFT 会销毁,NFT 存量减少。

税收不断进入地板价池,卖回金库的 NFT 会被销毁,剩余 NFT 数量会减少。它不强调持续领取 BNB,而是强调"有限 NFT + 税收托底 + 回收销毁"的结构。

早期没有 NFT 时,税收也会进入地板价池,但地板价会按 initialFloorNFTs / 首次税收托底 NFT 数量 摊开,避免第一张 NFT 直接按全部池子卖出。

合成 NFT 的数量计算

mintNFTWithTokenAmount(tokenAmount / 传入 Token 数量) 要求:

tokenAmount % tokenCostPerNFT == 0

传入的 Token 数量 % 每张 NFT 消耗的 Token 数量 = 0

也就是说,传入的 Token 数量必须刚好能整除单张 NFT 价格。

合成数量公式:

mintQuantity = tokenAmount / tokenCostPerNFT

可合成 NFT 数量 = 传入的 Token 数量 / 每张 NFT 消耗的 Token 数量

单次合成数量必须满足:

mintQuantity <= MAX_MINT_BATCH

单次最多合成 100 张 NFT

示例:

  • 每张 NFT 需要 1,000,000 Token。
  • 输入 5,000,000 Token,可以合成 5 张 NFT。
  • 输入 4,500,000 Token,不能合成 4.5 张,会直接失败。

分红如何计算

分红按地址 NFT 余额权重结算,不按用户持有的 NFT 列表逐张循环。

公式:

accDividendPerNFT += dividendAmount * PRECISION / activeNFTSupply

pending(user) = pendingRewardOf[user] + NFTBalance(user) * (accDividendPerNFT - rewardDebtOf[user]) / PRECISION

如果某个地址持有 10 张 NFT,它就有 10 份分红权重;持有 100 张 NFT,就有 100 份分红权重。

用户调用 claim() 时,会领取当前地址拥有的所有 NFT 的待领取 BNB。

NFT 转账前会先把旧 owner 和新 owner 的分红账本结算到各自地址,不会强制转出 BNB。转出成功后,旧收益留给旧 owner,新收益归新 owner。

卖回金库如何计算

用户调用:

sellToVault(tokenIds)

tokenIds 是 NFT 编号数组。卖 1 张时传 [1],卖多张时传 [1,2,3]。单次最多卖回 100 张。

合约会:

  1. 检查数组不为空,且数量不超过 MAX_SELL_BATCH / 单次卖回上限
  2. 检查数组里的每个 NFT 都属于当前调用者。
  3. 先结算当前调用者地址下所有 NFT 的待领取分红。
  4. 在本次交易开始时读取一次 floorPrice() / NFT 地板价,作为本次卖回的单张快照价格。
  5. 计算总卖回金额:本次单张地板价 * tokenIds.length
  6. floorPool / 地板价池 一次性扣除总金额,并支付 BNB 给用户。
  7. 销毁数组里的所有 NFT。
  8. 本次卖回不会改变其他已经铸造 NFT 的主体图像。

同一笔批量卖回里,每张 NFT 的单张价格一致。例如当前地板价是 1 BNB

sellToVault([1])       = 1 BNB
sellToVault([1,2,3])   = 3 BNB
sellToVault([1...100]) = 100 BNB

卖回本身只是按当前地板价赎回并销毁 NFT。卖回会减少当前还存在的 NFT 数量,并消耗对应托底份额;只有新的地板价税收进入 floorPool / 地板价池 后,地板价池余额才会继续增加。

只有调用 sellToVault(tokenIds) 才会从金库拿到地板价 BNB。用户直接把 NFT 转到 DEAD 或其他不可控地址,只是普通 ERC-721 转账,不会触发地板价赎回,也不会更新金库的 totalBurned / 已销毁NFT 统计。

没有 NFT 时税收怎么处理

没有 NFT 时,分红池没有接收对象,所以分红税不会留给第一张 NFT 直接领取。

处理方式:

  • 分红税部分:进入 pendingBuybackBNB,达到阈值后通过 Flap Portal 回购并销毁 Token。
  • 地板价税部分:进入 floorPool / 地板价池,按 initialFloorNFTs / 首次税收托底 NFT 数量 摊开计算地板价。

当前 buyback burn 规则:

  • 小于 0.01 BNB 时先累计。
  • 大于等于 0.01 BNB 时尝试执行。
  • 单次最多执行 1 BNB
  • 回购销毁统计按 DEAD 地址实际增加的 Token 数量记录。
  • 如果 Flap Portal swap 失败,不 revert 收款,金额保留到下一次继续尝试。

图像 Schema(自定义 NFT 图像)

开发者想自定义 NFT 图像时,正式规范就是实现 IPixelArtSchema

标准接口:

interface IPixelArtSchema {
    function schemaVersion() external view returns (uint256);
    function schemaName() external view returns (string memory);
    function canvas(uint256 seed, uint256 tokenId) external view returns (string memory svgFragment);
}

interface IPixelArtSchemaTraitsV1 {
    function attributes(uint256 seed, uint256 tokenId) external view returns (string memory jsonAttributeObjects);
}

核心规则:

  • schemaVersion() 必须返回大于 0 的整数。
  • schemaName() 不能为空。
  • canvas(seed, tokenId) 只返回 SVG fragment,不要返回完整 <svg>
  • 画布按 512 x 512 来设计。
  • 图像不强制使用像素网格;可以使用 rectcirclepathg 等 SVG 元素自由绘制。
  • attributes(seed, tokenId) 是可选接口,返回 JSON attribute 对象片段,不带外层数组。
  • 外部 schema 的图像和属性都会通过 staticcall 读取,不能修改 Vault 状态。
  • canvas() fragment 最大 9,500 bytes,render gas limit 为 1,800,000
  • attributes() 返回值最大 4,000 bytes,attributes gas limit 为 400,000