Initial commit

This commit is contained in:
Moon 2021-02-14 14:09:47 -07:00
commit b956f48b62
10 changed files with 25624 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea
node_modules

1970
IERC20.json Normal file

File diff suppressed because it is too large Load Diff

379
IUniswapV2Factory.json Normal file
View File

@ -0,0 +1,379 @@
{
"abi": [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token0",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "token1",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "pair",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "PairCreated",
"type": "event"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "allPairs",
"outputs": [
{
"internalType": "address",
"name": "pair",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "allPairsLength",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "tokenA",
"type": "address"
},
{
"internalType": "address",
"name": "tokenB",
"type": "address"
}
],
"name": "createPair",
"outputs": [
{
"internalType": "address",
"name": "pair",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "feeTo",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "feeToSetter",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "tokenA",
"type": "address"
},
{
"internalType": "address",
"name": "tokenB",
"type": "address"
}
],
"name": "getPair",
"outputs": [
{
"internalType": "address",
"name": "pair",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "setFeeTo",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "setFeeToSetter",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
],
"evm": {
"bytecode": {
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"deployedBytecode": {
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
}
},
"interface": [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token0",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "token1",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "pair",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "PairCreated",
"type": "event"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "allPairs",
"outputs": [
{
"internalType": "address",
"name": "pair",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "allPairsLength",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "tokenA",
"type": "address"
},
{
"internalType": "address",
"name": "tokenB",
"type": "address"
}
],
"name": "createPair",
"outputs": [
{
"internalType": "address",
"name": "pair",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "feeTo",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "feeToSetter",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "tokenA",
"type": "address"
},
{
"internalType": "address",
"name": "tokenB",
"type": "address"
}
],
"name": "getPair",
"outputs": [
{
"internalType": "address",
"name": "pair",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "setFeeTo",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "setFeeToSetter",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": ""
}

1431
IUniswapV2Pair.json Normal file

File diff suppressed because it is too large Load Diff

1924
IUniswapV2Router02.json Normal file

File diff suppressed because it is too large Load Diff

15769
ShinobiPool.json Normal file

File diff suppressed because one or more lines are too long

201
index.js Normal file
View File

@ -0,0 +1,201 @@
// 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);
});

6
loop.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
while :
do
node index.js || continue
sleep 5
done

3925
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

17
package.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "auto-farmer",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@truffle/hdwallet-provider": "^1.2.2",
"bignumber.js": "^9.0.1",
"web3": "^1.3.4",
"web3-eth-contract": "^1.3.4"
}
}