Near Learn

NEP-148: Fungible token metadata

NEP-148 fungible token metadata on NEAR: the ft_metadata view, the "ft-1.0.0" spec string, name, symbol, decimals and an inline data-URL icon.

Intermediate6 min read3-question check

NEP-141 moves raw integers around; it says nothing about what the token is called or where the decimal point goes. NEP-148 adds a single view method, ft_metadata(), that returns the token’s display information. Wallets and explorers call it once and cache the result.

The metadata object#

FieldTypeMeaning
specstringMust be "ft-1.0.0" for this version of the standard.
namestringHuman-readable name, e.g. "Wrapped NEAR fungible token".
symbolstringTicker, e.g. "wNEAR".
iconstring or nullAn image, ideally a small data URL (SVG preferred) so it renders without fetching anything.
referencestring or nullLink to a JSON file with extra off-chain info (IPFS/Arweave/HTTPS).
reference_hashstring or nullBase64-encoded sha256 of the reference JSON; required if reference is set.
decimalsnumber (u8)How many decimal places the integer amounts use. wNEAR uses 24 (like NEAR); USDC and USDT use 6.
What `near view <token> ft_metadata` returns (illustrative)
JSON
{
  "spec": "ft-1.0.0",
  "name": "Example Token",
  "symbol": "EXT",
  "icon": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='16' fill='%2300EC97'/%3E%3C/svg%3E",
  "reference": null,
  "reference_hash": null,
  "decimals": 18
}
Declaring metadata
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract ExampleToken is ERC20 {
    constructor() ERC20("Example Token", "EXT") {
        _mint(msg.sender, 1_000_000 * 10 ** decimals());
    }
    // decimals() defaults to 18; no icon in the standard
}
Rust
use near_contract_standards::fungible_token::metadata::{
    FungibleTokenMetadata, FungibleTokenMetadataProvider, FT_METADATA_SPEC,
};
use near_contract_standards::fungible_token::FungibleToken;
use near_sdk::{near, PanicOnDefault};

#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct Contract {
    token: FungibleToken,
    metadata: FungibleTokenMetadata,
}

#[near]
impl Contract {
    #[init]
    pub fn new() -> Self {
        let metadata = FungibleTokenMetadata {
            spec: FT_METADATA_SPEC.to_string(), // "ft-1.0.0"
            name: "Example Token".to_string(),
            symbol: "EXT".to_string(),
            icon: Some("data:image/svg+xml,...".to_string()),
            reference: None,
            reference_hash: None,
            decimals: 18,
        };
        metadata.assert_valid();
        // ...register the owner and mint the initial supply here
        Self { token: FungibleToken::new(b"t"), metadata }
    }
}

#[near]
impl FungibleTokenMetadataProvider for Contract {
    fn ft_metadata(&self) -> FungibleTokenMetadata {
        self.metadata.clone()
    }
}
Coming from EVM

ERC-20 splits metadata into three optional getters (name(), symbol(), decimals()). NEP-148 returns one object, adds an icon, and pins a spec version string.

assert_valid() checks the spec string and that reference and reference_hash are set together.

Check yourself

3 questions · progress saved in this browser

  1. 1.A token’s ft_metadata says decimals: 6. A user’s ft_balance_of returns "2500000". What should a wallet display?
  2. 2.Why does NEP-148 recommend a data URL for icon?
  3. 3.What must the spec field contain for this version of the standard?