Fungible Token (FT)
Example implementation of a Fungible Token contract which uses near-contract-standards and simulation tests. This is a contract-only example.
Prerequisites
If you're using Gitpod, you can skip this step.
- Make sure Rust is installed per the prerequisites in
near-sdk-rs
- Ensure
near-cli
is installed by runningnear --version
. If not installed, install with:npm install -g near-cli
Building
To build run:
./scripts/build.sh
Using this contract
Quickest deploy
You can build and deploy this smart contract to a development account. Dev Accounts are auto-generated accounts to assist in developing and testing smart contracts. Please see the Standard deploy section for creating a more personalized account to deploy to.
near dev-deploy --wasmFile res/fungible_token.wasm --helperUrl https://near-contract-helper.onrender.com
Behind the scenes, this is creating an account and deploying a contract to it. On the console, notice a message like:
Done deploying to dev-1234567890123
In this instance, the account is dev-1234567890123
. A file has been created containing a key pair to
the account, located at neardev/dev-account
. To make the next few steps easier, we're going to set an
environment variable containing this development account id and use that when copy/pasting commands.
Run this command to the environment variable:
source neardev/dev-account.env
You can tell if the environment variable is set correctly if your command line prints the account name after this command:
echo $CONTRACT_NAME
The next command will initialize the contract using the new
method:
near call $CONTRACT_NAME new '{"owner_id": "'$CONTRACT_NAME'", "total_supply": "1000000000000000", "metadata": { "spec": "ft-1.0.0", "name": "Example Token Name", "symbol": "EXLT", "decimals": 8 }}' --accountId $CONTRACT_NAME
To get the fungible token metadata:
near view $CONTRACT_NAME ft_metadata
Standard deploy
This smart contract will get deployed to your NEAR account. For this example, please create a new NEAR account. Because NEAR allows the ability to upgrade contracts on the same account, initialization functions must be cleared. If you'd like to run this example on a NEAR account that has had prior contracts deployed, please use the near-cli
command near delete
, and then recreate it in Wallet. To create (or recreate) an account, please follow the directions on NEAR Wallet.
Switch to mainnet
. You can skip this step to use testnet
as a default network.
export NEAR_ENV=mainnet
In the project root, log in to your newly created account with near-cli
by following the instructions after this command:
near login
To make this tutorial easier to copy/paste, we're going to set an environment variable for your account id. In the below command, replace MY_ACCOUNT_NAME
with the account name you just logged in with, including the .near
:
ID=MY_ACCOUNT_NAME
You can tell if the environment variable is set correctly if your command line prints the account name after this command:
echo $ID
Now we can deploy the compiled contract in this example to your account:
near deploy --wasmFile res/fungible_token.wasm --accountId $ID
FT contract should be initialized before usage. You can read more about metadata at 'nomicon.io'. Modify the parameters and create a token:
near call $ID new '{"owner_id": "'$ID'", "total_supply": "1000000000000000", "metadata": { "spec": "ft-1.0.0", "name": "Example Token Name", "symbol": "EXLT", "decimals": 8 }}' --accountId $ID
Get metadata:
near view $ID ft_metadata
Transfer Example
Let's set up an account to transfer some tokens to. These account will be a sub-account of the NEAR account you logged in with.
near create-account bob.$ID --masterAccount $ID --initialBalance 1
Add storage deposit for Bob's account:
near call $ID storage_deposit '' --accountId bob.$ID --amount 0.00125
Check balance of Bob's account, it should be 0
for now:
near view $ID ft_balance_of '{"account_id": "'bob.$ID'"}'
Transfer tokens to Bob from the contract that minted these fungible tokens, exactly 1 yoctoNEAR of deposit should be attached:
near call $ID ft_transfer '{"receiver_id": "'bob.$ID'", "amount": "19"}' --accountId $ID --amount 0.000000000000000000000001
Check the balance of Bob again with the command from before and it will now return 19
.
Testing
As with many Rust libraries and contracts, there are tests in the main fungible token implementation at ft/src/lib.rs
.
Additionally, this project has simulation tests in tests/sim
. Simulation tests allow testing cross-contract calls, which is crucial to ensuring that the ft_transfer_call
function works properly. These simulation tests are the reason this project has the file structure it does. Note that the root project has a Cargo.toml
which sets it up as a workspace. ft
and test-contract-defi
are both small & focused contract projects, the latter only existing for simulation tests. The root project imports near-sdk-sim
and tests interaction between these contracts.
You can run unit tests with the following command:
cd ft && cargo test -- --nocapture --color=always
You can run integration tests with the following commands: Rust
cd integration-tests/rs && cargo run --example integration-tests
TypeScript
cd integration-tests/ts && yarn && yarn test
Notes
- The maximum balance value is limited by U128 (
2**128 - 1
). - JSON calls should pass U128 as a base-10 string. E.g. "100".
- This does not include escrow functionality, as
ft_transfer_call
provides a superior approach. An escrow system can, of course, be added as a separate contract or additional functionality within this contract.
No AssemblyScript?
near-contract-standards is currently Rust-only. We strongly suggest using this library to create your own Fungible Token contract to ensure it works as expected.
Someday NEAR core or community contributors may provide a similar library for AssemblyScript, at which point this example will be updated to include both a Rust and AssemblyScript version.
Contributing
When making changes to the files in ft
or test-contract-defi
, remember to use ./build.sh
to compile all contracts and copy the output to the res
folder. If you forget this, the simulation tests will not use the latest versions.
Note that if the rust-toolchain
file in this repository changes, please make sure to update the .gitpod.Dockerfile
to explicitly specify using that as default as well.