- Storage fee management
- Escrow pattern and derive macro
- Owner pattern and derive macro
- Pause pattern and derive macro
- Role-based access control
- Derive macros for NEP standards
- NEP-141 (fungible token), extension NEP-148
- NEP-145 (storage management), and integrations for the fungible token and non-fungible token standards
- NEP-171 (non-fungible token), extensions NEP-177, NEP-178, NEP-181
- NEP-297 (events)
Introduction
Use the existing global FT contract by default. Use this guide only if your application truly requires behavior the global contract does not provide. Usenear-contract-standards or near-sdk-contract-tools for the token implementation; do not implement the FT standard by hand.
near-sdk-contract-tools provides derive macros for FT standards and access control, allowing us to focus on custom logic such as minting and burning.
In this example, the contract starts with zero supply and wraps deposited NEAR into tokens. Only the current owner can mint tokens by depositing NEAR or burn tokens from their own balance to redeem NEAR. Other accounts can hold and transfer tokens using the standard FT methods.
Basic FT Methods
To derive basic FT methods to our contract, we need to deriveFungibleToken macro to our contract struct:
lib.rs
ft_balance_offt_metadataft_total_supplystorage_balance_boundsstorage_balance_offt_resolve_transferft_transferft_transfer_callstorage_depositstorage_unregisterstorage_withdraw
Owner macro which adds the following methods:
own_get_ownerown_get_proposed_ownerown_accept_ownerown_propose_ownerown_renounce_owner
Initialization
To initialize the basic FT contract with a custom owner, metadata, and storage bounds, we implement thenew method. The owner_id account can be different from the account where the contract is deployed. Calling Owner::init records that account as the current owner:
lib.rs
Transfer Hook
Use a transfer hook to run application logic before or after the library-provided transfer. Keep the standardft_transfer implementation in the library:
transfer_hook.rs
lib.rs
Minting
The FT standards do not define a minting method, so we implement our ownmint method. It takes no arguments and mints tokens for the owner based on the attached NEAR deposit.
We import the Owner trait and call self.assert_owner() before making any changes. This checks that the account calling the method is the current owner recorded by the contract:
mint.rs
997500000000000000000000 token units, while a registered owner receives 1000000000000000000000000 token units.
Burning
The FT standards do not define a burning method either. We implement aburn method that removes amount token units from the owner’s balance and refunds the corresponding NEAR.
As with minting, self.assert_owner() restricts the call to the current owner. The caller must attach exactly 1 yoctoNEAR, and amount is passed as a base-10 string in the smallest token units:
burn.rs
amount, then schedules a transfer of amount yoctoNEAR to the owner. For example, {"amount": "1000000000000000000000000"} burns token units representing 1 NEAR and refunds 1 NEAR. The owner must have enough tokens in their balance to burn the requested amount.
The owner checks in both methods follow ownership transfers through own_propose_owner and own_accept_owner. Once the new owner accepts, only that account can mint or burn. Token holders who are not the current owner can still transfer tokens, but cannot mint or redeem them through these methods.
Testing the Contract
From the example repository, run:near-sandbox and near-api to check minting, burning, ownership transfers, storage refunds, and cross-contract transfers.
Conclusion
Usingnear-sdk-contract-tools is a very simple and flexible way to create FT contract with minimal boilerplate which allows us to focus on the business logic.
You can further extend this contract with more features like pausing, role-based access control, escrow pattern, and more by deriving corresponding macros from the package.