Docs menu

Build a custom vault

What you build (and what you don't)

How it wires together

// The interfaces you implement
interface IVaultFactory {
    // Called ONCE by the VaultPortal at create time. taxToken is the PREDICTED
    // token address (not deployed yet) — do NOT call into it here.
    function newVault(
        address taxToken,
        address quoteToken,        // pricing/dividend token; address(0) = native BNB
        address creator,
        bytes calldata vaultData   // your create params, abi-encoded per vaultDataSchema()
    ) external returns (address vault);

    function isQuoteTokenSupported(address quoteToken) external view returns (bool);
    function vaultDataSchema() external pure returns (VaultDataSchema memory); // create-form UI
}

interface IVault {                  // your vault, extends VaultBase
    receive() external payable;     // receives the token's vault-bucket tax (BNB)
    function vaultUISchema() external pure returns (VaultUISchema memory); // interaction-page UI

    // Optional per-holder accounting (dividends): return true and the token will
    // call setShare(holder, eligibleShare) after every transfer. See "Per-holder rewards".
    function wantsShareUpdates() external view returns (bool);
    function setShare(address holder, uint256 share) external; // onlyTaxToken
}

Step 1 — the vault

Step 2 — the factory

contract MyVaultFactory is WhaleVaultFactoryBaseV2 {
    address public immutable implementation;   // your vault logic (deployed once)

    constructor(address impl, address vaultPortal)
        WhaleVaultFactoryBaseV2(vaultPortal) { implementation = impl; }

    function newVault(address taxToken, address quoteToken, address creator, bytes calldata vaultData)
        external override returns (address vault)
    {
        if (msg.sender != _getVaultPortal()) revert OnlyVaultPortal();  // only the platform may call
        if (taxToken == address(0) || creator == address(0)) revert ZeroAddress();
        if (quoteToken != address(0)) revert UnsupportedQuoteToken();    // this example wants BNB
        // decode YOUR create params (must match vaultDataSchema below)
        (address dividendToken, uint256 minimumShareBalance) =
            abi.decode(vaultData, (address, uint256));

        vault = Clones.clone(implementation);                           // one independent vault per token
        MyVault(payable(vault)).initialize(taxToken, quoteToken, dividendToken, minimumShareBalance, creator);
    }

    function isQuoteTokenSupported(address quoteToken) external pure override returns (bool) {
        return quoteToken == address(0);  // BNB only
    }
}

Step 3 — the two schemas (your free UI)

// create-form schema: 2 inputs -> abi.encode(address, uint256) -> vaultData
function vaultDataSchema() public pure override returns (VaultDataSchema memory s) {
    s.description = "Dividend any-token vault: snowball round every ~3 min.";
    s.fields = new FieldDescriptor[](2);
    s.fields[0] = FieldDescriptor("dividendToken", "address", "Reward token address", 0);
    s.fields[1] = FieldDescriptor("minimumShareBalance", "uint256", "Min hold to qualify", 18);
    s.isArray = false;
}

// interaction-page schema: the buttons holders see on the token page
function vaultUISchema() public pure returns (VaultUISchema memory s) {
    s.vaultType = "MyDividendVault";
    s.description = "Hold to earn; claim anytime.";
    s.methods = new VaultMethodSchema[](2);
    // claimDividend()  -> write, no inputs
    // pendingOf(addr)  -> read, returns uint256
}

Step 4 — deploy

VaultPortal · LaunchFactoryV2 · chain 56
0x1230B67525247DA20e56E9f8CAaA263ae670401a

Step 5 — use it on the create page

Step 6 — get listed (optional)

Security checklist: restrict newVault to the VaultPortal (OnlyVaultPortal); never call taxToken during init; keep funds under the guardian model (creator config-only); use ReentrancyGuard + SafeERC20; for price-sensitive logic avoid spot price (use Portal + TWAP). A working, audited reference is the Dividend Any-Token Vault (DividendWorldCupVault + its factory).