Cover photo

Create a discord bot for managing an Axie Inifinity guild

Managing a gaming guild with many scholars can be a challenging operational task given that we need to manage a lot of valuable assets (tokens and axies) and at the same time worry about security of our private keys which makes it impossible to give access to collaborators without delegating control of the assets.

We are going to be interacting with RONIN blockchain through the Web3js library. We are going to create a discord bot that interprets wallets via their aliases and then verifies discord user’s privileges to run different commands like claims, transfers and reading information.

Checkout the GitHub repo

What we will learn:

  • Set up discord bot

  • Obtain SLP balance

  • Get information about a scholar

  • Transfer axies

  • Transfer tokens

  • Claim SLP

1- Set up application

Get discord application token

First we need to create a discord application for interacting with our bot. We can read discord documentation. After that we can get out TOKEN what we will be using for running our bot on localhost

post image

After that we need to create a /Data/config.json file inside the Data folder of the repo and inside it copy our token. You cannot share this token with anyone so protect it and that’s why I hide it in the .gitignore so you can’t commit it to git.

{
    "token": "DISCORD_TOKEN",
    "prefix": "!"
}

Find the contract ABI’s

For managing different assets in the ronin blockchain we are going to need 2 ABI. One for the tokens and one for the Axies. There may be others for land or other assets but not needed in the context of this tutorial.

Ronin is an Ethereum sidechain, it’s EVM compatible and uses the same standard ERC20 for its tokens. We ERC20 ABI to manage AXS, SLP, WETH or RON. For managing AXIES, the contract is not fully compliant with the ERC721 standard. This is because it was deployed before the standard had become finalized.

post image

Still we can get the ABI which we can get from the contract creator and still has all the needed methods. This ABI’s are already in the repo witch filenames axie_abi.json and token_abi.json

Setting up wallet aliases

First we need to create a secrets.json file containing all our private keys, each one with its own descriptive alias. This alias may be a number or a descriptive name we use in our wallets. For example main wallet could be were we make the breeding of axies or were we store high value assets.

DO NOT SHARE THIS PRIVATE KEYS EVER.

{
    "breed_old": "PRIVATE KEY",
    "buenos": "PRIVATE KEY",
    "main":"PRIVATE KEY",
    "acc2": "PRIVATE KEY",
    "acc_admin": "PRIVATE KEY",
    "acc_tri": "PRIVATE KEY",
    "account1": "PRIVATE KEY",
    "account2": "PRIVATE KEY",
    "account3": "PRIVATE KEY",
    "account69": "PRIVATE KEY"   
}

Once we have our secrets file ready we can star writing and running commands by only mentioning this aliases instead of the public keys. For example for transferring certain axie from one account to another should be very easy!

async getWalletByAlias(alias){
    let from_private = secrets[alias]   
    const web3 = await new Web3(new Web3.providers.HttpProvider(RONIN_PROVIDER_FREE));
    if(from_private){
        const signer = web3.eth.accounts.privateKeyToAccount(from_private)
        let wallet=signer.address//.replace('0x','ronin:')
        return wallet
    }
    return false
}

!transfer axie_123 account1 account2

Set up roles

Through discord, we are going to allow other collaborators of the guild to manage Ronin assets. We only need to protect the private keys by uploading them to a private server. After these are done we can allow certain roles in discord to run certain commands.

For the example we are going to create the Manager role in discord and then get the ID on Server settings → Roles

post image

Then we can call a method that verifies the Role of the incoming message to see if allows the command to execute or not.

 isManager:function(message){
        if(message.author.bot)return true
        let r1=message.guild.roles.cache.find(r => r.name === "Manager")
        //we can also filter with channel names using message.channel.name
        if(r1 && message.member.roles.cache.has(r1.id))return true
        return false
}

2- Obtain SLP balance

Let’s start with the commands. We are going to start simple by checking the token balances of a wallet, for example of main wallet.

post image

We first need to identify which token or assets we need to check the balance of and then identify the contract address

const web3 = await new Web3(new Web3.providers.HttpProvider(RONIN_PROVIDER));
from_acc=await this.getWalletByAlias(from_acc)

