auto-farmer/index.js

202 lines
7.6 KiB
JavaScript

// SPHRc Liquidity Harvester Pro 2000
// License: Proprietary
/**
* INSTALL:
* first install node.js and npm (they are usually together.)
* navigate in terminal to the directory you unpacked this in.
* type: npm install
*
* RUN:
* navigate in terminal to the directory you unpacked this in.
* type, putting a space before "export":
* export MNEMONIC="your seed phrase goes here"
* type: ./loop.sh
* leave the terminal open. It will run forever. If you need to end it, exit
* the terminal.
*/
const account_number = 1; // Sparrow acct num, STARTS AT ZERO, CAN'T USE TREZOR
const loop_delay = 7200 * 1000; // 2 hours
const router_address = '0xf3ce4655a44146c8eefbf45651f6479f9d67a77a';
const sphr_token_address = '0x20e3dD746DdF519B23Ffbbb6Da7a5d33eA6349D6';
const sphrc_token_address = '0x402C2C3aceeb52b8654A0631012ceD49cbc9bDc4';
const slpt_token_address = '0x596430dF1662B39d481265752A0c694f72C99AD3';
const grans_sphr_pool_address = '0xcc22E95B1926F48acF17852582F14B22AB4cF0b9';
const ubiq_sphr_pool_address = '0x5130bc430Ed7cD252a18B724fBa40cd1078F8C75';
const ricks_sphr_pool_address = '0xB934A32F065a9db4B2Df565b901276c88dc31F3E';
const sphrc_sphr_pool_address = '0x93CdC7AF7fa7CfA9E603Cf4406acD81171A7Cfaa';
const pool_addresses = [
grans_sphr_pool_address,
ubiq_sphr_pool_address,
ricks_sphr_pool_address,
sphrc_sphr_pool_address
];
const deadline_seconds = 600;
slippage_pct = .995;
const HDWalletProvider = require('@truffle/hdwallet-provider');
const Web3 = require("web3");
const Contract = require('web3-eth-contract');
const BigNumber = require('bignumber.js');
BigNumber.set({ DECIMAL_PLACES: 18, ROUNDING_MODE: BigNumber.ROUND_FLOOR })
const mnemonic = process.env.MNEMONIC;
const provider = new HDWalletProvider({
mnemonic: {
phrase: mnemonic
},
providerOrUrl: "https://rpc.octano.dev"
});
Contract.setProvider(provider);
let web3 = new Web3(provider);
const erc20_abi = require('./IERC20.json').abi;
const router_abi = require('./IUniswapV2Router02.json').abi;
const router = new Contract(router_abi, router_address);
console.log("Starting loop");
(async () => {
const account = (await web3.eth.getAccounts())[account_number];
console.log(`account: ${account}`);
const factory_address = await router.methods.factory().call();
console.log(`factory address: ${factory_address}`);
const factory_abi = require('./IUniswapV2Factory.json').abi;
const factory = new Contract(factory_abi, factory_address);
const pool_abi = require('./ShinobiPool.json').abi;
while (true) {
const reward_promises = [];
for (const pool_address of pool_addresses) {
reward_promises.push(
new Promise(resolve => {
const pool = new Contract(pool_abi, pool_address);
console.log(`Calling harvest for ${pool_address}`);
pool.methods.getReward().send({ from: account }).then(() => {
console.log(`Harvested rewards for ${pool_address}`);
resolve();
})
})
);
}
console.log(`There are ${reward_promises.length} pools to harvest.`);
console.log("Getting rewards from all pools.")
await Promise.all(reward_promises);
console.log("Done harvesting.")
const sphr = new Contract(erc20_abi, sphr_token_address);
const sphr_balance = await sphr.methods.balanceOf(account).call();
const sphr_balance_pretty = new BigNumber(sphr_balance).div('1e+8');
console.log(`sphere balance: ${sphr_balance_pretty}`);
const sphrc = new Contract(erc20_abi, sphrc_token_address);
const sphrc_balance = await sphrc.methods.balanceOf(account).call();
const sphrc_balance_pretty = new BigNumber(sphrc_balance).div('1e+18');
console.log(`spherec balance: ${sphrc_balance_pretty}`);
if (new BigNumber(sphr_balance).isZero() || new BigNumber(sphrc_balance).lt('1000000000000000000')) {
console.log("sphere or spherec balance too low, skipping this step.")
}
else {
const pair_address = await factory.methods.getPair(sphr_token_address, sphrc_token_address).call();
const pair_abi = require('./IUniswapV2Pair.json').abi;
const pair = new Contract(pair_abi, pair_address);
const reserves = await pair.methods.getReserves().call();
console.log(`reserve0: ${reserves.reserve0}, reserve1: ${reserves.reserve1}`);
const sphr_sphrc_ratio = new BigNumber(reserves.reserve0).div(reserves.reserve1);
console.log(`ratio of sphr to sphrc: ${sphr_sphrc_ratio}`);
const needed_sphr = new BigNumber(sphrc_balance).times(sphr_sphrc_ratio).integerValue().toString();
console.log(`needed sphere: ${new BigNumber(needed_sphr).div('1e+8')}`);
if (new BigNumber(needed_sphr).gt(sphr_balance)) {
console.log("Not enough sphr, skipping this step.");
}
else {
// account for slippage
const sphr_min_desired = new BigNumber(needed_sphr).times(slippage_pct).integerValue().toString();
const sphrc_min_desired = new BigNumber(sphrc_balance).times(slippage_pct).integerValue().toString()
console.log(`desired: ${new BigNumber(sphr_min_desired).div('1e+8')} ${new BigNumber(sphrc_min_desired).div('1e+18')}`);
// make sure both tokens are approved for the amounts
const approval_promises = [];
const sphr_allowance = await sphr.methods.allowance(account, router_address).call();
if (new BigNumber(sphr_allowance).lt(needed_sphr)) {
console.log("Sphere allowance isn't big enough, approving now.");
approval_promises.push( sphr.methods.approve(router_address, needed_sphr).send({ from: account }) );
}
const sphrc_allowance = await sphrc.methods.allowance(account, router_address).call()
if (new BigNumber(sphrc_allowance).lt(sphrc_balance)) {
console.log("Sphere Cubed allowance isn't big enough, approving now.");
approval_promises.push( sphrc.methods.approve(router_address, sphrc_balance).send({ from: account }) );
}
if (approval_promises.length) {
await Promise.all(approval_promises);
}
const deadline = Math.floor(Date.now() / 1000) + deadline_seconds;
console.log('Adding liquidity to Shinobi.');
await router.methods.addLiquidity(
sphr_token_address,
sphrc_token_address,
needed_sphr,
sphrc_balance,
sphr_min_desired,
sphrc_min_desired,
account,
deadline
).send({ from: account });
}
}
const slpt = new Contract(erc20_abi, slpt_token_address);
const slpt_balance = await slpt.methods.balanceOf(account).call();
if ( new BigNumber(slpt_balance).isZero() ) {
console.log("No new SLPT to stake.")
}
else {
console.log(`SLPT balance: ${new BigNumber(slpt_balance).div('1e+18')}`);
const sphr_sphrc_pool = new Contract(pool_abi, sphrc_sphr_pool_address);
const slpt_allowance = await slpt.methods.allowance(account, sphrc_sphr_pool_address).call();
if (new BigNumber(slpt_allowance).lt(slpt_balance)) {
console.log("SLPT Allowance for yield farm is not enough, approving now.");
await slpt.methods.approve(sphrc_sphr_pool_address, slpt_balance).send({ from: account });
}
console.log("Staking SLPT tokens.");
await sphr_sphrc_pool.methods.stake(slpt_balance).send({ from: account });
}
console.log("Waiting for a while...");
await new Promise(resolve => setTimeout(resolve, loop_delay));
console.log("Done waiting, resuming.");
}
})().catch(e => {
console.error('oh shit', e);
try { provider.engine.stop(); } catch(e) {}
process.exit(1);
});