Setup up a CRYPTO Balance widget on IOS with Node and scriptable
Tags: node
Published: 2021-05-06 06:00:46

---
title: Setup up a CRYPTO Balance widget on IOS with Node and scriptable
published: true
description: 
tags: node
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5jq6kcjxk7mcmbp1k651.jpg
---
### About me: https://kenchambers.dev

> I've been having so much fun with [Scriptable](https://scriptable.app/) which allows you to create IOS widgets using javascript code! I've also been delving into the crypto world for funsies, and I need a way to track my wallet addresses easily on my phone so i can obsess and check them all the time.


I plan on expanding this tutorial, depending on the feedback i get , I had to take alot of things out for the purposes of this tutorial since i needed a custom build for Coinmetro and blockfi, since their API interactions where a little more complicated.

If the feedback is good on this article, i'll open up my code for the chart as well!

Please note that your widget by the end of this will look like this:

![lesser](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/20nspjyrmp6p8320t4ux.png)

### Enjoy!
---
##### references:
https://devcenter.heroku.com/articles/getting-started-with-nodejs
https://devcenter.heroku.com/articles/deploying-nodejs
https://dev.to/matthri/create-your-own-ios-widget-with-javascript-5a11

##### Code:
https://github.com/nodefiend/scriptable-crypto-balance-widget

##### assumptions:
- Node.js and npm installed.

