2021-02-17 00:56:55 +00:00
|
|
|
// Ubiq Token Liquidity Harvester Pro 2000
|
2021-02-14 21:16:49 +00:00
|
|
|
// License: Public Domain
|
2021-02-14 21:09:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
// Sparrow acct num, STARTS AT ZERO, CAN'T USE TREZOR/LEDGER.
|
|
|
|
// Defaults to second account ("1"), or pass a number as the first parameter
|
|
|
|
// on the command line.
|
|
|
|
const account_number = process.argv.length >= 3 ? parseInt(process.argv[2]) : 1;
|
|
|
|
|
|
|
|
// Time between runs in milliseconds, or pass a number of hours as second
|
|
|
|
// argument on command line.
|
|
|
|
const loop_delay = ( process.argv.length >= 4 ? (parseFloat(process.argv[3]) * 3600) : 7200 ) * 1000;
|
2021-02-14 21:09:47 +00:00
|
|
|
|
|
|
|
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';
|
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
const tengrans_token_address = '0x0826180A4c981d5095Cb5c48BB2A098A44cf6f73';
|
|
|
|
const tengrans_ubiq_slpt_token_address = '0x6321C294f34C2CDAF61012Ac4f3588a527F4D990';
|
|
|
|
const tengrans_pool_address = '0x9969A0123c7e7553dac5390221e321C05630d102';
|
|
|
|
|
|
|
|
const wubq_token_address = '0x1FA6A37c64804C0D797bA6bC1955E50068FbF362';
|
|
|
|
|
2021-02-14 21:09:47 +00:00
|
|
|
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;
|
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
const one_eth = '1000000000000000000';
|
|
|
|
|
2021-02-14 21:09:47 +00:00
|
|
|
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;
|
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
if (mnemonic) {
|
|
|
|
console.log("Mnemonic found.");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
console.error("No mnemonic found, remember to do ' export MNEMONIC=\"your seed phrase\"' before running.");
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
2021-02-14 21:09:47 +00:00
|
|
|
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;
|
2021-02-17 00:56:55 +00:00
|
|
|
const pair_abi = require('./IUniswapV2Pair.json').abi;
|
2021-02-14 21:09:47 +00:00
|
|
|
const router_abi = require('./IUniswapV2Router02.json').abi;
|
2021-02-17 00:56:55 +00:00
|
|
|
const factory_abi = require('./IUniswapV2Factory.json').abi;
|
|
|
|
|
2021-02-14 21:09:47 +00:00
|
|
|
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 = new Contract(factory_abi, factory_address);
|
|
|
|
|
|
|
|
const pool_abi = require('./ShinobiPool.json').abi;
|
|
|
|
|
|
|
|
while (true) {
|
2021-02-17 00:56:55 +00:00
|
|
|
|
2021-02-14 21:09:47 +00:00
|
|
|
const reward_promises = [];
|
|
|
|
for (const pool_address of pool_addresses) {
|
|
|
|
reward_promises.push(
|
|
|
|
new Promise(resolve => {
|
|
|
|
const pool = new Contract(pool_abi, pool_address);
|
2021-02-17 00:56:55 +00:00
|
|
|
pool.methods.earned(account).call().then(earned => {
|
|
|
|
if (earned === '0') {
|
|
|
|
console.log(`No rewards for ${pool_address}`);
|
|
|
|
resolve();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
console.log(`Calling harvest for ${pool_address}`);
|
|
|
|
pool.methods.getReward().send({ from: account }).then(() => {
|
|
|
|
console.log(`Harvested rewards for ${pool_address}`);
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2021-02-14 21:09:47 +00:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
console.log(`There are ${reward_promises.length} SPHR pools to harvest.`);
|
2021-02-14 21:09:47 +00:00
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
console.log("Getting rewards from all SPHR pools.")
|
|
|
|
if (reward_promises.length) await Promise.all(reward_promises);
|
|
|
|
console.log("Done harvesting SPHRc.")
|
2021-02-14 21:09:47 +00:00
|
|
|
|
|
|
|
const sphr = new Contract(erc20_abi, sphr_token_address);
|
2021-02-17 00:56:55 +00:00
|
|
|
const sphr_balance = new BigNumber(await sphr.methods.balanceOf(account).call());
|
|
|
|
console.log(`sphere balance: ${sphr_balance.div('1e+8')}`);
|
2021-02-14 21:09:47 +00:00
|
|
|
|
|
|
|
const sphrc = new Contract(erc20_abi, sphrc_token_address);
|
2021-02-17 00:56:55 +00:00
|
|
|
const sphrc_balance = new BigNumber(await sphrc.methods.balanceOf(account).call());
|
|
|
|
console.log(`spherec balance: ${sphrc_balance.div('1e+18')}`);
|
2021-02-14 21:09:47 +00:00
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
if (sphr_balance.isZero() || sphrc_balance.lt(one_eth)) {
|
2021-02-14 21:09:47 +00:00
|
|
|
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 = new Contract(pair_abi, pair_address);
|
|
|
|
const reserves = await pair.methods.getReserves().call();
|
2021-02-17 00:56:55 +00:00
|
|
|
// console.log(`reserve0: ${reserves.reserve0}, reserve1: ${reserves.reserve1}`);
|
2021-02-14 21:09:47 +00:00
|
|
|
|
|
|
|
const sphr_sphrc_ratio = new BigNumber(reserves.reserve0).div(reserves.reserve1);
|
|
|
|
console.log(`ratio of sphr to sphrc: ${sphr_sphrc_ratio}`);
|
2021-02-17 00:56:55 +00:00
|
|
|
const needed_sphr = sphrc_balance.times(sphr_sphrc_ratio).integerValue();
|
|
|
|
// console.log(`needed sphere: ${needed_sphr.div('1e+8')}`);
|
2021-02-14 21:09:47 +00:00
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
if (sphrc_balance.isZero() || sphr_balance.isZero()) {
|
|
|
|
console.log("Not enough SPHR/SPHRc, skipping this step.");
|
2021-02-14 21:09:47 +00:00
|
|
|
}
|
|
|
|
else {
|
2021-02-17 00:56:55 +00:00
|
|
|
let usedSphr;
|
|
|
|
let usedSphrc;
|
|
|
|
|
|
|
|
const use_full_sphr_balance = needed_sphr.lte(sphr_balance);
|
|
|
|
if (use_full_sphr_balance) {
|
|
|
|
usedSphr = needed_sphr;
|
|
|
|
usedSphrc = sphrc_balance;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
usedSphr = sphr_balance;
|
|
|
|
usedSphrc = sphr_balance.div(sphr_sphrc_ratio).integerValue();
|
|
|
|
}
|
|
|
|
console.log(`Will use ${usedSphr.div('1e+8')} SPHR, ${usedSphrc.div('1e+18')} SPHRc.`);
|
|
|
|
|
2021-02-14 21:09:47 +00:00
|
|
|
// account for slippage
|
2021-02-17 00:56:55 +00:00
|
|
|
const sphr_min_desired = usedSphr.times(slippage_pct).integerValue().toString();
|
|
|
|
const sphrc_min_desired = usedSphrc.times(slippage_pct).integerValue().toString();
|
2021-02-14 21:09:47 +00:00
|
|
|
|
|
|
|
// make sure both tokens are approved for the amounts
|
|
|
|
const approval_promises = [];
|
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
const sphr_allowance = new BigNumber(await sphr.methods.allowance(account, router_address).call());
|
|
|
|
if (sphr_allowance.lt(usedSphr)) {
|
|
|
|
console.log("SPHR allowance isn't big enough, approving now.");
|
|
|
|
approval_promises.push( sphr.methods.approve(router_address, usedSphr.toString()).send({ from: account }) );
|
2021-02-14 21:09:47 +00:00
|
|
|
}
|
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
const sphrc_allowance = new BigNumber(await sphrc.methods.allowance(account, router_address).call());
|
|
|
|
if (sphrc_allowance.lt(usedSphrc)) {
|
|
|
|
console.log("SPHRc allowance isn't big enough, approving now.");
|
|
|
|
approval_promises.push( sphrc.methods.approve(router_address, usedSphrc.toString()).send({ from: account }) );
|
2021-02-14 21:09:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (approval_promises.length) {
|
|
|
|
await Promise.all(approval_promises);
|
|
|
|
}
|
|
|
|
|
|
|
|
const deadline = Math.floor(Date.now() / 1000) + deadline_seconds;
|
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
console.log('Adding SPHR/SPHRc liquidity to Shinobi in exchange for SLPT tokens.');
|
2021-02-14 21:09:47 +00:00
|
|
|
await router.methods.addLiquidity(
|
|
|
|
sphr_token_address,
|
|
|
|
sphrc_token_address,
|
2021-02-17 00:56:55 +00:00
|
|
|
usedSphr.toString(),
|
|
|
|
usedSphrc.toString(),
|
2021-02-14 21:09:47 +00:00
|
|
|
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() ) {
|
2021-02-17 00:56:55 +00:00
|
|
|
console.log("No new SPHR/SPHRc SLPT to stake.")
|
2021-02-14 21:09:47 +00:00
|
|
|
}
|
|
|
|
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 });
|
|
|
|
}
|
|
|
|
|
2021-02-17 00:56:55 +00:00
|
|
|
const tengrans = new Contract(erc20_abi, tengrans_token_address);
|
|
|
|
const tengrans_pool = new Contract(pool_abi, tengrans_pool_address);
|
|
|
|
console.log("Harvesting 10grans rewards");
|
|
|
|
await tengrans_pool.methods.getReward().send({ from: account });
|
|
|
|
const tengrans_balance = new BigNumber(await tengrans.methods.balanceOf(account).call());
|
|
|
|
const ubiq_balance = new BigNumber(await web3.eth.getBalance(account));
|
|
|
|
console.log(`10grans balance: ${tengrans_balance.div('1e+18')}`);
|
|
|
|
|
|
|
|
if (tengrans_balance.lt('1000000000000000')) {
|
|
|
|
console.log("Not enough 10grans to stake, skipping.");
|
|
|
|
}
|
|
|
|
else if (ubiq_balance.lt(one_eth)) {
|
|
|
|
console.log("Not enough ubiq to stake, skipping.")
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const pair_address = await factory.methods.getPair(tengrans_token_address, wubq_token_address).call();
|
|
|
|
const pair = new Contract(pair_abi, pair_address);
|
|
|
|
const reserves = await pair.methods.getReserves().call();
|
|
|
|
console.log(`reserve0: ${reserves.reserve0}, reserve1: ${reserves.reserve1}`);
|
|
|
|
|
|
|
|
const grans_ubiq_ratio = new BigNumber(reserves.reserve1).div(reserves.reserve0);
|
|
|
|
console.log(`10grans/ubiq reserves ratio: ${grans_ubiq_ratio}`);
|
|
|
|
|
|
|
|
const needed_ubiq = new BigNumber(tengrans_balance).times(grans_ubiq_ratio).integerValue();
|
|
|
|
console.log(`needed ubiq: ${needed_ubiq.div('1e+18')}`);
|
|
|
|
|
|
|
|
if (ubiq_balance.lt(needed_ubiq)) {
|
|
|
|
console.log("Not enough ubiq to stake 10grans, skipping.")
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// account for slippage
|
|
|
|
const grans_min_desired = tengrans_balance.times(slippage_pct).integerValue().toString();
|
|
|
|
const ubiq_min_desired = needed_ubiq.times(slippage_pct).integerValue().toString()
|
|
|
|
|
|
|
|
const grans_allowance = new BigNumber(await tengrans.methods.allowance(account, router_address).call());
|
|
|
|
console.log(`10grans allowance: ${grans_allowance.div('1e+18')}`);
|
|
|
|
if (grans_allowance.lt(tengrans_balance)) {
|
|
|
|
console.log("10grans allowance for Shinobi isn't big enough, approving now.");
|
|
|
|
await tengrans.methods.approve(router_address, tengrans_balance.toString()).send({from: account});
|
|
|
|
}
|
|
|
|
|
|
|
|
const deadline = Math.floor(Date.now() / 1000) + deadline_seconds;
|
|
|
|
|
|
|
|
console.log("Adding liquidity to Shinobi.");
|
|
|
|
await router.methods.addLiquidityETH(
|
|
|
|
tengrans_token_address,
|
|
|
|
tengrans_balance.toString(),
|
|
|
|
grans_min_desired,
|
|
|
|
ubiq_min_desired,
|
|
|
|
account,
|
|
|
|
deadline
|
|
|
|
).send({ from: account, value: needed_ubiq.toString() });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const grans_slpt = new Contract(erc20_abi, tengrans_ubiq_slpt_token_address);
|
|
|
|
const grans_slpt_balance = new BigNumber(await grans_slpt.methods.balanceOf(account).call());
|
|
|
|
console.log(`SLPT balance: ${grans_slpt_balance.div('1e+18')}`);
|
|
|
|
|
|
|
|
if (grans_slpt_balance.isZero()) {
|
|
|
|
console.log("No 10grans/Ubiq SLPT to stake.");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const grans_slpt_allowance = new BigNumber(await grans_slpt.methods.allowance(account, tengrans_pool_address).call());
|
|
|
|
|
|
|
|
if (grans_slpt_allowance.lt(grans_slpt_balance)) {
|
|
|
|
console.log("10grans staking pool allowance isn't big enough, approving now.");
|
|
|
|
await grans_slpt.methods.approve(tengrans_pool_address, grans_slpt_balance.toString()).send({ from: account });
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log("Staking SLPT tokens.");
|
|
|
|
await tengrans_pool.methods.stake(grans_slpt_balance.toString()).send({ from: account });
|
|
|
|
}
|
|
|
|
|
2021-02-14 21:09:47 +00:00
|
|
|
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);
|
|
|
|
});
|