Adding Filecoin support to your NEAR dApp, the easy way

tutorials Jul 08, 2021

Textile recently announce our vision for bringing native Filecoin storage to NEAR, ETH, Polygon, and others. In that post (and series of Tweets), we talk about plans for rolling out a series of Bridge smart contracts that will provide permissionless storage to users and applications on each target blockchain. In our initial release, we focused on NEAR's Testnet, and it is already ready to start testing! This initial release offers a fully functional storage bridge that can store data for any NEAR address owner. To get folks excited and ready to #BUIDL already, today we're providing a short technical walkthrough of one of our demo dApps that leverages the @textile/near-storage Javascript SDK. For existing NEAR developers, the library interacts with the near-api-js code you've already written to provide an easy to use API for storing any amount of data off of the NEAR blockchain, and in the Filecoin network. For those new to the NEAR ecosystem, we think you'll love how easy and intuitive it is to get started with NEAR, Textile, and Filecoin!

Let's dig in...

What is it?

This demo allows users to authenticate with their NEAR (Testnet) account, create unique visualizations of cellular automata, store those visualizations on the Filecoin network, and mint verifiable records of the stored visualizations within a NEAR smart contract.

This app was built starting from one of the many NEAR example apps, so be sure you're familiar with those examples and the NEAR docs so you know the basics of NEAR smart contracts and near-api-js usage.

You can find the demo source code here: https://github.com/textileio/near-storage-dapp-demo. For a pure web-app (i.e., no Smart Contract, but still interacts with Near) see our basic demo.

Quick Start

To run this project locally:

  1. Prerequisites: Make sure you have Node.js ≥ 12 installed (https://nodejs.org).
  2. Install dependencies: npm install (or just npm i)
  3. Run the local development server: npm run dev (see package.json for a full list of scripts you can run with npm)

Now you'll have a local development environment backed by the NEAR TestNet and Filecoin Storage! Running npm run dev will tell you the URL you can visit in your browser to see the app.

How it works

There are two main components in this example NEAR dApp using Filecoin Storage:

  1. The example NEAR dApp and associated smart contract. This would be replaced by your own app and smart contract.
  2. The @textile/near-storage library which abstracts use of the Filecoin Storage NEAR smart contract and Filecoin Storage Provider into an easy-to-use API.

Example dApp and Smart Contract

We start with code you'll typically see in any NEAR dApp... In src/index.tsx, we read our configuration, initialize the Near client, create a WalletConnection, get a reference to the current NEAR user, and create a reference to our example app's smart contract:

import getConfig from './config.js';
import { connect, keyStores, Contract, WalletConnection } from 'near-api-js';

// Read the configuration
const nearConfig = getConfig(ENV.NODE_ENV as any || 'testnet');

// Initializing connection to the NEAR TestNet
const near = await connect({
  deps: {
    keyStore: new keyStores.BrowserLocalStorageKeyStore()
  },
  ...nearConfig
});

// Needed to access wallet
const walletConnection = new WalletConnection(near, null);

// Load in account data
let currentUser;
if (walletConnection.getAccountId()) {
  currentUser = {
    accountId: walletConnection.getAccountId(),
    balance: (await walletConnection.account().state()).amount
  };
}

// Initializing our contract APIs by contract name and configuration
const contract = await new Contract(walletConnection.account(), nearConfig.contractName, {
  viewMethods: ['getStoredAssets'],
  changeMethods: ['storeNewAsset'],
  sender: walletConnection.getAccountId()
});

Our app's smart contract (the source is in the assembly directory) exposes two functions; storeNewAsset which we'll use to store on chain records of the cellular automata visualizations stored on Filecoin and getStoredAssets which will be used to retrieve the list of all records previously stored using storeNewAsset.

Using Filecoin Storage

Initializing the API

After creating a NEAR WalletConnection in the src/index.tsx code above, we pass that object into the initfunction provided by @textile/near-storage. This provides the API we'll use from then on to interact with Filecoin Storage.

import { init, requestSignIn } from "@textile/near-storage"

const storage = await init(walletConnection)

We bind our newly created NEAR and Filecoin Storage objects to the App React component, and the web app is then rendered in App.tsx.

User Sign In

Before using Filecoin Storage to store data, the web app user must be signed in to their NEAR account. If they aren't already signed in, Filecoin Storage provides a helper method to initiate the sign in. Here, we use it inside a function that is bound to a clickable UI element:

const signIn = () => {
  requestSignIn(walletConnection, {})
};

Adding a Deposit

Before a user can store data using Filecoin Storage, a deposit (or fund locking) must be made, either by the user or by you (the dApp developer) on the user's behalf, into the Filecoin Storage NEAR smart contract. The deposited funds help provide the Filecoin Storage Provider some amount of Sybil attack resistance. After depositing funds, the user has an active storage session. The session will expire after an hour, and the deposited funds will be returned to the sender.

const addDeposit = async () => {
  await storage.addDeposit()
}

Storing Data

Once a deposit is made and the user has an active storage session, we can now store data. It's as simple as passing a File object into the Filecoin Storage store function. Our mint funtion in App.tsx builds a File from a blob of data, our cellular automaton visualization, stores the file using the store function, and then saves the resulting information about the stored data in our dApp's smart contract using its storeNewAsset function:

const mint = async () => {
  if (artwork == undefined) {
    alert("error: no token selected");
    return;
  }

  const data = await fetch(artwork)
  const blob = await data.blob()

  const file = new File([blob], "rule.png", {
    type: "image/png",
    lastModified: new Date().getTime(),
  });
  const stored = await storage.store(file)
  const rule = "" + parseInt([...positionals].reverse().join(""), 2)
  
	contract.storeNewAsset(
    { id: stored.id, cid: stored.cid["/"], rule },
    BOATLOAD_OF_GAS,
    Big('0').toFixed()
  ).then(() => {
    contract.getStoredAssets().then((tokens: any) => {
      setTokens(tokens);
    });
  });
  return
}

We've now stored data using Filecoin Storage and kept a record of that data storage on the NEAR blockchain!

Checking Filecoin Storage Status

You stored data is available on the IPFS network (note that during the Testnet release, data will pinned for a limited time only), but stored data takes time to make it through the Filecoin Storage Provider, become sealed with a Filecoin miner, and verified on the Filecoin blockchain. You can check the data's status at any time by calling the Filecoin Storage status method. In App.tsx we do that when the page loads by getting a list of stored data IDs from our dApp's smart contract and passing those IDs to the statusmethod. It's a bit of Javascript async Promise wrangling, but all comes down to calling:

useEffect(() => {
  ...
  return await storage.status(asset.id)
  ...
}, []);

And that's just about it. This is by far the easiest way to integrate Filecoin storage into your existing NEAR dApp development workflow. All in all, storing data on Filecoin adds about 10 lines of code to your dApp, and plays nicely with the ways you already interact with off-chain storage. It's that easy!

Learn More

Our example dApp provides a thorough example of integrating Filecoin Storage into a NEAR dApp. We've covered all the highlights of the integration in this README, but be sure to check out the full Filecoin Storage documentation for more.

Exploring the Code

  1. The backend code lives in the /assembly folder. This code gets deployed to the NEAR blockchain when you run npm run deploy:contract. This sort of code-that-runs-on-a-blockchain is called a "smart contract" – learn more about NEAR smart contracts.
  2. The frontend code lives in the /src folder. /src/index.html is a great place to start exploring. Note that it loads in /src/index.ts, where you can learn how the frontend connects to the NEAR blockchain.

Both contract and client-side code will auto-reload as you change source files.

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.