Axie Infinity Tutorial: Paint my Axies, vanilla JS

in #javascript5 years ago (edited)

It is first article from series which will teach you how to do basic interaction with Ethereum Blockchain via web3 library along with using assets provided by AxieInfinity.

If you played Flappy Axie or Axie Sushi and want to create your own mini-game or tool in this universe it is right place to be!

1.png

We will start with simple task:

  • retrieve Ethereum address of user, who is connected with Metamask/Mist wallet
  • request Axie Infinity API for list of Axies
  • paint static images of Axies

Before we start take note that Metamask, which is most popular Ethereum wallet, has problems with exposing user accounts if we are requesting them on file://.

Due to browser security restrictions, we can’t communicate with dapps running on file://. Please use a local server for development.

To address this problem, you can serve your index.html file with simple Apache server like XAMPP on your localhost or other services.

Project boilerplate

We will start our tutorial with 3 empty files:

css/
  main.css
js/
  app.js
index.html

In our index.html we can already put all of our markup which will be needed to completing this tutorial:

<!DOCTYPE html>
<html>
<head>
 <title>Axie Tutorial</title>
 <link rel=”stylesheet” href=”./css/main.css”>
</head>
<body>
 <div class=”container”>
  <p id=”eth-address”></p>
  <span id=”loader”>Requesting axies</span>
  <div id=”axies”></div>
 </div>
 <script src=”https://cdn.jsdelivr.net/gh/ethereum/[email protected] beta.34/dist/web3.min.js"></script>
 <script src=”https://unpkg.com/axios/dist/axios.min.js"></script>
 <script type=”text/javascript” src=”./js/app.js”></script>
</body>
</html>

Also our main.css is very small so we can fill it also:

#axies {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}
.axie {
  max-width: 250px;
}
#loader {
  display: none;
}
#loader.visible {
  display: inline-block;
}

We prepared small HTML markup for our Axies, ETH address and dummy loader, imported our main.css and app.js files. On top of that we are importing two libraries:

  • web3, a collection of libraries, which allow us to interact with Ethereum node,
  • axios, promise based HTTP client which will help us with our requests to API

Retrieve Ethereum address of user

Now head over to app.js file. Snippet below is responsible for retrieving our etherum node provider.

const API_KEY = 'your api key for infura';
window.addEventListener("load", async function() {
if (window.ethereum) {
    // new privacy mode
    window.web3 = new Web3(ethereum);
    try {
      // Request account access if needed
      await ethereum.enable();
    } catch (error) {
      console.log(error);
    }
  } else if (window.web3) {
    // old way of asking for web3
    window.web3 = new Web3(web3.currentProvider);
  } else {
    // connect to custom provider, like Infura if there is no wallet detected
    window.web3 = new Web3(new Web3.providers.HttpProvider('https://mainnet.infura.io/' + API_KEY)); 
  }
});

As of November Metamask added privacy mode, and by default is not exposing user accounts to App. Before user can interact with it, he has to accept granting permissions to read addresses:

permission.png

However some users don’t use this mode and they accounts will be exposed by default. In situation where user doesn’t have some wallet installed, but to interact with our App we need some web3 methods, there is option to inject external provider, like Infura. Generating API Key is free, so if you think that you can need such option feel free to check it out!

We are also using async/await syntax as asking for this permission is asynchronous task. User can grant it within 3 seconds, or maybe almost immediately, we don’t know. Thanks to this syntax our code is more readable and feels more synchronous. We will also use it in some of our requests to API, which are also asynchronous.

After this step we can finally request accounts with getAccounts method, we can set interval for this function to see if account of user has changed:

window.addEventListener("load", async function() {
 ...
 setInterval(requestAccount, 1000);
});
function requestAccount() {
  web3.eth.getAccounts(function(error, result) {
   console.log(result);
 })
}

If everything is working you should see your account address in developers console:

addressConsole.png

Next step will be to assign this address to variable and display it in our span. We will also include logic to change value of this variable if new address will be exposed from getAccounts function.

On top of file you can add global variable for our address, and also refactor requestAccount function:

let address = null;
...
function requestAccount() {
  web3.eth.getAccounts(function(error, result) {
    // only update if address changed or on init
    if (address === null || address !== result[0]) {
      address = result[0];
      if (address === undefined) {
        document.getElementById('eth-address').innerHTML = "No account";
      } else {
        document.getElementById('eth-address').innerHTML = result[0];
      }
}

Now in you web browser window you should see your address in span:

address.png

Request Axie Infinity API for Axies list

We can move forward to requesting list of our Axies from API. Unfortunately there is no good documentation of it, so to find out about more sophisticated parameters for requests you will need to head to Network tab on Axie Infinity and search for proper ones or you can alson ping me or any other community developer on Axie Discord for help (Clumsier#1657)! :)

Requesting Axie List will only take place for newly exposed address. To deal with it, we will invoke function only when address changed and is not undefined. You can find this fragment in your code and add one line:

if (address === undefined) {
  document.getElementById('eth-address').innerHTML = "No account";
} else {
  requestAxies();
  document.getElementById('eth-address').innerHTML = result[0];
}

Function for requesting will look like that:

function requestAxies() {
  const BASE_URL = 'https://axieinfinity.com/api/';
  const URL = BASE_URL + 'addresses/' + address + '/axies?offset=0&stage=4';
document.getElementById('loader').classList.add('visible');
axios.get(URL)
    .then(function(response) {
      console.log(response);
      document.getElementById('loader').classList.remove('visible');
    })
    .catch(function(error) {
      console.log(error);
    })
}

We are toggling our loader and requesting Axies for given address with parameters:

  • offset, this API request is returning detail of up to 12 Axies from list. If there are more Axies, treat this parameter as a page in paginated data.
  • stage, life stage for axie, 1 for Egg, 2 for Larva, 3 for Petite and 4 for Adult,

sampleData.png

Unfortunately object containing Axie data doesn’t contain static idle img, for requesting that we need to make request to another API route. This API work in progress so keep in mind different path. So, for our Array of Axies we can loop through them and request proper image and then append this to proper container, let’s do it in paintAxies function and run it after we are done requesting our list of Axies. After hiding our loader, add this line of code:

document.getElementById('loader').classList.remove('visible');
paintAxies(response.data);

Then we can finally request static image from API and append them to container.

async function paintAxies(axiesData) {
  const BASE_URL = 'https://api.axieinfinity.com/v1/';
  const axiesContainer = document.getElementById('axies');
  const axies = axiesData.axies;
  axiesContainer.innerHTML = '';
for (let i = 0; i < axies.length; i += 1) {
    const axie = axies[i];
await axios.get(BASE_URL + 'figure/' + axie.id)
      .then(function(response) {
        const staticImage = response.data.images.static.idle;
        const HTML = '<img id="' + axie.id + '" class="axie" src="' + staticImage + '" alt="" />';
        axiesContainer.insertAdjacentHTML('beforeend', HTML);
      })
      .catch(function(error) {
        console.log(error);
      })
  }
}

Now you should see you Axies rendered in browser!

Axies.png

tenor.gif

Summary

It wasn’t that hard, was it? Part which can be most confusing is retrieving used address of Ethereum wallet, but after that is is just common interaction with API and consuming data from response.

Whole code is available in this repository: https://github.com/pacxiu/AxieTutorialVanillaJS

In next tutorials we will use PIXI.js to animate our Axies! Also there will be mirror series where we will be using React as framework.

If you are curious about Axies, don’t forget to visit their official website and Discord!

Sort:  

This post has received a 55.79% upvote from @lovejuice thanks to @pacxiu. They love you, so does Aggroed. Please be sure to vote for Witnesses at https://steemit.com/~witnesses.

Congratulations @pacxiu! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You received more than 500 upvotes. Your next target is to reach 1000 upvotes.

Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word STOP

Support SteemitBoard's project! Vote for its witness and get one more award!

Hello @pacxiu! This is a friendly reminder that you have 3000 Partiko Points unclaimed in your Partiko account!

Partiko is a fast and beautiful mobile app for Steem, and it’s the most popular Steem mobile app out there! Download Partiko using the link below and login using SteemConnect to claim your 3000 Partiko points! You can easily convert them into Steem token!

https://partiko.app/referral/partiko

Coin Marketplace

STEEM 0.24
TRX 0.11
JST 0.031
BTC 60936.15
ETH 2921.43
USDT 1.00
SBD 3.70