let contract_add=''
if(token == 'slp') contract_add = SLP_CONTRACT
else if(token == 'axs') contract_add = AXS_CONTRACT
else if(token == "axies")contract_add = AXIE_CONTRACT
else if(token == "weth")contract_add = WETH_CONTRACT
else  if(token == "ron") return await web3.eth.getBalance(from_acc)
else return message.channel.send("Message send")

let contract = new web3.eth.Contract(token_abi,web3.utils.toChecksumAddress(contract_add))
let balance = await  contract.methods.balanceOf( web3.utils.toChecksumAddress(from_acc)).call()

4- Get information about a scholar

With the command report we can get information about SLP, MMR, list the Axies and much more.

post image

For this to work we need to check out Axie Infinity API’s which we can found on their discord server.

getSLP:async function(from_acc,message=null,cache=false){
    try{
        let data={}
        let url = "https://game-api.axie.technology/api/v2/"+from_acc.replace('0x','ronin:')  ;
        data= await fetch(url, { method: "Get" }).then(res => res.json()).then((json) => { return json});
        return data

    }catch(e){
        this.log("ERROR: "+e.message,message)
    }
}

Also using GraphQL we can get the information about the particular Axies inside the wallet

getAxiesIds:async function (wallet){
    if(!wallet)return
    wallet=wallet.replace('ronin:','0x')
    let url = `https://graphql-gateway.axieinfinity.com/graphql`;
    let query = `
    {
        "operationName": "GetAxieBriefList",
        "variables": {
                "owner":"${wallet}"
        },
        "query": "query GetAxieBriefList($auctionType: AuctionType, $criteria: AxieSearchCriteria, $from: Int, $sort: SortBy, $size: Int, $owner: String) {  axies(auctionType: $auctionType, criteria: $criteria, from: $from, sort: $sort, size: $size, owner: $owner) {    total    results {      ...AxieBrief      __typename    }    __typename  }}fragment AxieBrief on Axie {  id  name  stage  class  breedCount  image  title  battleInfo {    banned    __typename  }  auction {    currentPrice    currentPriceUSD    __typename  }  parts {    id    name    class    type    specialGenes    __typename  }  __typename}"
    }`

    let response=await fetch(url, { method: 'post',headers: { 'Content-Type': 'application/json'},body: JSON.stringify(JSON.parse(query))}).then(response => response.json()).then(data => { return data});
    if(!response || !response.data || !response.data.axies)return null
    let dev= {count:response.data.axies.total,axies:response.data.axies.results}
    return dev

}

4- Transfer axies

Now we are going to start with riskier commands like transfers. It is very important that we understand the security risks and implications of allowing this type of commands. For security, we are going to use 2 filters.

Using each discord message object we are going to check if corresponds with the Manager role we defined before. If not, the command will return error.

if(!utils.isManager(message))return message.channel.send("You don't have the propper rights to run this command.")

Also, we are going to check the wallet where the transfer is destined to go. If we only allow transfers between our own wallets then we can protect from thief.

if(!this.isSafe(from_acc) || !this.isSafe(to_acc))return message.channel.send(`Incorrect alias/wallet, this wallet doesn't look like your own!`);

Finally, we can run a command that transfers multiple axies from one wallet to another

post image

5- Transfer USD

Within a guild you may need to send USD equivalent to some collaborators. In this is case it is convenient to have the SLP automatically converted in real time.

let qty=args[2]
let url = "https://api.coingecko.com/api/v3/simple/price?ids=smooth-love-potion&vs_currencies=usd";
let slp_price= await fetch(url, { method: "Get" }).then(res => res.json()).then((json) => { return (Object.values(json)[0].usd)});
qty=Math.round(qty/slp_price)
await utils.transfer('usd'from,to,qty,message)

We can also use this method for transferring other tokens like Weth, Axs and SLP.

6- Claim SLP

Finally, we can claim the in game SLP generated by players for then distribute it according to your guilds rules and policies.

post image

Conclusion

Discord is a beautiful for managing community, privileges and contributors. It’s also a great tool for automation. My guild uses a more developed discord bot which contains databases, scholars can verify using passwords and can also claim their tokens without need for managers to do anything.