Building with Lotus APIs
Several API client libraries are available. These libraries manage the low-level logic of connecting to Lotus node, making requests, and handing the responses.
To use the complete API, we’ll need to run our own node locally. For read-only access, you can also use a hosted node such as those provided by Glif or Infura.
In this tutorial we’ll set up a Lotus node locally and use the filecoin.js library to interact with it.
Prerequisites
Before getting started, you should be familiar with:
Also, make sure you have the following dependencies installed with the latest version:
- Node.js
- NPM
- The dependencies listed on the Install a Lotus node page
Setup
In this section, we’ll set up a Lotus node, install the filecoin.js library, and confirm that they work together correctly.
Lotus node
The following the steps walk through how to install a Lotus node. Further, more detailed, instructions are available on the Install a Lotus node page.
Download the Lotus source code. The version number of the latest release can be found on the lotus releases page:
git clone https://github.com/filecoin-project/lotus.git cd lotus git checkout releases # 'releases' always checks out the latest stable release # if you need a specific release use git checkout <tag_or_release>
Build and install Lotus for the Calibration network:
make clean calibnet sudo make install
Download the latest chain snapshot using the CID listed under
Genesis CAR file
. Then sync to the network which will take several hours to fully sync the chain:lotus daemon --import-snapshot curl -sI https://www.mediafire.com/file/gquphc7qw0ffzdk/lotus_cali_snapshot_2022_05_18_high_959844.car.tar.gz/file
Create a wallet and request some test FIL from the Faucet.
Verify your setup
To make sure everything’s running and set up properly, we should check a couple of things.
First, check the status of your local node:
lotus sync wait
> Worker: 10161; Base: 778820; Target: 778820 (diff: 0) > State: complete; Current Epoch: 778820; Todo: 0 > > Done!
This tells us that our node is fully synced and ready to go.
Next, let’s check that the test FIL has landed in your wallet:
lotus wallet list
Address Balance Nonce t154xvuihhicgluafwmohwzwmtmqp44pwwgewyvma 100 FIL 0
This tells us that the testnet faucet was able to send us some test FIL.
Create a Node.js project
Let’s set up a Node.js project with the necessary dependencies.
Create a new Node.js project.
mkdir build-with-lotus && cd build-with-lotus npm init
Install the filecoin.js library.
npm install filecoin.js
Test a simple function
Now we’re ready to connect to our Lotus node and interact with the Lotus APIs. Let’s write a simple script to query the Lotus version to verify if everything works as expected.
Create a
chainDataQuery.mjs
file in your project and add the following code.// Import necessary packages import { HttpJsonRpcConnector, LotusClient } from "filecoin.js"; // Use the local node URL to create a connection to your Lotus node const localNodeUrl = "http://127.0.0.1:1234/rpc/v0"; const localConnector = new HttpJsonRpcConnector({ url: localNodeUrl }); // lotusClient exposes all Lotus APIs const lotusClient = new LotusClient(localConnector); const version = await lotusClient.common.version(); console.log(version);
Run the following command in your project directory:
node chainDataQuery.mjs
This will output something like:
{ Version: '1.15.0+calibnet+git.0ac1bbc7a', APIVersion: 66816, BlockDelay: 30 }
If everything is set up correctly, your code will be able to connect to the Lotus node and return the chain data as shown above. Check the Lotus Troubleshooting if you run into any errors here.
Get chain data
Blockchain data is a fundamental part of any blockchain network. Using the Lotus API you can query chain data, including tipsets, blocks, messages, network status, storage deals, storage provider details, and more.
Let’s look at some common chain data-related packages in the filecoin.js library’s LotusClient:
LotusClient.common
- get node infoLotusClient.chain
- interact with the blockchainLotusClient.wallet
- manage wallets on the Lotus nodeLotusClient.client
- interact with the storage and retrieval markets as a clientLotusClient.paych
- interact with and manage payment channelsLotusClient.state
- query, inspect, and interact with chain state
In the previous step, we created a chainDataQuery.mjs
file to demonstrate the basic steps to connect to the Lotus node and initialize a LotusClient
to query chain data. Next, let’s use some more functions that don’t require an authentication token.
Get current chain head:
We can add the following code in
chainDataQuery.mjs
to query current head of the chain.//Query the current block head const chainHead = await lotusClient.chain.getHead(); console.log(chainHead);
As a result, it will return the current head of the chain, including current block height, a list of blocks, and a list of CIDs.
{ Cids: [ {'/': 'bafy2bzacecdkonmhngylnnhrk4azkg2wkgcm6cnm5qn5sk4ww5cszjlvkgkd6'}, {'/': 'bafy2bzaceaglcpzhd5gfrzdyt7ce3e5asnbfz3s3stbqyxniziny5snewbpbg'} ], Blocks: [ {...} # long list of additional blocks ], Height: 781268 }
Get messages in a block
//Query all the messages in a block const tipSet = await lotusClient.chain.getTipSetByHeight(781267); const messages = await lotusClient.chain.getBlockMessages(tipSet.Cids[0]); console.log(messages);
Using the above code in
chainDataQuery.mjs
, we can also retrieve messages from a certain block height. And the returned message body will look like the following.{ BlsMessages: [ { Version: 0, To: 't3ukhj6tpkgxjknu54opiaej2vrjz7nh7gzodkqlhpfphc6gxkogzmojv2cnlpgcuwkvnyhloctnc6lmlvceuq', From: 't3ws4jhb7g4n5s7t3mwsc2enzfija2kzo3p7vkqdqbiaqrabqhmfxzzy3knrvn3ykp75okab6pse2nxud3ineq', ... } ], SecpkMessages: [ { Message: [Object], Signature: [Object], CID: [Object] } ], Cids: [ {'/': 'bafy2bzaceapv6ms4m3x4gdaefmpilgsqfhfsrvrciw4iux4n7va47es4vudpa'}, {'/': 'bafy2bzaceaxaz5ews6a2jnwmeafhi2p5xpwjz7kogilakblp3q3wpxa3ax2xy'} ] }
Get wallet balance
With a given Calibration wallet address, you can query its balance by calling lotusClient.wallet.balance() function.
//Query Wallet balance const walletBalance = await lotusClient.wallet.balance("t1ne72cbn6r55wea7ifjv4ypyti7t2df5dumsjhzq");
Apart from the basic data queries shown above, there are many more features. Please see the Lotus JSON-RPC API Reference for all Lotus API definitions. Note that some API calls require an authorization token.
Manage wallets and FIL tokens
Filecoin wallets hold the addresses that you can use to manage FIL tokens, sign transactions, and pay for gas fees. In this section, we will demonstrate different ways to manage your Filecoin wallet. There are two types of Filecoin wallet:
- Lotus wallets that are held directly in Lotus node.
- Light wallets that are detached from any node, and are managed by key-pairs.
Lotus wallets
Lotus wallets are created and hosted in the Lotus node. When the Lotus node is running, the wallet is opened in the node and you can manage your wallet’s addresses and balances using the command line or API calls. You will need an authorization token with sign permissions to interact with a wallet. Authorization tokens are often called auth tokens.
First, let’s make sure your local Lotus node is running and the wallet address contains some testnet FIL.
lotus wallet list
This will output something like:
Address Balance Nonce t154xvuihhicgluafwmohwzwmtmqp44pwwgewyvma 100 FIL 0
Next, generate an authorization token with sign permission:
lotus auth create-token --perm sign
This will output your auth token. Save this somewhere, we’ll be using it later:
eyJhbGciOiJIUzI1NiIsInRaaaaaa.xxxxxxx.bbbbbbbbbbbvq1W0ZjqrXHygd6fBRk
Create a new file called
lotusWallet.mjs
in the same project as earlier. This is where we’ll write all out wallet-related code.In
lotusWallet.mjs
import modules from filecoin.js and initialize theHttpJsonRpcConnector
,LotusClient
, andLotusWalletProvider
components:import { HttpJsonRpcConnector, LotusWalletProvider, LotusClient} from "filecoin.js"; import BigNumber from "bignumber.js"; // Use your localNodeUrl and auth token to interact with Lotus wallet const localNodeUrl = "http://127.0.0.1:1234/rpc/v0"; const signAuthToken ="<YOUR-SIGN-AUTH-TOKEN>" const httpConnector = new HttpJsonRpcConnector({ url: localNodeUrl, token: signAuthToken }); const lotusClient = new LotusClient(httpConnector); const lotusWallet = new LotusWalletProvider(lotusClient);
Let’s create a second wallet address so we can test transfers. The new address will be created in your Lotus node and start with 0 token balance:
async function newWallet(){ try { const account = await lotusWallet.newAddress(); const hasWallet = await lotusWallet.hasAddress(account); if(hasWallet){console.log("new wallet address: ", account)} } catch (error) { console.log(error); } } // Test newWallet function. newWallet();
Run the script to create the new address:
node lotusWallet.mjs
This will output something like:
new wallet address: t1ax5kdqxrrecxpyys6svvxjing7shqju26ytcsoa
You can also verify if the new wallet address is created in your local Lotus node using Lotus CLI command:
lotus wallet list
This will output smething like:
Address Balance Nonce Default t154xvuihhicgluafwmohwzwmtmqp44pwwgewyvma 100 FIL 0 X t1ax5kdqxrrecxpyys6svvxjing7shqju26ytcsoa 0 FIL 0 #This is the new created address
Now, let’s try to transfer the FIL token to this new address. Under the hood, this will create a transaction to transfer FIL token from
fromAddress
totoAddress
, sign the transaction using the Lotus wallet, and send the signed transaction to the Lotus node. Fortunately, these processes are taken care of by filecoin.js so that you can do all these by calling thelotusWallet.sendMessage(params)
methods. Add the following code tolotusWallet.mjs
:async function transferFIL(){ try { const fromAddress = await lotusWallet.getDefaultAddress(); const toAddress = "t1ax5kdqxrrecxpyys6svvxjing7shqju26ytcsoa"; // SendMessage will create a message, assign a nounce, // Sign the message using the fromAddress and push it to mpool const msgResult = await lotusWallet.sendMessage({ From: fromAddress, To: toAddress, Value: new BigNumber(10000000000000000000)//in autoFIL }); console.log("message: ", msgResult); } catch (error) { console.log(error); } } // Test FIL transfer function transferFIL();
Run the code to test the transfer of FIL:
node lotusWallet.mjs
This will output a message CID. You can use this CID to check the message by pasting it into a block explorer:
message CID: { '/': 'bafy2bzacecrrnelqjqhsspfayzcxmx4nhaw6t7j5dpexuxix5jkgloyze2jpu' }
Now, you should have created a new address in your Lotus node and also transferred some FIL tokens to it. Next, let’s query the balance of all your wallet addresses in the Lotus node:
async function walletBalance(){ try { //Get the list of wallet address on your Lotus node & query their balance const walletAdds = await lotusWallet.getAddresses(); walletAdds.forEach(async(address) => { try { const balance = await lotusWallet.getBalance(address); console.log(address + " : "+balance); } catch (error) { console.log(error); } }); } catch (error) { console.log(error); } } // Test walletBalance function walletBalance();
Run the code to test the FIL transfer:
node lotusWallet.mjs
This will output the FIL values in attoFIL:
t154xvuihhicgluafwmohwzwmtmqp44pwwgewyvma : 89999999675971952500 t1ax5kdqxrrecxpyys6svvxjing7shqju26ytcsoa : 10000000000000000000
You can also check the balance directly in your Lotus node through the command line:
lotus wallet list
This will output something like:
Address Balance Nonce Default t154xvuihhicgluafwmohwzwmtmqp44pwwgewyvma 89.9999996759719525 FIL 2 X t1ax5kdqxrrecxpyys6svvxjing7shqju26ytcsoa 10 FIL
Notice that the sum of both balances is slightly less than 100 FIL. This is because we had to pay gas to transfer FIL from one address to another. Every process that makes a state change on the blockchain incurs a gas cost.
Light wallets
The filecoin.js
library also supports creating a light wallet using a mnemonic and a password. As long as you have the mnemonic and password of your wallet, you can recover it anywhere and use it by sending PRC requests to any Lotus full nodes.
In your
build-with-lotus
project, let’s create alightWallet.mjs
file. Since we are using a light wallet, we will use the Glif hosted endpoint as the connection to the Filecoin network to send transactions. First, we import all the modules and create a connection and lightWallet client using the Glif endpoint’s URL:import { HttpJsonRpcConnector, LotusClient, LightWalletProvider,MnemonicWalletProvider} from "filecoin.js"; import BigNumber from "bignumber.js"; const glifNodeurl = "https://calibration.node.glif.io"; const glifNodeConn = new HttpJsonRpcConnector({url:glifNodeurl}); const glifClient = new LotusClient(glifNodeConn);
When creating a light wallet using the BIP39 standard, it is very important to backup the mnemonic code, the
encryptedWallet
object, and your password. Do not share them — anyone who has this information will have control of your light wallet and assets!async function createLightWallet(){ try { const lightWallet = new LightWalletProvider(glifClient, () => { return '<YOUR-Password>' }, 'test'); let mnemonic = await lightWallet.createLightWallet('<YOUR-Password>'); console.log(mnemonic); let encryptedWallet = lightWallet.keystore.serialize(); console.log(encryptedWallet); const lightWalletAddress = await lightWallet.getDefaultAddress(); console.log(lightWalletAddress); } catch (error) { console.log(error); } } // Test the function createLightWallet()
Calling the
createLightWallet()
function will output your mnemonic,encryptedWallet
object, and your wallet address:mnemonic: mouse escape tilt destroy xxx top yyy spice art zzz price image encryptedWallet: {......} Wallet Address: t1qfyuosiqgoycy5ojwsxtpnlp44orxg4gtmuhhaq
Make sure to backup these three items by saving and storing
encryptedWallet
as a json file, and recording mnemonic and wallet address somewhere safe.Request some FIL tokens from the Calibration Faucet using the wallet address you just received.
Next let’s transfer some FIL from your light wallet to another wallet:
async function sendFromLightWallet(){ try { //This is just for demo. We do NOT recommend exposing your mnemonic and password in code. const mnemonic = '<YOUR mnemonic code>'; const pw = '<YOUR-Password>'; const lightWallet = new LightWalletProvider(glifClient, () => { return pw }, 'test'); await lightWallet.recoverLightWallet(mnemonic, pw); const lightWalletAddress = await lightWalletHttp.getDefaultAddress(); //Create a FIL transfer message const message = await lightWallet.createMessage({ From: lightWalletAddress, To: "t1ax5kdqxrrecxpyys6svvxjing7shqju26ytcsoa", Value: new BigNumber(15000000000000000000)//in autoFIL }); //Sign and send the message const signedMessage = await lightWallet.signMessage(message); const msgCid = await lightWallet.sendSignedMessage(signedMessage); console.log(msgCid); } catch (error) { console.log(error); } } // Test the function sendFromLightWallet();
Run the code to test this transfer:
node lightWallet.mjs
This will output a message CID.
{ '/': 'bafy2bzaceamdsqcc3jccrhvwrz6kmpujfkg6crwynmrtal2nsmwkqv22bktrs' }
After the message containing this transfer makes it on-chain, you will be able to see it using the Filecoin Calibration Explorer website.
By this point we’ve learned how to create a wallet on the Lotus node using an RPC API call, as well as create a light wallet and manage its assets through a hosted Glif node. To explore more features of the Lotus API, keep following this tutorial. You can also go to the Lotus JSON-RPC API and start creating your own projects on Filecoin.
Make a storage deal
Storing data is one of the most important features of Filecoin. In this section, we’ll walk through the end-to-end process of storing your data on the Filecoin network. We’ll start by importing your data to the local Lotus node, then make a storage deal with a storage provider, and finally wait for the deal to complete.
Note that the simplest way to store data on Filecoin is by using one of the many storage helper tools which wrap all of the following steps into a single API call. However, we include this section of the tutorial for anyone who wants to explore Filecoin storage at the protocol level in order to build developer tools or deepen their understanding.
Let’s dive in!
Start by generating an admin auth token. This is required to import data into the Lotus node:
lotus auth create-token --perm admin
This will output your auth token. Make a note of this, we’ll be using it later.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9......n7ugba1aJECuZXkMgJxHUH08MKTENQ-hAtsosDu-HXg
Create a
storeFile.mjs
file in yourbuild-with-lotus
project and add the following boilerplate code:import { HttpJsonRpcConnector, LotusClient} from "filecoin.js"; const localNode = "http://127.0.0.1:1234/rpc/v0"; const adminAuthToken="<Your-admin-auth-token>" const localConnector = new HttpJsonRpcConnector({ url: localNode, token: adminAuthToken }); const lotusClient = new LotusClient(localConnector); const lotusWallet = new LotusWalletProvider(lotusClient); async function storeFile(){ try { //1. import data to lotus //2. query storage provider's offer for storing this file //3. start storage deal with SP } catch (error) { console.log(error); } } storeFile();
The next step is to import your data to the Lotus node:
const importResult = await lotusClient.client.import({ Path: "PATH_TO_YOUR_FILE", IsCAR: false, }); console.log(importResult.Root);
This returns a CID of your data we’ll use to refer to the data in future steps:
{ '/': 'bafykbzaceclwpu76hyjemz3ggynjek4y2mh47pwamfp3rk3icnjvsmhsumio6' }
Next, we need to find a storage provider that is willing to accept your storage deal. Since we are using the calibration network for this tutorial, we can use the
list-asks
to find storage providers. It will return a list ofasks
from the storage providers which are actively accepting deals, containing the details of their storage asks:lotus client list-asks
This will output something like:
.. querying asks * Queried 43 asks, got 8 responses t029598: min:256 B max:32 GiB price:0 FIL/GiB/Epoch verifiedPrice:0 FIL/GiB/Epoch ping:340.644025ms t031337: min:256 B max:32 GiB price:0 FIL/GiB/Epoch verifiedPrice:0 FIL/GiB/Epoch ping:342.296042ms t01105: min:256 B max:32 GiB price:0.0000000002 FIL/GiB/Epoch verifiedPrice:0.00000000002 FIL/GiB/Epoch ping:589.173496ms t032368: min:256 B max:32 GiB price:0.0000000005 FIL/GiB/Epoch verifiedPrice:0.00000000005 FIL/GiB/Epoch ping:294.554013ms
As a side note; if you were working on Filecoin Mainnet, you could use the Filecoin Plus program to find storage providers offering free storage for verified clients:
Pick a storage provider ID from the
list-asks
response and add it into the following code. Replacet00001
with the storage provider ID you picked://2. query Miner offers for storing this file const queryOffer = await lotusClient.client.minerQueryOffer('t00001', importResult.Root); console.log(queryOffer); const isActive = importResult.Root["/"] === queryOffer.Root["/"]; console.log("Provider is active: ", isActive);
If the storage provider you picked is active, the above code will output:
Provider is active: true
Create a storage deal with a storage provider. Replace
t00001
with the storage provider ID you chose://3. start storage deal with SP if(isActive){ const dealCid = await lotusClient.client.startDeal({ Data: { TransferType: 'graphsync', Root: importResult.Root, }, Miner: 't00001', Wallet: await lotusWallet.getDefaultAddress(), EpochPrice: , MinBlocksDuration: 518400, }); console.log("dealCID is ", dealCid); }
Let’s run the js code to store a file on Filecoin:
node lightWallet.mjs
If everything runs successfully the script will output a deal CID:
{ '/': 'bafy2bzaceamdsqcc3jccrhvwrz6kmpujfkg6crwynmrtal2nsmwkqv22bktrs' }
By this step, you have made a storage deal with a storage provider successfully. The Lotus node will start processing the data and sending it to the storage provider. Your storage deal will need to go through many sub-processes to be finalized on-chain. See the Deal states table for more details.
You can check the status of your storage deal via the Lotus command line using its CID.
lotus client list-deals --show-failed
This should output something like:
DealCid DealId Provider State On Chain? Slashed? PieceCID Size Price Duration Verified
...dbuwcbjq 0 t024557 StorageDealFundsReserved N N ...7rkejcnq 3.969 MiB 0 FIL 522909 false
...pkbhfkju 0 t01105 StorageDealError N N ...7rkejcnq 3.969 MiB 0.0000177158 FIL 88579 false
...wb4wiuwq 0 t01105 StorageDealClientFunding N N ...7rkejcnq 3.969 MiB 0.0001041094 FIL 520547 false
Congratulations on making it all the way through this tutorial! In this tutorial, we learned the basics of interacting with the Filecoin network using an API client library and local Lotus node. This can serve as the foundation for you explore the complete Lotus JSON-RPC API.