- you have heroku CLI and you are logged in, if not [click here](https://devcenter.heroku.com/articles/getting-started-with-nodejs#set-up)
---

## Setting up your repo:

To make things super easy, lets create a new repo on Github and clone it to our computer. 

![setup_github](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a6hvm4ae58ypuej2ec2r.png)


Now use this URL to clone it to your computer with any method you think is best.

![clonewars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/km93ri0f1q0g3nscx2hv.png)

now lets initialize the repo with npm: defaults should be fine

```
cd /scriptable-crypto-balance-widget
npm init
```

add this to package json, so we can specify version of node, and add the dependencies we will need:

`package.json`
```json
...
    "engines": {
    "node": "14.2.0"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "express": "^4.17.1"
  }
...
```

we need to specify what happens when npm start is run: (so also add this to `package.json`)

`package.json`
```json
...
"scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
 ...
```

Heres my final package JSON file:

`package.json`

```json
{
  "name": "scriptable-crypto-balance-widget",
  "version": "1.0.0",
  "description": "A scriptable widget for checking crypto wallet balance",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/nodefiend/scriptable-crypto-balance-widget.git"
  },
  "author": "",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/nodefiend/scriptable-crypto-balance-widget/issues"
  },
  "homepage": "https://github.com/nodefiend/scriptable-crypto-balance-widget#readme",
  "engines": {
    "node": "14.2.0"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "express": "^4.17.1"
  }
}

```

now that we have that all laced up, lets install our dependencies:

```
npm install

```
Now lets build our node server:


`index.js`
```javascript
const axios = require('axios')
const express = require('express')
const app = express()
const port = 5000

app.listen(process.env.PORT || port)

```

You should have a pre-generated `.gitignore`, but make sure it has at least these things, to prevent build artifacts from being deployed to heroku:

`.gitignore`
```
/node_modules
npm-debug.log
.DS_Store
/*.env

```

sweet, we should have 3 files in our git commit history:

index.js
package.json
package-lock.json

## Deploy to heroku

```
git add .
git commit -m 'first commit'

```

make sure your logged in before you run this next command:

```
heroku create crypto-balance-widget
git push heroku main
```

This will automatically deploy to heroku, and push to the `main` branch.

It will give you a public URL to hit our new heroku server, but we don't have anything up there yet so lets just add some code before we make any requests to it.

## Creating a route to return BTC Price

So for brevity, I have all this code in the same index.js file, but I would reccomend DRYing it up, or sticking it in a class or at least a separate file.

Lets start by creating our first route, a simple `GET /balance` endpoint in which our widget will be sending a request to:

```javascript
app.get('/balance', async function (req, res) {
  try {

  } catch (err) {


  }
})

```

inside of our try catch, we want to run two async requests that we have not written yet, these functions will gather the price of the BTC , and the amount inside of the crypto wallet.

Please note, if you wanted to get wallet prices of a different crypto, you would simply change the contents of these functions, to hit different APIS for different crypto networks.


```javascript
app.get('/balance', async function (req, res) {
  try {
  	    let [ walletBalance, btcPrice ] = await Promise.all([
      getWalletBalance(), getBTCPrice()
    ])

  } catch (err) {


  }
})

```

Now that we have the price and amount in the wallet, we simply multiply these together and send back a response to our request:

```javascript
...
    let balance = (walletBalance * btcPrice).toFixed(2)

    const response = {
      statusCode: 200,
      body: balance
    }
    res.send(response)

...
```

And if there is an error, lets catch it and also return a response:

```javascript
...
  } catch (err) {
    const response = {
      statusCode: 500,
      body: err
    }
    res.send(response)
  }
})
...

```

Heres what our request looks like in completion:

```javascript
app.get('/balance', async function (req, res) {
  try {
    let [ walletBalance, btcPrice ] = await Promise.all([
      getBTCWallet(), getBTCPrice()
    ])

    let balance = (walletBalance * btcPrice).toFixed(2)

    const response = {
      statusCode: 200,
      body: balance
    }
    res.send(response)
  } catch (err) {
    const response = {
      statusCode: 500,
      body: err
    }
    res.send(response)
  }
})


```

Alright, lets write `getWalletBalance()` and `getBTCPrice()` so that we can use them in the above function:


This async function will hit testnet-api to retrieve the current price of bitcoin. If you know a different API you could replace the URL here, just make sure to update the parsing of the response, since the JSON data will be shaped differently.

```javascript
async function getBTCPrice() {
  try {
    let response = await axios({
      method: 'get',
      url: 'https://testnet-api.smartbit.com.au/v1/exchange-rates'
    })
    let price = response.data['exchange_rates'].filter(function(rate){ return rate['code'] == 'USD'})
    return price[0]['rate']
  } catch (e) {
    console.log(e)
  }
}
```

Next we will write our function to retrieve the balance of a existing crypto wallet. The same applies for this function, we can update the API we are using simply by switching out `smartbitURL` or we can update the wallet address simply by switching out the `wallet` variable. If you do switch out the API, make sure to update the response, as it will most likely be shaped differently.

Since the wallet balance came back as a string, I turned it into a number so we can easily multiply it by the current price of bitcoin.

```javascript

async function getBTCWallet(){
  let wallet = '3P3QsMVK89JBNqZQv5zMAKG8FK3kJM4rjt'
  let smartbitURL = 'https://api.smartbit.com.au/v1/blockchain/address/' + wallet

  try {
    let response = await axios({
      method: 'get',
      url: smartbitURL
    })

    let walletBalance = parseFloat(response.data['address']['total']['balance'])
    return walletBalance
  } catch (e) {
	   console.log(e)
  }
}
```


All together now, our index.js should look like this

```javascript
const axios = require('axios')
const express = require('express')
const app = express()
const port = 5000

async function getBTCPrice() {
  try {
    let response = await axios({
      method: 'get',
      url: 'https://testnet-api.smartbit.com.au/v1/exchange-rates'
    })
    let price = response.data['exchange_rates'].filter(function(rate){ return rate['code'] == 'USD'})
    return price[0]['rate']
  } catch (e) {
    console.log(e)
  }
}

async function getBTCWallet(){
  let wallet = '3P3QsMVK89JBNqZQv5zMAKG8FK3kJM4rjt'
  let smartbitURL = 'https://api.smartbit.com.au/v1/blockchain/address/' + wallet

  try {
    let response = await axios({
      method: 'get',
      url: smartbitURL
    })

    let walletBalance = parseFloat(response.data['address']['total']['balance'])
    return walletBalance
  } catch (e) {
	   console.log(e)
  }
}

app.get('/balance', async function (req, res) {
  try {
    let [ walletBalance, btcPrice ] = await Promise.all([
      getBTCWallet(), getBTCPrice()
    ])

    let balance = (walletBalance * btcPrice).toFixed(2)

    const response = {
      statusCode: 200,
      body: balance
    }
    res.send(response)
  } catch (err) {
    const response = {
      statusCode: 500,
      body: err
    }
    res.send(response)
  }
})

console.log("App is running on ", port);

app.listen(process.env.PORT || port)


```

Lets commit our changes now to heroku:

`git heroku push main`

now that our changes are up , we should be able to contact our server via our scriptable widget:

## Scriptable Widget: 

Scriptable is an app that we can download off the app store. 

you can set up the app to run different scripts, since this article is more about the code aspect, I wont cover how to set up scriptable and run a script, you can decern that from this article [here](https://dev.to/matthri/create-your-own-ios-widget-with-javascript-5a11) 

This is a great article because it covers how to send async requests.

first lets write the function that will create the widget:


```javascript
let widget = await createWidget()
if (config.runsInWidget) {
  Script.setWidget(widget)
} else {
  widget.presentMedium()
}
Script.complete()
```

Now lets build the meat and potatoes, `createWidget()`

```javascript
async function createWidget() {
  // declare widget  	
  let w = new ListWidget()
  // call async request to fetch wallet amount
  let { balance } = await fetchBitcoinWalletAmount()
  //background color
  w.backgroundColor = new Color("#000000")
  // **************************************
  //header icon
  let docsSymbol = SFSymbol.named("bitcoinsign.square")
  let bitcoinIconImage = w.addImage(docsSymbol.image)
  bitcoinIconImage.rightAlignImage()
  bitcoinIconImage.imageSize = new Size(25, 25)
  bitcoinIconImage.tintColor = Color.green()
  bitcoinIconImage.imageOpacity = 0.8
  bitcoinIconImage.url = "https://www.google.com"
  // **************************************
  // MAIN CONTAINER
  let mainContainerStack = w.addStack()
  // TOP CONTAINER
  let leftContainerStack = mainContainerStack.addStack()
  leftContainerStack.layoutVertically()
  let rightContainerStack = mainContainerStack.addStack()
  rightContainerStack.layoutVertically()
  // TOP LEFT STACK:
  // **************************************
  // Large Bal
  let largeFont = Font.largeTitle(20)
  const largeBalanceStack = leftContainerStack.addStack()
  const largeBalance = largeBalanceStack.addText('$' + (balance).toString())
  largeBalance.font = largeFont
  largeBalance.textColor = new Color('#ffffff')

  // **************************************
  //refresh widget automatically
  let nextRefresh = Date.now() + 1000
  w.refreshAfterDate = new Date(nextRefresh)
  showGradientBackground(w)
  return w
}

```

lets write our function for applying a gradient background.

```javascript

function showGradientBackground(widget) {
  let gradient = new LinearGradient()
  gradient.colors = [new Color("#0a0a0a"), new Color("#141414"), new Color("#1f1f1f")]
  gradient.locations = [0,0.8,1]
  widget.backgroundGradient = gradient
}
```


Now that we have the widget set up, lets build our `fetchBitcoinWalletAmount()` function.
This will be composed of two async functions, of course you can format it a number of different ways depending on your code style, but because this is a watered down version of my actual widget, its broken into two functions.

```javascript
async function getBalance(){
  let BTCUrl = 'http://localhost:5000/balance'
  let request = new Request(BTCUrl)
  request.method = "get";
  let response = await request.loadJSON()
  return response.body
}
// fetch bitcoin wallet amount
async function fetchBitcoinWalletAmount(){
  let btcBalanceAmount = await getBalance()
  return { balance: btcBalanceAmount }
}
```


Now all together, here is our scriptable.js file- it can be found in the code repo as well. 

A good way to trouble shoot this function , and if you want to code on your computer instead of your phone, use this download:

https://scriptable.app/mac-beta/

`scriptable.js`
```javascript
// ************************************
// execute widget
let widget = await createWidget()
if (config.runsInWidget) {
  Script.setWidget(widget)
} else {
  widget.presentMedium()
}
Script.complete()
// ************************************
async function createWidget() {
  // declare widget  	
  let w = new ListWidget()
  // call async request to fetch wallet amount
  let { balance } = await fetchBitcoinWalletAmount()
  //background color
  w.backgroundColor = new Color("#000000")
  // **************************************
  //header icon
  let docsSymbol = SFSymbol.named("bitcoinsign.square")
  let bitcoinIconImage = w.addImage(docsSymbol.image)
  bitcoinIconImage.rightAlignImage()
  bitcoinIconImage.imageSize = new Size(25, 25)
  bitcoinIconImage.tintColor = Color.green()
  bitcoinIconImage.imageOpacity = 0.8
  bitcoinIconImage.url = "https://www.google.com"
  // **************************************
  // MAIN CONTAINER
  let mainContainerStack = w.addStack()
  // TOP CONTAINER
  let leftContainerStack = mainContainerStack.addStack()
  leftContainerStack.layoutVertically()
  let rightContainerStack = mainContainerStack.addStack()
  rightContainerStack.layoutVertically()
  // TOP LEFT STACK:
  // **************************************
  // Large Bal
  let largeFont = Font.largeTitle(20)
  const largeBalanceStack = leftContainerStack.addStack()
  const largeBalance = largeBalanceStack.addText('$' + (balance).toString())
  largeBalance.font = largeFont
  largeBalance.textColor = new Color('#ffffff')

  // **************************************
  //refresh widget automatically
  let nextRefresh = Date.now() + 1000
  w.refreshAfterDate = new Date(nextRefresh)
  // add gradient to widget
  showGradientBackground(w)
  return w
}
function showGradientBackground(widget) {
  let gradient = new LinearGradient()
  gradient.colors = [new Color("#0a0a0a"), new Color("#141414"), new Color("#1f1f1f")]
  gradient.locations = [0,0.8,1]
  widget.backgroundGradient = gradient
}

async function getBalance(){
  let BTCUrl = 'http://localhost:5000/balance'
  let request = new Request(BTCUrl)
  request.method = "get";
  let response = await request.loadJSON()
  return response.body
}
// fetch bitcoin wallet amount
async function fetchBitcoinWalletAmount(){
  let btcBalanceAmount = await getBalance()
  return { balance: btcBalanceAmount }
}

```

and Voaila! we have our crypto balance in an IOS app.

to push your code to heroku, use :

`git push heroku [branch]`

and then get the URL of your app from the heroku dashboard and plug it into our scriptable.js file on the widget in place of: `localhost:5000`

I plan to write more on to include a graph that will display something, maybe a history of your balances? or maybe the current price of crypto? let me know down below in the comments.

This is kind of a huge tutorial, so if you have any issues with it, leave me a message in the comments.

Or if you want to fight me, cause my code is so deplorable- please let me know as well.