Skip to main content
A tokenized vault is a smart contract that accepts deposits of an underlying fungible token (FT) and, in exchange, issues shares that represent proportional ownership of the assets held by the vault. The underlying asset can be any NEP-141 compliant token (e.g. a stablecoin or a yield-bearing token). When a user deposits, the vault mints new shares based on the current exchange rate between the vault’s total assets and its total shares in circulation. When a user redeems, the vault burns those shares and returns the equivalent amount of the underlying asset. The shares themselves are also NEP-141 compliant fungible tokens, so they can be transferred between accounts or traded on DEXs, and integrated into broader DeFi use cases such as collateral in lending protocols, liquidity provision, or composable yield strategies. This interface is defined by NEP-621, and is heavily inspired by the ERC-4626 standard widely used on Ethereum. Standardizing the vault interface improves interoperability, reduces integration costs, and encourages consistent, secure vault implementations across the NEAR ecosystem.
NEP-621 defines the interface and the expected behavior of a vault contract, but it does not dictate how the internal logic (yield strategy, fees, accounting) should be implemented.Unlike ERC-4626, deposits and mints on NEAR are handled through the ft_on_transfer callback (as defined by NEP-141), rather than by direct method calls, to fit NEAR’s asynchronous execution model.

NEP-621 (Vault Interface)

A vault contract MUST implement the VaultCore trait, which extends:
  • FungibleTokenCore — to provide NEP-141 functionality for the shares.
  • FungibleTokenReceiver — to receive the underlying NEP-141 asset.
In other words, a vault is itself a fungible token (the shares) that can also receive another fungible token (the underlying asset).

Asset Information

asset_contract_id (read-only)

Returns the account ID of the underlying asset’s NEP-141 contract. Should be stored as an immutable configuration value.

total_asset_amount (read-only)

Returns the vault’s total managed value, denominated in the underlying asset, represented by all shares in existence.
📖 This is the vault’s total managed value, not just the balance held in the contract. If assets are staked, lent, or deployed elsewhere, this returns the estimated total equivalent value, net of any deposit/withdrawal fees.

Conversion Helpers

These are view-only estimations. They do not update state and ignore user-specific constraints such as limits or fees. They MUST never panic, returning 0 if conversion is not possible.

convert_to_shares (read-only)

Converts an amount of underlying assets to the equivalent number of shares.

convert_to_asset_amount (read-only)

Converts an amount of shares to the equivalent amount of underlying assets.
Use convert_* for generic price quotes, and the preview_* methods below when you need an estimate that also accounts for per-user limits and fees.

Deposit & Redemption Limits

All limit and preview methods are read-only and MUST never panic, returning 0 when the operation is not currently allowed. Limit methods should return U128::MAX to signal “unlimited”.

max_deposit_amount (read-only)

Maximum amount of underlying assets that receiver_id can deposit.

preview_deposit_shares (read-only)

Simulates depositing exactly asset_amount and returns the number of shares that would be minted, accounting for per-user deposit limits and fees.

max_mint_shares (read-only)

Maximum number of shares that receiver_id can mint.

preview_asset_amount_required_to_mint_shares (read-only)

Simulates minting exactly shares and returns the amount of underlying assets required.

max_redeem_shares (read-only)

Maximum number of shares that owner_id can redeem (considering balance, withdrawal restrictions, and lock-ups).

preview_redeem_amount (read-only)

Simulates redeeming shares and returns the amount of assets that would be received, factoring in the owner’s balance, withdrawal limits, and fees.

max_withdraw_amount (read-only)

Maximum amount of assets that owner_id can withdraw.

preview_shares_deducted_for_withdraw (read-only)

Simulates withdrawing exactly asset_amount and returns the number of shares that would be burned.

Deposit & Mint

Following the NEP-141 standard, assets are sent to the vault via ft_transfer_call on the underlying asset’s contract, which invokes ft_on_transfer on the vault. Both the ERC-4626 deposit and mint operations are handled through this single entrypoint.
Upon a successful deposit, the vault MUST emit a VaultDeposit event and should also emit an FtMint event for the issued shares. The msg parameter carries the deposit options. While implementers are free to define their own schema, the suggested convention is a JSON-encoded message:
As required by NEP-141, ft_on_transfer must return the number of tokens to refund to the sender: 0 if all tokens were accepted, or a non-zero value otherwise.

Redemption & Withdrawal

redeem

Burns shares from the caller and returns the equivalent amount of underlying assets. If receiver_id is omitted, assets are sent to the caller. Set min_amount_out to 0 to ignore slippage, or to a reasonable value to protect against it.

withdraw

Withdraws exactly asset_amount of underlying tokens, burning the required number of shares. If receiver_id is omitted, assets are sent to the caller. Provide max_shares_deducted to protect against slippage.
Upon a successful withdrawal, the vault MUST emit a VaultWithdraw event.

Events

Vaults emit standardized events so wallets, indexers, and other DeFi applications can track deposits and withdrawals.
Because transactions on NEAR are non-atomic, a vault should emit VaultWithdraw in one of two ways:
  1. Emit VaultWithdraw when the fee is deducted, and emit a compensating VaultDeposit if the withdrawal later fails; or
  2. Emit VaultWithdraw only if the withdrawal succeeds.
In the reference implementation, an FtBurn event is emitted when withdraw is called. On success, a VaultWithdraw event is emitted in the resolve_withdraw callback; on failure, an FtMint event restores the user’s balance.

Security Considerations

When implementing a vault, keep the following risks in mind:
  • Exchange rate manipulation (inflation attacks): if the vault has a permissionless donation mechanism, an attacker can donate assets to inflate the share price and steal value from later depositors. Mitigate by seeding a non-trivial initial deposit and/or using a virtual decimal offset on issued shares (demonstrated in the reference implementation).
  • Cross-contract calls: redeem and withdraw perform asynchronous FT transfers, opening the door to reentrancy. Use proper state management, callback security, and rollback mechanisms for failed operations.
  • Rounding direction: always round in favor of the vault — round down when issuing shares or transferring assets out, round up when calculating shares/assets required for a given amount. This prevents value extraction through repeated micro-transactions.
  • Oracle and external price dependencies: stale oracle data creates temporal windows for exploitation. Include staleness checks, prevent operations during oracle updates, and consider fallback pricing for oracle failures.

Reference Implementation

A reference implementation is available at Meteor-Wallet/tokenized-vault-nep-implementation.