Vault & VaultFactory Specification

Table of Contents

Quick start

  1. Follow the example β€” Work through the FlapVaultExample repositoryarrow-up-right to see a complete, working vault and vault factory implementation. It is the fastest way to understand the patterns you need to follow.

  2. Verify your implementation β€” Once you have written your vault, use the FlapVaultSpecCheckerarrow-up-right AI toolkit to automatically check that your implementation correctly follows this specification before deploying.

Overview

Flap's vault system allows anyone to build smart contracts for managing and distributing BNB revenue generated from tax tokens. Vaults can be customized to fit various use cases β€” splitting revenue among multiple recipients, routing funds based on social proof, funding a game treasury, or anything else you can imagine.

circle-info

Permissionless vault creation β€” You can now create and deploy your own vaults and vault factories without any permission or registration. Vault factories no longer need to be registered in the VaultPortal before they can be used to launch tokens. Users can use any vault they want when launching tokens.

circle-exclamation

There are two generations of the vault specification:

Version
Vault base contract
Factory base contract
Key addition

V1

VaultBase

IVaultFactory

Core spec β€” description(), Guardian mandate

V2

VaultBaseV2 (extends VaultBase)

VaultFactoryBaseV2 (implements IVaultFactory)

On-chain UI schema for automatic UI generation

V2 is fully backwards-compatible with V1. Existing vaults that extend VaultBase are unaffected. New vault implementations should extend VaultBaseV2 and new factory implementations should extend VaultFactoryBaseV2 to gain UI schema support.

The Vault Specification

VaultBase (V1)

To be compatible with the VaultPortal system, your vault smart contract must inherit the VaultBase contract:

Implementation requirements:

  1. Implement description() β€” Return a dynamic string that describes the vault's current state. The description should change based on the vault's state (e.g. balance, streaming status, etc.).

  2. Implement receive() β€” Accept BNB from the tax token and process the revenue according to your vault's logic.

  3. Guardian mandate β€” If you have any permissioned functions that should be triggered by an external address, and it is not suitable to make them public (e.g. buyback which may be sandwich attacked), you must also give the Guardian address the permissions alongside other allowed addresses as a backup. See the Flap Guardian section for details.

VaultBaseV2

VaultBaseV2 inherits from VaultBase and adds a single new abstract method: vaultUISchema(). This allows the UI to automatically discover and render the vault's user-facing methods β€” without needing custom code for each vault type.

What changes from V1:

  • All V1 obligations still apply (description(), receive(), Guardian mandate).

  • You must additionally override vaultUISchema() to return a VaultUISchema describing every user-facing method your vault exposes. See the UI Schema Reference section for the full struct definitions.

Why this matters: Without a self-describing mechanism, every new vault type would require a custom UI to be built and deployed before users could interact with it. vaultUISchema() solves this problem by enabling automatic UI generation for any future vault type.

circle-info

Existing vault types (FlapXVault, SplitVault, SnowBallVault, BlackHoleVault) already have purpose-built UIs. VaultBaseV2 is designed for future vault implementations β€” the UI can automatically generate a full interaction page for any vault that implements this interface.

Example β€” Hypothetical DonationVault:

Example β€” StakingVault with approve actions:

The Flap Guardian

The Flap Guardian is a privileged address that can always call permissioned functions in vault contracts that implement the Vault Specification. The Guardian serves as a backup mechanism to ensure that critical functions can be executed even if the primary authorized addresses are unable to do so.

At this moment, the Guardian is an empty but upgradeable contract managed by Flap:

Ideally, we don't need to implement any functionality in the Guardian contract. It just serves as a trusted address that can step in when necessary to protect users' funds.

circle-exclamation

Adapter for legacy vaults

If you have built a vault to receive tax token revenue before the Vault Specification was introduced, you can still make your vault compatible with the VaultPortal system by creating an adapter contract that inherits from VaultBase and wraps around your existing vault. The adapter will implement the description() method and forward calls to your legacy vault as needed. This way, you can leverage VaultPortal features without modifying your original vault contract.

Please reach out to our team for assistance in creating an adapter for your legacy vault.

VaultFactory Specification

A vault factory deploys vault instances for new tax tokens. When a user launches a token through the VaultPortal, the portal calls your factory's newVault() method to create the vault.

circle-info

No registration required β€” In V2, any contract that implements VaultFactoryBaseV2 can be passed to the VaultPortal to launch tokens. You do not need to register your factory on-chain.

IVaultFactory (V1)

The base interface that all vault factories must implement:

Key points:

  • Currently, only BNB (quoteToken == address(0)) is supported as the quote token, but future support for other quote tokens may be added.

  • The newVault method is called by the VaultPortal when a new tax token is being created. The tax token does not exist yet when this method is called β€” the VaultPortal predicts the token address and passes it. The actual token is created after the vault.

VaultFactoryBaseV2

