Building a Substrate Address Translation Filter with VueJS

Substrate based chains use prefixes for account address encoding, meaning formats of account addresses can vary dramatically between chains. Let's build a plug-and-play filter to translate from one address to another!

If you haven't already, boot up a copy of Polkadash first as per this guide.

Skill level prerequisites:

Background

With Substrate, each chain has an encoding prefix. This prefix is used when encoding a user's public key to create an address. Through different prefixes, different chains have different address formats so they're easy to tell apart.

For example, the Polkadot address 1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg matches the Kusama address CpjsLDC1JFyrhm3ftC9Gs4QoyrkHKhZKtK7YqGTRFtTafgp. For more info on accounts, prefixes, and how it all works, please see the Accounts wiki page.

Adding Crypto Utilities

The functionality to encode and decode Substrate addresses can be found in the crypto utilities package from the PolkadotJS family. Let's install it.

yarn add @polkadot/util-crypto

We can now call on this package in any component with const util = require("@polkadot/util-crypto");. We could add this to connection.js and make it available in every component, but that makes our components less portable. We'll call it when we need it instead.

Adding a Filter

Filters in VueJS let you change strings during render-time, in templates. This is how we'll use our filter:

My address: {{ address }}
My address on Polkadot: {{ address | prefix("polkadot") }}

We'll put the filter's code into src/components/Filters/prefix.js:

import Vue from "vue";

Vue.filter("prefix", (value, prefix) => {
  return value.toUpperCase();
});

In our application's src/main.js we'll add the line:

import "@/components/Filters/prefix.js";

This activated our filter globally - it will be available across the entire app. You can also have per-component filters which can be more lightweight. This is recommended when components are very context specific. The official docs on filters will have more information about that.

Let's make sure this works before we dive into crypto logic.

Creating a New Component

Create Addresses.vue in src\pages\ with the following boilerplate code:

<template>
  <div class="content">
    <card>
      <template slot="header">
        <h5 class="title">Addresses</h5>
        <p class="category">A utility for converting between address formats</p>
				<p>Original address: {{ address }}</p>
				<p>Filtered address: {{ address | prefix("polkadot") }}</p>
      </template>
    </card>
  </div>
</template>
<script>
import { Card } from "@/components/index";

export default {
  components: {
    Card,
  },
  data() {
    return {
      address: "CpjsLDC1JFyrhm3ftC9Gs4QoyrkHKhZKtK7YqGTRFtTafgp",
    };
  },
};
</script>

Then add it to the sidebar as an option by adding a new sidebar-link into DashboardLayout.vue:

      <sidebar-link to="/addresses">
        <i class="tim-icons icon-book-bookmark"></i>
        <p>Addresses</p>
      </sidebar-link>

and a new route into router.js:

      {
        path: "Addresses",
        name: "Addresses",
        component: () => import("@/pages/Addresses.vue"),
      },

Our mock filter implementation should already have an effect - it should turn the address into uppercase letters.

Converting Address Formats

Finally, let's finish the filter.

Let's start by defining the prefixes the filter can support. Let's add the list to prefix.js after the initial import statement:

const prefixes = {
    "polkadot": 0,
    "kusama": 2,
    "plasm": 5,
    "bifrost": 6,
    "edgeware": 7,
    "karura": 8,
    "reynolds": 9,
    "acala": 10,
    "laminar": 11,
"substratee": 13, "kulupu": 16, "darwinia": 18, "robonomics": 32, "centrifuge": 36, "substrate": 42,
"chainx": 44 }

Tip: these prefixes were taken from the crypto primitives inside of Substrate.

The user should be able to define any numeric prefix - these names are just for convenience. By not enforcing a strict rule to use these prefixes specifically, we make our filter future-proof. Our filter's final code is:

import Vue from "vue";

const util = require("@polkadot/util-crypto");

const prefixes = {
  polkadot: 0,
  kusama: 2,
  plasm: 5,
  bifrost: 6,
  edgeware: 7,
  karura: 8,
  reynolds: 9,
  acala: 10,
  laminar: 11,
substratee: 13, kulupu: 16, darwinia: 18, robonomics: 32, centrifuge: 36, substrate: 42,
chainx: 44 }; Vue.filter("prefix", (value, prefix) => { if (typeof prefix !== "number") { prefix = prefixes[prefix] || 42; } let decoded = util.decodeAddress(value); return util.encodeAddress(value, prefix); });

We import the crypto utilities into a util constant. This constant will contain references to functions such as encodeAddress and decodeAddress. We define the prefixes, and then we define our function - it takes two arguments: the value which is whatever is being piped in the template, and the prefix which we supply in parentheses when using the filter.

We then check if the prefix is a number, to allow the user to use any number with the filter, avoiding the need for hardcoded chain names. If the prefix is not a number, we either get a number from our list of prefixes, or set it to 42 if invalid, which returns a standard Substrate address.

Finally, we decode the original address into its raw format, and then re-encode it with the prefix we got. Any value we return will be what's rendered on screen.

Putting it all Together

Let's output a single address in all these formats at once by modifying our Addresses component slightly to the following final code:

<template>
  <div class="content">
    <card>
      <template slot="header">
        <h5 class="title">Addresses</h5>
        <p class="category">A utility for converting between address formats</p>
        <p>Original address: {{ address }}</p>
        <hr />
        <p v-for="(prefix, name) in prefixes">
          Address on {{ name }}: {{ address | prefix(prefix) }}
        </p>
      </template>
    </card>
  </div>
</template>
<script>
import { Card } from "@/components/index";

export default {
  components: {
    Card,
  },
  data() {
    return {
      address: "CpjsLDC1JFyrhm3ftC9Gs4QoyrkHKhZKtK7YqGTRFtTafgp",
      prefixes: {
        polkadot: 0,
        kusama: 2,
        plasm: 5,
        bifrost: 6,
        edgeware: 7,
        karura: 8,
        reynolds: 9,
        acala: 10,
        laminar: 11,
substratee: 13, kulupu: 16, darwinia: 18, robonomics: 32, centrifuge: 36, substrate: 42,
chainx: 44 }, }; }, }; </script>

The output we get should be similar to the image below:

The full code of this tutorial can be found in the tutorial-filter branch of Polkadash.


Remember to subscribe to our newsletter to be kept up to date on new posts and to be kept in the loop about Web 3.0 developments.

Related posts