跳到主要内容

Vaults

一个 tax token 会从每笔买入和卖出中收取一部分。这一部分会流向一个 fee receiver。在普通的 tax-token 上线中,fee receiver 是一个钱包。在 vault 上线中,fee receiver 是一个在上线时 部署的合约,因此收入会落到一个带规则的地方:把它拆分给多个钱包、把它流式支付给单个接收方、 为某个身份代管,或者 vault 作者所编写的其他任何逻辑。

本页面向想要用 vault 上线或读取 vault 的集成方。如果你想编写自己的 vault,参见 custom-vault.md。

目录​

收入如何到达 vault​

   trader ──buy/sell──▶ tax token
│ collects tax on the transfer
▼
settles the tax ← permissionless; anyone can poke it
│ splits by the allocation you set at launch
┌──────────────┼───────────────┬───────────────┐
▼ ▼ ▼ ▼
fee receiver holders burn liquidity
(the vault) (dividends) (0xdead) (adds LP)

代币并不在每笔交易上推送收入。它累计 tax 并分批结算。结算时,fee receiver 份额会被发送到 vault。你的 vault 记录这笔收入,稍后通过它自己的 distribute() / claim() 调用支付出去。 正是这种分批机制,使得 vault 总是呈现"现在累计、稍后支付"的形态。

CheesePad 对 creator tax 收取的 10% 平台费是一笔独立的费用,不属于你的 allocation;关于它 如何与 tax 并存,参见代币费用模型。

四个 allocation 份额来自你在上线时于 taxConfig 中设置的 allocation,它们之和必须为 10000 BPS:

SliceWhere it goes
feeReceiverthe vault
holderstoken holders, as dividends (needs a non-zero minHolderShare)
burnthe dead address
liquidityadded back as LP

要让一个 vault 有机会获得支付,allocation.feeReceiver 必须大于零,且至少一侧 tax 必须非零。 launchpad 会检查这一点,否则会 revert 掉这次上线。

用 vault 上线​

使用 createMicroSaleTaxTokenWithVaultV2。在通常的 CreateParamsV2 之外,你还要传入一个 vault 工厂地址、一个用于配置具体 vault 的 ABI 编码 vaultData 数据块,以及指定要上线的 DEX 的 dexKey。

function createMicroSaleTaxTokenWithVaultV2(
CreateParamsV2 calldata params, // same struct as the tax-token launch, incl. taxConfig
address vaultFactory, // a factory the platform trusts (see Factory addresses)
bytes calldata vaultData, // ABI-encoded config for this vault; shape depends on factory
address dexKey // the DEX to list on
) external payable returns (address fundManager);

launchpad 会按顺序做以下这些事,全部为 fail-closed(任何一步失败都会 revert 整个上线):

  1. 确认工厂指向这个 launchpad。
  2. 确认工厂接受该销售的 quote token。
  3. 确认工厂理解当前的 launch-data 版本。
  4. 用你的上线参数调用工厂的 review hook;它必须批准。
  5. 让工厂部署 vault。
  6. 确认 vault 的 revenue 模式与 quote token 匹配(见下文)。
  7. 把 vault 绑定为代币的 fee receiver。

你从不需要自己接线任何东西。如果工厂批准,你就会得到一个活跃的代币,其 tax 会流入一个全新的 vault。

同样的握手流程支撑着 bonding curve tax token:createBondingCurveTaxTokenWithVaultV2 接受一个 vaultFactory、vaultData 和 dexKey,并运行完全相同的检查。约束在于 vault 的 revenue 模式 必须与曲线的 quote currency 匹配——BNB 曲线用 native 模式,以 ERC20 计价的曲线用 ERC20 模式 (见原生 vs ERC20 收入)。下文的一切对两种上线方式都适用。

编码 vaultData​

vaultData 对 launchpad 而言是不透明的;每个工厂定义自己的形态。工厂通过 vaultDataSchema() 公布这个形态,因此 UI 可以通用地构建表单。若要手动编码,请遵循 schema 的字段顺序与类型。两个 现成 vault 的具体示例见下文。

取回 vault 地址​

上线之后,从销售中读取它:

const sale = await launchpad.getMicroSaleByFundManager(fundManager);
console.log("vault:", sale.vault); // the deployed vault
console.log("via factory:", sale.vaultFactory); // the factory that produced it

销售还会记录是哪个工厂生产了这个 vault,因此一个链下白名单可以为来自可信工厂的 vault 打上 标记。

原生 vs ERC20 收入​

一个 vault 只处理一种资产的收入,在上线时决定:

  • Native 模式:当销售的 quote token 是 WBNB 时。收入以 BNB 到达。
  • ERC20 模式:其他情况。收入以 quote token 到达,且孤立的原生币会被拒收。

launchpad 会断言 vault 的模式与 quote token 匹配,因此一个不匹配的工厂无法使交易瘫痪。对于以 ERC20 计价的上线,贡献者也会以那个 ERC20 quote token 募资—— 参见 microsale.md。quote token 来自模板的绑定,因此针对一个绑定了 ERC20 的模板发起 wallet-receiver 或 no-tax 上线时,也会以那个 quote token 募资;vault 带来的差别 仅在于 fee-receiver 份额落到哪里。

CheesePad 提供的 vault​

两个现成的 vault,各自有自己的工厂。你把 createMicroSaleTaxTokenWithVaultV2 指向该工厂,并 传入匹配的 vaultData。