VaultFactoryBaseV2 implements IVaultFactory and adds a new abstract method: vaultDataSchema(). This allows the UI to discover what vaultData encoding the factory expects, and render the launch form accordingly.

What changes from V1:

  • All V1 obligations still apply (newVault(), isQuoteTokenSupported()).

  • You must additionally override vaultDataSchema() to return a VaultDataSchema describing the fields your factory expects in the vaultData parameter.

  • The factory now provides _getVaultPortal() and _getGuardian() helpers.

Example β€” Single tuple schema:

Example β€” Array of tuples schema:

Example β€” Factory that ignores vaultData:

Vault factories can charge a commission fee from the tax revenue. The fee structure must be clearly described in the vault's description() method. The recommended fee calculation is based on the tax rate (taxRateBps) and the received tax revenue (msg.value):

  • If taxRate ≀ 1% (100 bps), the fee is 6% of msg.value.

  • If taxRate > 1%, the fee is (msg.value * 6) / taxRateBps.

UI Schema Reference (IVaultSchemasV1)

The shared struct definitions in IVaultSchemasV1.sol power the automatic UI generation for both vault factories (token launch forms) and vaults (vault interaction pages).

FieldDescriptor

The unified leaf type shared by both the factory schema and the vault UI schema:

Supported fieldType values:

fieldType

UI widget

Notes

"string"

Text input

"address"

Address input

With checksum validation

"uint16"

Number input

0–65535

"uint256"

Big-number input

"uint128"

Number input

"bool"

Checkbox

"bytes"

Hex input

"bytes32"

Hex input

32 bytes

"time"

Date/time picker

Alias for uint256; value is a Unix timestamp in seconds. Displayed as human-readable time or countdown in outputs

decimals behaviour:

  • Factory encoding (input): If decimals > 0, the UI multiplies the user's input by $10^{\text{decimals}}$ before ABI-encoding. If 0, the raw value is used.

  • Vault display (output): If decimals > 0, the UI divides the raw on-chain value by $10^{\text{decimals}}$ for display. If 0, the raw value is displayed.

  • For non-numeric fields (string, address, bytes, bool), decimals should always be 0.

VaultDataSchema

Returned by VaultFactoryBaseV2.vaultDataSchema(). Describes the shape of the vaultData bytes the factory expects:

How the UI uses VaultDataSchema:

  1. Call factory.vaultDataSchema() to get the schema.

  2. For each field in fields, render the appropriate input widget based on fieldType.

  3. If isArray == true, render an "Add Item" button for dynamic array entries.

  4. Encode the user input: if isArray use abi.encode(tuple[]); otherwise use abi.encode(tuple).

  5. The encoded bytes are passed as vaultData in NewTaxTokenWithVaultParams.

If fields is empty and isArray is false, the factory ignores vaultData entirely.

VaultUISchema & supporting types

Returned by VaultBaseV2.vaultUISchema(). Describes the vault's entire UI surface:

How the UI uses VaultUISchema:

  1. Display vaultType as a badge/header and description as subtitle.

  2. Always call vault.description() and display the result as a dynamic status banner (polled periodically).

  3. For each method:

    • View methods (isWriteMethod == false): If no inputs, call immediately and display results. If inputs exist, render input fields with a "Query" button.

    • Write methods (isWriteMethod == true): Render a form with inputs. If approvals is non-empty, execute each ApproveAction before sending the write transaction.

  4. Methods are displayed in the order returned.

ApproveAction workflow:

  1. Resolve the token address from tokenType: "taxToken" β†’ call vault.taxToken(), "lpToken" β†’ call vault.lpToken(), unknown β†’ skip (forward-compatible).

  2. Read the amount from the user's input field named by amountFieldName (already scaled by decimals).

  3. Check current allowance: token.allowance(user, vault). If sufficient, skip.

  4. Send token.approve(vault, amount) and wait for confirmation.

Get your vault verified

Please reach out to our team if you want to get your vault or vault factory verified by us or our auditing partners.

Before reaching out, make sure:

  • You have correctly implemented the Vault Specification (the description() method, the Guardian access to permissioned functions, and vaultUISchema() / vaultDataSchema() if using V2).

  • Your factory contract is not upgradeable. If you want to upgrade your factory in the future, you must deploy a new factory contract and get it verified again.

  • The commission fee structure is clearly described in the vault's description() method.

  • You have passed some basic AI auditing tools β€” see the FAQ below.

circle-exclamation

FAQ

How to use AI to audit your smart contracts?

You can use any AI for auditing your smart contracts. For example, you can use Google's AI Studio which is free to usearrow-up-right. The prompt is as follows:

This can help you identify common vulnerabilities and issues in your smart contracts. If your comments are clear enough, it will even help identify logical issues. However, AI tools are not perfect and may miss vulnerabilities or provide incorrect suggestions. Always review your smart contracts thoroughly before deploying them on mainnet.

Last updated