This week I have been researching how to improve Symbol's blockchain onboarding for developers. In the docs getting started tutorial, there is a crucial step in which devs need to ask for tokens to pay for the transaction fees. This "faucet" is hosted on a separate site, leading visitors to leave the documentation page to complete the step.

Currently, I'm considering embedding a form on the page. This change would permit newcomers to receive tokens as they follow the tutorial. Since there is the need to store a private key that holds the funds, it's not feasible to code a solution directly on the client-side. However, I don't want to host any infrastructure or maintain a full-featured Node.js project. For this reason, I come up with the idea of combining blockchain technology with serverless functions.

In this article, I share how I combined blockchain and serverless technologies to create a faucet application using Symbol and Netlify Functions.

Do you want to improve developers' onboarding process? Shoot me a message, and let's figure out how to enhance your product developer experience together.

What is Symbol blockchain?

Symbol is a next-generation, open-source, decentralized blockchain platform. Thanks to its REST API, developers can integrate blockchain capabilities in new or existing applications with lightweight code using any programming language.

Unlike other blockchain solutions, Symbol does not allow developers to define new smart contracts that will execute on the platform. Instead, the platform provides a predefined set of tested built-in features that solve the most common use cases for using blockchain technology through API calls. For instance, Symbol comes with plugins to handle asset management, access control, multi-signature and escrow services, and cross-chain swaps, among others.

The code that defines a Symbol application remains off-chain. This greatly reduces inherent immutability risk since devs can change parts of the application when necessary instead of defining a rigid contract that cannot change once deployed.

... and serverless?

An application needs to live somewhere to be accessed by others.

Initially, applications were located in servers within the companies facilities. Now, it's common to hire virtual machines from providers such as Amazon or Google instead of owning the iron at home. Still, developers have to shift the focus from development to configure the servers, virtual machines, or containers to run their applications.

The serverless architecture provides a higher abstraction for devs, enabling them to execute code in an environment entirely managed by others.

Executing serverless functions is usually cheaper than running a highly available and distributed server 24 hours a day, considering that functions are charged per-use. One of the platforms offering Function-as-a-Service (FaaS) is Netlify, which adds a simplistic configuration layer on top of Amazon Lambda.

How everything fits together?

Instead of hosting a full-featured application in a virtual server, serverless functions can host each piece of code using Symbol features separately.

In a single function, we could transform the response returned by the Symbol API, combine different services, and spun a new instance running the code block for each request.

Another handy use case is the possibility to use the TypeScript SDK to build integrations in any other language by creating custom APIs. While reading from Symbol blockchain is as straightforward as consuming an API call, things start getting trickier when changing this state of the blockchain is required since nodes only accept serialized data.

To serialize transactions, we typically use a Software Development Kit (SDK), but up to the moment, Symbol does not offer an SDK for every programming language. We can fill this gap by creating custom serverless functions in plain JavaScript, that later can be consumed by apps written in any other language.

Diagram using Lambda functions to integrate Symbol with an existent solution

Imagine a company willing to integrate Symbol blockchain into their app written in Ruby. Unfortunately, there is no Symbol SDK for Ruby. To overcome the problem, they could use a lambda function to record transactions on the blockchain.

Overview of the application

To combine serverless and blockchain apps, we are going to build a sample serverless faucet for Symbol.

Demo: https://symbol-faucet.netlify.com/

The faucet presented is hosted on GitHub, made available to others thanks to Netlify's CDN and their service called Functions.

Requirements

To run this example, make sure you have installed:

  • Git
  • NodeJS 12 LTS
  • yarn

If you have not developed Symbol apps before, I recommend you to complete Symbol's Getting Started section to create an account and send your first transaction to the network.

Download the boilerplate project

The project symbol-serverless-faucet contains the code to deploy the serverless blockchain app.

Deploy to Netlify

We will test the app first locally before deploying it to Netlify. If you want to deploy a custom faucet, jump to "Deploying the app" section.

1. Fork the repository dgarcia360/symbol-serverless-faucet.

2. Clone the forked repository in your local system.

git clone https://github.com/your-username/symbol-serverless-faucet.git

3. Enter the project and install yarn dependencies.

cd symbol-serverless-faucet yarn install

Developing a lambda function