SplitterVault:按份额固定分给收款人​

按基点把收入拆分给一个固定的钱包列表。最多 50 个收款人,份额之和必须为 10000,不得重复,不得 有零地址。

vaultData 为 abi.encode(address[] payees, uint16[] bps):

const payees = ["0xAaa...", "0xBbb..."];
const bps = [7000, 3000]; // 70% / 30%; must sum to 10000

const vaultData = ethers.AbiCoder.defaultAbiCoder().encode(
["address[]", "uint16[]"],
[payees, bps]
);

await launchpadRW.createMicroSaleTaxTokenWithVaultV2(params, SPLITTER_FACTORY, vaultData, dexKey, {
value: creationFee,
});

上线后使用该 vault:

  • distribute() 是无需许可的。它把已累计余额中每个收款人的份额记入其名下并推送给他们。如果 某个收款人无法接收(例如一个会 revert 的合约),他的份额会被保留为 owed,供其稍后自行 提取,其他人照常获得支付。
  • claim() 让某个收款人提取自己的 owed 余额。
  • splitConfig() 返回收款人及其 bps;owed(address) 返回某个收款人的待领余额;credited() 返回已记入的累计总额。

SplitterVault 接受任何 quote token。

GiftVault:流式支付给某个 X 账号背后的钱包​

为某个 X(Twitter)账号背后的人代管一个代币的收入。在一个钱包被绑定到该账号之前,没有人会 获得支付,而绑定由一个平台 relayer 签名授权,因此只有该账号的真正拥有者才能领取。

vaultData 为 abi.encode(string xHandle, uint256 timeoutPeriod):

const vaultData = ethers.AbiCoder.defaultAbiCoder().encode(
["string", "uint256"],
["cheesepad", 30n * 24n * 60n * 60n] // handle, and 30-day bind window
);

await launchpadRW.createMicroSaleTaxTokenWithVaultV2(params, GIFT_FACTORY, vaultData, dexKey, {
value: creationFee,
});

timeoutPeriod 介于 1 到 365 天之间。GiftVault 工厂还要求 allocation.holders 大于零,因为 存在下面的回退机制。

它的运作过程:

  • Bind(绑定)。 钱包拥有者取得一个由 relayer 签名的证明(一个 EIP-712 签名,覆盖账号、 代币、受益人钱包、一个 nonce 和一个过期时间),并调用 bindBeneficiary(beneficiary, nonce, validUntil, signature)。从此之后,收入会流式支付给那个 钱包。绑定只在超时之前开放,且只有在证明校验通过后才可进行。
  • Withdraw(提取)。 绑定之后,distribute() 会把已累计的收入推送给受益人,claim() 则 让他们自行提取。
  • Lock(锁定)。 受益人可以调用 lock(),将 relayer 和平台从任何未来的重新绑定中排除。
  • Rescue(救援)。 在超时之前且在锁定之前,平台后备机制可以纠正一个被错误绑定的钱包。它 只会重定向未来的收入。
  • Dividend fallback(分红回退)。 如果在超时之前无人绑定,任何人都可以调用 distributeDividend(),它会把未领取的收入作为分红路由给代币的持有者。这就是工厂坚持要有一个 holder 份额的原因。一旦它触发,vault 就永久进入分红模式,绑定随之关闭。

通用地读取一个 vault​

每个 vault 都通过 vaultUISchema() 描述自己的读取字段与操作。一个 UI 可以渲染一个它从未见过 的 vault:它读取 schema,调用每个读取方法,并用声明的类型解码结果。通用形态:

const vault = new ethers.Contract(sale.vault, VaultABI, provider);

const isNative = await vault.isNativeRevenue(); // true = BNB, false = ERC20
const quote = await vault.quoteToken(); // revenue asset
const desc = await vault.description(); // one-liner for listings
const schema = await vault.vaultUISchema(); // methods to render (reads + writes)

vaultUISchema() 返回一个 vaultType、一个 description 和一个方法列表。每个方法要么是一个 读取字段(UI 调用它并展示带类型的结果),要么是一个写入操作(UI 渲染一个按钮和一个输入表单, 外加它事先需要的任何 ERC20 approval)。schema 类型在 custom-vault.md 和 events-and-data.md 中有说明。

对于通用读取方,重要之处在于:一个 vault 只会暴露它的 schema 所声明的内容。没有自动的存储 自省,所以请把 schema 当作一切值得展示的内容的清单来信任。

工厂地址​

VaultNetworkFactory
SplitterVaultBNB (56)0x9d04B0Bfe281411d4A01a69BD89f7Ba6b7F5A59b
SplitterVaultBNB testnet (97)0xbB5165684832a8Cda1121081EBb168ca6636abc6
SplitterVaultRobinhood (4663)0x58DBab8C3529492c12Ee913313Cf600f522a39D9
GiftVaultBNB (56)0xFa36D3e2240BEDB22bad35D8FD4165AAf0598896
GiftVaultBNB testnet (97)0x8227F4330E3aa43c94b9fDc409D71A6C6C2033B7
GiftVaultRobinhood (4663)0x7FD34AeA57250aC66C59448339316aF745f6B9ef

工厂是无状态且不可变的。新版本是一个新地址,所以在你针对它自动化上线之前,请先向 CheesePad 团队确认当前地址。