Programming Tutorial: Claiming Airdrops and Staking SPS (JavaScript/NodeJS)
Intro
Practically every day for the last 4 months I have been manually claiming and staking my SPS airdrops. These happening across several accounts which I use to hold my Splinterlands assets has taken more hours than it would have, if I'd taken the time to automate it.
After reading @bauloewe's Python tutorials (Part 1 & Part 2) to do this, I decided to put these into JavsScript, so I can run them alongside some of my other utilities.
This script has been tested using NodeJS v16.13.0, though the same code should run through a browser page with the same packages imported. The airdrop claiming is limited to hive-engine/in-game assets, with game assets held on platforms such as WAX, Ethereum and Binance Smart Chain, requiring a different signature (a variation of Step 3).
Requirements
The following node modules will need to be installed. Each of the following links have instructions for installation using Node Package Manager(npm), as well as browser importing URLs.
Packages | |
---|---|
axios | https://www.npmjs.com/package/axios |
hive-js | https://www.npmjs.com/package/@hiveio/hive-js |
eosjs-ecc | https://www.npmjs.com/package/eosjs-ecc |
Into the Code
Importing our Modules
Step 1: Import
Our first step is to import the node modules. These can be grouped together at the top of our .js file.
// Modules
const axios = require('axios'); // npm i axios
const hive = require('@hiveio/hive-js'); // https://www.npmjs.com/package/@hiveio/hive-js
const ecc = require('eosjs-ecc') // https://www.npmjs.com/package/eosjs-ecc
Step 2: Configure
Our second step is to configure the hive account and run frequency. To sign the required transactions and broadcast to hive we will need a valid username and posting key for the player's Hive/Splinterlands account. The claim/stake frequencies can be adjusted, though to prevent unnecessary requests to the API, we will be using 5 minutes for our stake frequency, and 3 hours for our airdrop checking frequency.
// Configurations
let params = {
username: `YOUR_USERNAME`,
posting_key: `YOUR_PRIVATE_POSTING_KEY`,
stake_frequency: 5, // Minutes
airdrop_frequency: 3 // Hours
}
Step 3: Claim
To Claim our airdrop, we'll write three functions:
- auto_claim_airdrop()
- login()
- claim_sps()
This first function, auto_claim_airdrop(), checks if the account has any airdrops unclaimed related to their HIVE wallet and (in-game) balances. The only required in our GET request is the accounts username.
async function auto_claim_airdrop() {
setInterval(async () => {
let url = `https://api2.splinterlands.com/players/sps?username=${params.username}`
let response = await axios.get(url);
let airdrops_array = response.data.airdrop;
let claim_pending = airdrops_array.find(claim =>
(claim.platform === `game` && claim.claim_tx === null)) !== undefined;
if (claim_pending) login();
else console.log(`No aidrops to claim.`)
}, params.airdrop_frequency * 60 * 60 * 1000)
}
Our second function, login(), is called only when an airdrop is available to claim. The reason for logging in, which wasn't required during the first step, is to get login token used to claim our airdrop during the third function.
To log in, 3 parameters are required in our GET request to Splinterlands:
- name - @username
- ts - the current time in milliseconds
- sig - a string signed with the accounts posting key, generated using the above two strings.
async function login() {
params.login_signature = ecc.sign(`${params.username}${params.ts}`, params.posting_key);
params.ts = new Date().getTime();
let url = `https://api2.splinterlands.com/players/login`
+ `?name=${params.username}`
+ `&ts=${params.ts}`
+ `&sig=${params.login_signature}`;
let response = await axios.get(url);
params.token = response.data.token;
claim_sps();
}
Our third function, claim_sps(), submits the claim through another GET request, and reports whether it was successful, or whether an error has occured.
Six parameters are required:
- platform - hive
- address - @username
- sig - a string signed with the accounts posting key, using the platform, accounts username, and time string.
- ts - the current time in milliseconds (we reuse the time generated for our login)
- token - our loging token from earlier
- username - @username
async function claim_sps() {
params.claim_signature = ecc.sign(`hive${params.username}${params.ts}`, params.posting_key);
let url = `https://ec-api.splinterlands.com/players/claim_sps_airdrop`
+ `?platform=hive`
+ `&address=${params.username}`
+ `&sig=${params.claim_signature}`
+ `&ts=${params.ts}`
+ `&token=${params.token}`
+ `&username=${params.username}`
let response = await axios.get(url);
let data = response.data;
if (data.success) {
console.log(`Account ${data.data.to} successfully claimed ${data.data.qty} SPS.`
+ ` See transaction at hiveblocks.com/tx/${data.tx.id}`);
} else console.log(`Error:`, data.error);
}
Step 4: Stake
To Stake our airdrop token, and continue claiming our SPS balance for that compound interest, we'll write three functions:
- auto_stake()
- get_balance()
- stake_sps(qty)
Our first function, auto_stake(), is a short and simple scheduling function. It initiates the other two functions immediately, then schedules them for the configured frequency.
async function auto_stake() {
stake_sps(await get_balance());
setInterval(async () => stake_sps(await get_balance()), params.stake_frequency * 60 * 1000);
}
Our second function, get_balance(), sends a GET request to Splinterlands with the account username, to get the current in-game balance. This is used as an input to the next function.
async function get_balance() {
let url = `https://api2.splinterlands.com/players/balances?username=${params.username}`;
let response = await axios.get(url);
return response.data.find(currency => currency.token === `SPS`).balance;
}
Our third function submits our staking transaction to the HIVE Blockchain. A sample image of the output transaction is included:
function stake_sps(qty) {
let json = {"token": "SPS", "qty": qty}
hive.broadcast.customJson(
params.posting_key, [], [params.username], `sm_stake_tokens`, JSON.stringify(json), function (err, res) {
if (res) console.log(`Staked ${qty} tokens.`)
else console.log(`Error Staking: ${err}`)
});
}
Complete Code:
The following code is ready to run in NodeJS.
// Modules
const axios = require('axios'); // npm i axios
const hive = require('@hiveio/hive-js'); // https://www.npmjs.com/package/@hiveio/hive-js
const ecc = require('eosjs-ecc') // https://www.npmjs.com/package/eosjs-ecc
// Configurations
let params = {
username: `YOUR_USERNAME`,
posting_key: `YOUR_PRIVATE_POSTING_KEY`,
stake_frequency: 5, // Minutes
airdrop_frequency: 3 // Hours
}
auto_claim_airdrop();
auto_stake();
// Airdrop Claim Functions
async function auto_claim_airdrop() {
setInterval(async () => {
let url = `https://api2.splinterlands.com/players/sps?username=${params.username}`
let response = await axios.get(url);
let airdrops_array = response.data.airdrop;
let claim_pending = airdrops_array.find(claim => (claim.platform === `game` && claim.claim_tx === null)) !== undefined;
if (claim_pending) login();
else console.log(`No aidrops to claim.`)
}, params.airdrop_frequency * 60 * 60 * 1000)
}
async function login() {
params.ts = new Date().getTime();
params.login_signature = ecc.sign(`${params.username}${params.ts}`, params.posting_key);
let url = `https://api2.splinterlands.com/players/login`
+ `?name=${params.username}`
+ `&ts=${params.ts}`
+ `&sig=${params.login_signature}`;
let response = await axios.get(url);
params.token = response.data.token;
claim_sps();
}
async function claim_sps() {
params.claim_signature = ecc.sign(`hive${params.username}${params.ts}`, params.posting_key);
let url = `https://ec-api.splinterlands.com/players/claim_sps_airdrop`
+ `?platform=hive`
+ `&address=${params.username}`
+ `&sig=${params.claim_signature}`
+ `&ts=${params.ts}`
+ `&token=${params.token}`
+ `&username=${params.username}`
let response = await axios.get(url);
let data = response.data;
if (data.success) {
console.log(`Account ${data.data.to} successfully claimed ${data.data.qty} SPS.`
+ ` See transaction at hiveblocks.com/tx/${data.tx.id}`);
} else console.log(`Error:`, data.error);
}
// Auto Stake Functions
async function auto_stake() {
stake_sps(await get_balance());
setInterval(async () => stake_sps(await get_balance()), params.stake_frequency * 60 * 1000);
}
async function get_balance() {
let url = `https://api2.splinterlands.com/players/balances?username=${params.username}`;
let response = await axios.get(url);
return response.data.find(currency => currency.token === `SPS`).balance;
}
function stake_sps(qty) {
let json = {"token": "SPS", "qty": qty}
hive.broadcast.customJson(
params.posting_key, [], [params.username], `sm_stake_tokens`, JSON.stringify(json), function (err, res) {
if (res) console.log(`Staked ${qty} tokens.`)
else console.log(`Error Staking: ${err}`)
});
}
Hi @splinterstats (@kiokizz),
great tutorial. I never did much work with javascript just typescript so seeing how to auto claim sps with it is definitely interesting. Thanks for sharing :)
@kiokizz brother, you're a legend. This stuff is the real deal.
Congratulations @splinterstats! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):
Your next target is to reach 13000 upvotes.
You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word
STOP
Check out the last post from @hivebuzz:
Support the HiveBuzz project. Vote for our proposal!
Hello,
Thank you for this, saved me some work! Btw, I made a step by step instruction for non techie here.
👍