Besides the native NEAR token, NEAR accounts have access to a multitude of tokens to use throughout the ecosystem. Moreover, it is even possible for users to create their own fungible tokens.
In contrast with the NEAR native token, fungible token (FT) are not stored in the user’s account. In fact, each FT lives in their own contract which is in charge of doing bookkeeping. This is, the contract keeps track of how many tokens each user has, and handles transfers internally.
In order for a contract to be considered a FT-contract it has to follow the NEP-141 and NEP-148 standards. The NEP-141 & NEP-148 standards explain the minimum interface required to be implemented, as well as the expected functionality.
Token Factory Tool
You can create an FT using the toolbox on Dev Portal. The FT Tool is a token factory, you can interact with it through graphical interface, or by making calls to its contract.
The FT you create will live in the account <your_token_symbol>.tkn.primitives.near
(e.g. test.tkn.primitives.near
).
Deploying Your Own Contract
You can also create a fungible token by deploying and initializing a canonical FT contract.
On initialization you will define the token’s metadata such as its name (e.g. Ethereum), symbol (e.g. ETH) and total supply (e.g. 10M). You will also define an owner
, which will own the tokens total supply.
To initialize a FT contract you will need to deploy it and then call the new
method defining the token’s metadata.
cargo near deploy <account-id> with-init-call new json-args '{"owner_id": "<owner-account>", "total_supply": "1000000000000000", "metadata": { "spec": "ft-1.0.0", "name": "Example Token Name", "symbol": "EXLT", "decimals": 8 }}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send
heck the [Contract Wizard](https://dev.near.org/contractwizard.near/widget/ContractWizardUI) to create a personalized FT contract!.
Querying Metadata
You can query the FT’s metadata by calling the ft_metadata
.
Checking Balance
To know how many coins a user has you will need to query the method ft_balance_of
.
Registering a User
In order for an user to own and transfer tokens they need to first register in the contract. This is done by calling storage_deposit
and attaching 0.00125Ⓝ.
By calling this storage_deposit
the user can register themselves or register other users.
You can make sure a user is registered by calling `storage_balance_of`.
fter a user calls the `storage_deposit` the FT will appear in their Wallets.
Transferring Tokens
To send FT to another account you will use the ft_transfer
method, indicating the receiver and the amount of FT you want to send.
Attaching FTs to a Call
Natively, only NEAR tokens (Ⓝ) can be attached to a function calls. However, the FT standard enables to attach fungible tokens in a call by using the FT-contract as intermediary. This means that, instead of you attaching tokens directly to the call, you ask the FT-contract to do both a transfer and a function call in your name.
Let’s assume that you need to deposit FTs on Ref Finance.
How it works:
- You call ft_transfer_call in the FT contract passing: the receiver, a message, and the amount.
- The FT contract transfers the amount to the receiver.
- The FT contract calls receiver.ft_on_transfer(sender, msg, amount)
- The FT contract handles errors in the ft_resolve_transfer callback.
- The FT contract returns you how much of the attached amount was actually used.
Handling Deposits (Contract Only)
If you want your contract to handle deposit in FTs you have to implement the ft_on_transfer
method. When executed, such method will know:
- Which FT was transferred, since it is the predecessor account.
- Who is sending the FT, since it is a parameter
- How many FT were transferred, since it is a parameter
- If there are any parameters encoded as a message
The ft_on_transfer
must return how many FT tokens have to be refunded, so the FT contract gives them back to the sender.
// Implement the contract structure
#[near(contract_state)]
impl Contract {}
#[near]
impl FungibleTokenReceiver for Contract {
// Callback on receiving tokens by this contract.
// `msg` format is either "" for deposit or `TokenReceiverMessage`.
fn ft_on_transfer(
&mut self,
sender_ amount: U128,
msg: String,
) -> PromiseOrValue<U128> {
let token_in = env::predecessor_account_id();
assert!(token_in == self.ft_contract, "{}", "The token is not supported");
assert!(amount >= self.price, "{}", "The attached amount is not enough");
env::log_str(format!("Sender
if msg.is_empty() {
// Your internal logic here
PromiseOrValue::Value(U128(0))
} else {
let message =
serde_json::from_str::<TokenReceiverMessage>(&msg).expect("WRONG_MSG_FORMAT");
match message {
TokenReceiverMessage::Action {
buyer_id,
} => {
let buyer_id = buyer_id.map(|x| x.to_string());
env::log_str(format!("Target buyer // Your internal business logic
PromiseOrValue::Value(U128(0))
}
}
}
}
}
Additional Resources
- NEP-141 and NEP-148 standards
- FT Event Standards
- FT reference implementation
- Fungible Tokens 101 – a set of tutorials that cover how to create a FT contract using Rust.