First, let's define the serverless function. The file src/lamba/faucet.js holds the function the faucet is using.

import querystring from "querystring"; import { Account, Address, Deadline, EmptyMessage, Mosaic, MosaicId, TransactionHttp, TransferTransaction, UInt64 } from "nem2-sdk"; const {GENERATION_HASH, MOSAIC_DIVISIBILITY, MOSAIC_ID, NETWORK, NODE_URL, FAUCET_PRIVATE_KEY } = process.env; exports.handler = async (event, context) => { // Only allow POST if (event.httpMethod !== "POST") { return {statusCode: 405, body: "Method Not Allowed"}; } const params = querystring.parse(event.body); const recipientAddress = Address.createFromRawAddress(params.address); const amount = params.amount < 1000 ? UInt64.fromUint(params.amount * Math.pow(10, parseInt(MOSAIC_DIVISIBILITY))) : UInt64.fromUint(1000 * Math.pow(10, parseInt(MOSAIC_DIVISIBILITY))); const transferTransaction = TransferTransaction.create( Deadline.create(), recipientAddress, [new Mosaic(new MosaicId(MOSAIC_ID), amount)], EmptyMessage, parseInt(NETWORK), UInt64.fromUint(2000000)); const account = Account.createFromPrivateKey(FAUCET_PRIVATE_KEY, parseInt(NETWORK)); const signedTransaction = account.sign(transferTransaction, GENERATION_HASH); const transactionHttp = new TransactionHttp(NODE_URL, NETWORK); transactionHttp.announce(signedTransaction) .subscribe(x => console.log(x),err => console.log(err)); return { statusCode: 200, body: `Transaction announced: ${signedTransaction.hash}` }; };

As you can see, a lambda function is developed similarly to any other API. In this case, the function takes as the input amount and address. Then, the transaction is defined and signed using the faucet's account. Finally, the function returns an OK response if the transaction was successfully announced.

All the environment variables are defined in the .env file, except for the private key.

PropertyDescriptionSample Value
NODE_URLThe URL of the node your app will be connecting too.http://api-harvest-20.us-west-1.nemtech.network:3000
NETWORKThe network type. To retrive this value, open NODE_URL + '/node/info' in a new browser tab an copy the value networkIdentifier.152
GENERATION_HASHThe unique string that identifies the network. To retrieve the network generration hash seed, open NODE_URL + '/node/info' in a new browser tab and copy the networkGenerationHashSeed value.CC42A...385B
MOSAIC_IDThe mosaic id to distribute. To retrieve the currency mosaic id, open NODE_URL + '/network/properties' and copy the currencyMosaicId value.103E3F46B101D781
MOSAIC_DIVISIBILITYDetermines the decimal place to which the mosaic can be divided.6

Change the environment variables as desired to fit your network. Then, open a new terminal and set up the faucet's private key. Make sure this account has enough MOSAIC_ID funds to distribute between the users who request mosaics.

export FAUCET_PRIVATE_KEY="my value"

Calling the function

Now that we have defined the function, it's time to call it from the front-end. The file src/index.html defines the form used to make a POST request to the serverless function.

<form action="/.netlify/functions/faucet" method="POST"> <p> <label>What's your address?<br/><input name="address" type="text"></label> <label>How many units (max 1000)?<br/><input name="amount" type="number"></label> </p> <p> <button type="submit">Receive!</button> </p> </form>

Test the app locally by running npm run start. Then, open localhost:8080 in a new browser tab.

Deploying the app

Now that we have created and tested the faucet locally, we will use Netlify to host the app. If you don't have a Netlify account, sign up here first.

1. Create a new site from Git using this link.

2. Choose to deploy the site using your GitHub account.

3. Authorize Netlify.

4. Select the repo symbol-serverless-faucet.

5. Open the tab Advanced build settings and define the following environment variables:

Environment variables for Production

6. Click on the "Deploy Site" button— and we are done!

If everything goes well, Netlify will show you the site's URL after the app is deployed.

All together

In this article, we only have started to see what can be achieved by combining serverless functions and Symbol blockchain. Nevertheless, we can envision a wide range of promising possibilities. If you create a new serverless app using the boilerplate code, please do not hesitate to share the link in the comments!