1. var bob = new Person();
2. bob.sayHello();
3. bob.wave();
##################################################################################################################

Bincoin and building with Azure Static Web Apps

The appalling resource waste of Bitcoin and how Azure Static Web Apps made it easy for me to build and deploy a website to raise awareness of it in a couple of sessions

##################################################################################################################
4. var cup = new Coffee();
5. bob.drink(cup);
6. var cupTwo = new Coffee();

Bitcoin has a massive electricity footprint - that is relatively well known. Every few months a stat comes out about it using the same amount of currency as some country - this is shocking but it does lack any kind of real world comparison. Does anyone other than senior government leaders and energy analysts know what a particular countries energy usage is? What is a Terawatt Hour?

I had an idea for a new site, Bincoin. It would show similar stats, but instead of focusing on the unimaginably huge it would focus on the resources wasted in one transaction and relay them in the form of real world comparison’s. It would also move the focus from just electricity usage (which can be slightly conceptual) to the more hot button topics of CO2 emissions and electronic waste.

For the data for this project I turned to the Digiconomist’s website that shows per transaction figures for all of the above points. The Digiconomist doesn’t provide any API, but their website is well structured and therefore is a decent candidate for scraping. I have contacted Digiconomist about this and credited them on the site - I haven’t received a response as yet.

But how to build and deploy this application? What’s the easiest way? Previously I would have used my ARM template that combines together an Azure Function, a storage account and a CDN. But now there is a shiny new way, that has the added bonus of being free - Azure Static Web Apps.

Azure Static Web Apps make it super easy to get a static website (i.e. raw html, or a client side app like React, Vue or Angular) deployed to the cloud, and even includes a HTTP-only Azure Function to let you add an API. It maps the Azure Function to the same domain so you don’t have to deal with CORS grief, it supports AAD, Google auth etc and it’s very easy to map a custom domain. All with no ARM template, and cool stuff like automatic deployment of branches so you can test them on the cloud. I’m also hearing strong hints at it keeping its current pricing strategy of being totally free for the future, I presume with some limits.

I’m not going to go into the details of how to get started making one of these - the Microsoft documentation is going to be much better at doing that. But I will talk you through how exactly I built Bincoin on Azure Static Web Apps.

The App

azure static web app receiving data from azure function which retrieves its data from the digiconomist website and stores it in a table.
Bincoin architecture

Bincoin consists of a React application, which calls 2 Node.js Azure Function API’s. One of these API’s retrieves the latest per transaction resource usage from the digiconomist website and returns it. It uses Cheerio, a library that lets you use JQuery syntax to scan through a webpage and grab data out of it. It then caches that data in Azure Table Storage for a day - any future requests that day do not trigger a request to the digiconomist website, instead they retrieve the information from the table and display that instead. The code is as follows, I’m using an inbound table storage binding to get the object from the table:

import { AzureFunction, Context, HttpRequest } from "@azure/functions"
import axios from "axios";
import * as cheerio from 'cheerio';
import { BitcoinResourceUsage } from "../src/models/BitcoinResourceUsage";
import * as storage from "azure-storage";

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest, resourceUsage: BitcoinResourceUsage): Promise<void> {
    let responseMessage = JSON.stringify(resourceUsage)
    let now = new Date();
    let differenceInMilliseconds = now.getTime() - new Date(resourceUsage.dateTime).getTime();

    // To calculate the no. of days between two dates 
    var differenceInDays = differenceInMilliseconds / (1000 \* 3600 \* 24);
    if (differenceInDays > 1) {
        context.log("Refreshing")
        const storageClient = storage.createTableService(process.env\["MyStorageConnectionAppSetting"]);
        const tableName = "ResourceUsage"
        let responseMessage = ""
        const response = await axios.get('https://digiconomist.net/bitcoin-energy-consumption');
        const $ = await cheerio.load(response.data);
        let carbonText = $('b:contains("kgCO2")').text().replace(" kgCO2", "")
        let electricityText = $('b:contains("kWh")').text().replace(" kWh", "")
        let ewasteText = $('b:contains("grams")').text().replace(" grams", "")
        let bitcoinResourceUsageData = new BitcoinResourceUsage()
        bitcoinResourceUsageData.carbonDioxideInKg = Number.parseFloat(carbonText)
        bitcoinResourceUsageData.electricityUsageInkWh = Number.parseFloat(electricityText)
        bitcoinResourceUsageData.eWasteInGrams = Number.parseFloat(ewasteText)
        bitcoinResourceUsageData.dateTime = new Date()
        bitcoinResourceUsageData.ETag = "*"
        bitcoinResourceUsageData.RowKey = "1"
        bitcoinResourceUsageData.PartitionKey = "1"
        responseMessage = JSON.stringify(bitcoinResourceUsageData)
        context.res = {
            // status: 200, /\* Defaults to 200 \*/
            body: responseMessage
        };
        storageClient.insertOrMergeEntity(tableName, bitcoinResourceUsageData, function (error, result, response) {
            context.done()
        })
    } else {
    context.res = {
        // status: 200, /\* Defaults to 200 \*/
        body: responseMessage
    };
    context.done();
    }
};

export default httpTrigger;

This powers the overall figures, but how do I generate the individual comparisons? This varies between the types of comparison’s. Some of them, like the portions of chicken, the payload into space and the life support machine, are worked out by calculating the amount of resources needed for 1 unit of the comparison - the real resource usage is then plugged in and the largest whole number that fits is returned. The other ones, like the flights and Tesla journeys are unfortunately more tricky to do in that way. What I do with those is I hard code in example journeys and their total resource usage and then the API filters out any that have a higher resource usage than really happened that day - if anyone has any suggestions to make that better then I’d appreciate them!

Conclusion

Bincoin will hopefully go on to become a useful resource in highlighting the resource waste of bitcoin and the pressure to fix it’s sustainability problem. It’s also been a great opportunity for me to put Azure Static Web Apps through it’s paces and I have to say I think it’s a great tool for introducing developers to Azure. You can build any static website and get it on the internet in minutes, without understanding the rest of Azure at all. When you want to add a backend to the website, using a Node.js Azure Function isn’t too hard, particularly if you convert an existing express app to it. It’s also a powerful tool for more experienced Azure developers, but there are some rough edges on the experience - the main one being the lack of any sort of ARM support. A CLI is being worked on though, so hopefully that will make the CI/CD story better. The other suggestion I’d have would be some sort of inbuilt database - if you want to get new developers on to Azure then making them face the array of database options isn’t ideal. Integrating Serverless CosmosDB (perhaps in Mongo mode if targeting Node developers) or Table Storage would make it a more compelling offering and an easier onramp.

© 2022 - Built by Daniel Bass