Staking and accruing rewards using Ethers.js

This guide is created to help a user of Minterest protocol to optimise their position with regards to Governance rewards - update MINTY balance and stake additional MINTY. The example below allows doing the desired actions directly through Minterest contracts API and not using the UI.

The guide describes how to trigger the stake function and the update balance function. The first one allows to add more MINTY from a wallet into the protocol stake pool, thus increasing the weight of a user in buyback contract and increasing their share of rewards. The second one allows to optimise the user weight by adding all freshly earned rewards into the buyback weight, which again increases the weight of a user and their share of rewards.

Preconditions

In order to do the stake and update balance operations directly on the contract level using Ethers.js, one needs:

  1. A private key of a wallet with enough ETH and MINTY on it

  2. A public key of this wallet

  3. Infura API key (can be easily substituted with Alchemy or any other provider)

  4. Addresses and raw ABI files of the target contracts - Buyback and Rewards Hub

The example below is using node.js as the basis to execute the JavaScript code. One needs to set it up, as well as set up the Ethers.js library itself. The setup of node.js and Ethers.js is out of scope of the guide, as it may differ for different operating systems. Detailed instructions can be found on respective resources.

Considering the node.js and Ethers.js are installed and configured, three files should be created:

  1. A JavaScript file, that will contain the code to execute transactions

  2. Two JSON files to store the ABI of the target contracts

JSON file content

The ABI of the contracts can be retrieved using Etherscan. Open the contract pages of Buyback and Rewards Hub contracts and look for the ABI section. The result of 'RAW/Text Format" export should be copied to the respective JSON file. In the example below the JSON files are called buyback_abi.json and rewardshub_abi.json. They are placed next to the JS file with the code - this is important to simplify the import of the data.

JavaScript file

The next line of code creates a wallet abstraction in the code. This abstraction will be used to sign transaction

// 2. Create variables
const PRIVATE_KEY = "YOUR_WALLET_PRIVATE_KEY";
const PUBLIC_KEY = "YOUR_WALLET_PUBLIC_KEY";
const BUYBACK_ADDRESS = "0xED78B0879B09392a502Dd5De3f56516Be19F61e8"; 
const REWARDS_HUB_ADDRESS = "0x63e0DBEC2143acae33d2cc53e23D9134359e0399";
const stakeValue = 10;

The second section defines wallet data and addresses of contracts to work with.

// 1. Setup of provider and 
const { ethers, JsonRpcProvider } = require('ethers');
const BUYBACK_ABI = require("./buyback_abi.json");
const REWARDS_HUB_ABI = require("./rewardshub_abi.json");
const INFURA_API_KEY = "YOUR_INFURA_API_KEY";

const provider = new JsonRpcProvider(
    `https://mainnet.infura.io/v3/${INFURA_API_KEY}`
);

The next line of code creates a wallet abstraction in the code. This abstraction will be used to sign transaction

// 3. Create wallet
let wallet = new ethers.Wallet(PRIVATE_KEY, provider);

The contract abstractions (created in these line of code) will be used to call functions like stake. Note how the instances are already linked to the wallet that will be used to communicate with the chain.

// 4. Create contract instance with signer
const buyback = new ethers.Contract(BUYBACK_ADDRESS, BUYBACK_ABI, wallet);
const rewardsHub = new ethers.Contract(REWARDS_HUB_ADDRESS, REWARDS_HUB_ABI, wallet);

The following blocks define two functions, which call respective contract API endpoints for staking and updating MINTY balance. The functions are very similar, they send a signed transaction to the chain and wait for a success or error message. In this example the functions use calls to two different contracts.

// 6. Define functions to stake and update MNT balance
const makeStake = async (value) => {
    console.log(
        `Calling Stake with value ${value} in contract at address: ${BUYBACK_ADDRESS}`
    );
    const createReceipt = await buyback.stake(value);    
    await createReceipt.wait();
    console.log(`Tx successful with hash: ${createReceipt.hash}`);
};

const updateMntBalance = async () => {
    console.log(
        `Calling Update MNT in contract at address: ${REWARDS_HUB_ADDRESS}`
    );
    const createReceipt = await rewardsHub.distributeAllMnt(PUBLIC_KEY);    
    await createReceipt.wait();
    console.log(`Tx successful with hash: ${createReceipt.hash}`);
};

Finally, the code below invokes the functions.

// 7. Call the functions
makeStake(stakeValue);
updateMntBalance();

The full code of the example:

// 1. Setup of provider
const { ethers, JsonRpcProvider } = require('ethers');
const BUYBACK_ABI = require("./buyback_abi.json");
const REWARDS_HUB_ABI = require("./rewardshub_abi.json");
const INFURA_API_KEY = "2dd49fdb426a42dd9702e218034b0f2c";

const provider = new JsonRpcProvider(
    `https://mainnet.infura.io/v3/${INFURA_API_KEY}`
);

// 2. Create variables
const PRIVATE_KEY = "a23d1bf17acc1937db942b6f05a7a87db23bb618be83748f455a7acb420e87ae";
const PUBLIC_KEY = "0x7Ca0f34b42A22Dd3A0276d95A80837342cC6fc63";
const BUYBACK_ADDRESS = "0xED78B0879B09392a502Dd5De3f56516Be19F61e8"; 
const REWARDS_HUB_ADDRESS = "0x63e0DBEC2143acae33d2cc53e23D9134359e0399";
const stakeValue = 10;

// 3. Create wallet
let wallet = new ethers.Wallet(PRIVATE_KEY, provider);

// 4. Create contract instance with signer
const buyback = new ethers.Contract(BUYBACK_ADDRESS, BUYBACK_ABI, wallet);
const rewardsHub = new ethers.Contract(REWARDS_HUB_ADDRESS, REWARDS_HUB_ABI, wallet);

// 6. Define functions to stake and update MNT balance
const makeStake = async (value) => {
    console.log(
        `Calling Stake with value ${value} in contract at address: ${BUYBACK_ADDRESS}`
    );
    const createReceipt = await buyback.stake(value);    
    await createReceipt.wait();
    console.log(`Tx successful with hash: ${createReceipt.hash}`);
};

const updateMntBalance = async () => {
    console.log(
        `Calling Update MNT in contract at address: ${REWARDS_HUB_ADDRESS}`
    );
    const createReceipt = await rewardsHub.distributeAllMnt(PUBLIC_KEY);    
    await createReceipt.wait();
    console.log(`Tx successful with hash: ${createReceipt.hash}`);
};

// 7. Call the functions
makeStake(stakeValue);
updateMntBalance();

Last updated