Skip to content

Commit

Permalink
Merge pull request #41 from marcelooblan2016/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
marcelooblan2016 authored Jan 8, 2022
2 parents 7cce5ee + 5e176cd commit f88c8e2
Show file tree
Hide file tree
Showing 22 changed files with 4,609 additions and 285 deletions.
5 changes: 0 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
#################
# WALLET #
#################
# Wallet Private key
PRIVATE_KEY=
#################
# NETWORK #
#################
# Network Used for transaction
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ test/swapHistory.json
test/tokenContracts.json
test/log/*.*
test/logs/*.*
test/keys
48 changes: 45 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# CryptoCurrency Bot Trader
swapping of [ERC-20 Tokens][erc20] (buy/sell) based on the market health with bot strategy (condition) - by utilizing MetaMask & Puppeteer

![image](https://drive.google.com/uc?export=view&id=1IzFTRLwAVSPK3j1h2AkwkqbOBOq6VzeC)

## List of ERC-20 Tokens Included
- ChainLink Token, Decentraland, Uniswap, Graph Token, Aave, Basic Authentication Token (BAT), Curve (CRV), Sushi Token, Sand, Avalanche, Wrapped Matic
- [Full Details][erc20List]
Expand All @@ -24,7 +26,6 @@ npm i crypto-bot-trader
## Usage
Filename as .env in {root}
```bash
PRIVATE_KEY="{Wallet privateKey}"
PREFERRED_NETWORK="matic-mainnet"
# Default: 0
HEADLESS_BROWSER=1
Expand All @@ -35,6 +36,10 @@ SELL_CUTLOSS=-5
SELL_PROFIT=1
```

## Security
As for initial setup will require you to input your PrivateKey & password (required upon encryption of your data)
![image](https://drive.google.com/uc?export=view&id=1a7fPmue1yNcsjbS9_pQvgAUBIpifCTkW)

Filename as {jsFile} in {root}

```js
Expand Down Expand Up @@ -63,10 +68,47 @@ You can also stop your trading bot on a certain date; This is quite ideal on clo

On Env File: Add the following key
```bash
CHECKPOINT_DATE="{YOUR_DATE_HERE-> Format: YYYY-MM-DD HH:mm HH:mm} (24-hour)"
CHECKPOINT_DATE="{YOUR_DATE_HERE-> Format: YYYY-MM-DD HH:mm} (24-hour)"
```

## Headless
Typical headless in puppeteer with extension of metamask will not work. Alternatively, you can use X Virtual Frame Buffer (xvfb) [See Details][npmforever]

```js
const {metaMask, trader, token} = require('crypto-bot-trader');
const Xvfb = require('xvfb');

(async function() {
let xvfb = new Xvfb();
xvfb.startSync();
// // initiate
await metaMask.build();
const initiatedTrader = new trader({metamask_with_build: metaMask, token: token});

await initiatedTrader.analyzeMarket()
setInterval(async () => {
await initiatedTrader.analyzeMarket()
}, 300000);
// every 5 minutes

})();
```

## Deploy in cloud server
Setup in cloud server, A simple CLI tool for ensuring that a given script runs continuously/in-background called (forever) [See Details][npmforever]

```js
const {metaMask, trader, token} = require('crypto-bot-trader');
const { exec } = require("child_process");

(async function() {
await metaMask.initializeSecurity({pwd: null, is_setup: true});
exec(`forever start headless.js --pwd=1`, (error, stdout, stderr) => {});

})();
```

[erc20]: https://etherscan.io/tokens
[erc20List]: https://github.com/marcelooblan2016/crypto-bot-trader/blob/main/src/Records/Migrations/tokenContracts.js

[npmforever]: https://www.npmjs.com/package/forever
[npmxvfb]: https://www.npmjs.com/package/xvfb
5 changes: 3 additions & 2 deletions dist/Metamask/Libs/loadTokenContracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
const token_1 = __importDefault(require("../../Records/token"));
const logger_1 = __importDefault(require("../../Records/logger"));
function loadTokenContracts(params) {
return __awaiter(this, void 0, void 0, function* () {
const page = params.page;
Expand All @@ -39,7 +40,7 @@ function loadTokenContracts(params) {
yield buttonCustomAddToken.click();
}
let tokenContract = tokenContracts[index];
console.log("Adding " + tokenContract['slug'] + " token ...");
logger_1.default.write({ content: "Adding " + tokenContract['slug'] + " token ..." });
yield page.waitForSelector(C.elements.add_token.input_contract_address);
yield page.focus(C.elements.add_token.input_contract_address);
yield page.type(C.elements.add_token.input_contract_address, tokenContract['contract']);
Expand Down Expand Up @@ -75,7 +76,7 @@ function loadTokenContracts(params) {
}
}
catch (error) {
console.log(error);
logger_1.default.write({ content: JSON.stringify(error) });
}
});
}
Expand Down
35 changes: 32 additions & 3 deletions dist/Metamask/metaMask.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const constants_1 = __importDefault(require("../constants"));
const lib_1 = __importDefault(require("./Libs/lib"));
const config_1 = __importDefault(require("../Records/config"));
const logger_1 = __importDefault(require("../Records/logger"));
const security_1 = __importDefault(require("../Records/security"));
class Metamask {
constructor(options) {
this.browser = null;
Expand All @@ -45,6 +46,28 @@ class Metamask {
this.C = constants_1.default;
this.processId = process.pid;
}
// public retrieveSecurityPassword(): string | null
// {
// return security.password;
// }
/*
* initializeSecurity : retrieve / set private key & encrypt it with passphrase
*/
initializeSecurity(params) {
return __awaiter(this, void 0, void 0, function* () {
let pwd = params.pwd;
let isSetup = typeof params['is_setup'] != 'undefined' ? params['is_setup'] : false;
if (security_1.default.isKeyFileExists() == false) {
yield security_1.default.setKey();
}
let pKey = yield security_1.default.retrieveKey(pwd, isSetup);
if (pKey == false) {
logger_1.default.write({ content: "Invalid keys, Exiting..." });
process.exit(0);
}
return (pKey).toString();
});
}
/*
* build : opens chromium, install metamask extensions, restore wallet, add new network, import preferred tokens
* @return void
Expand All @@ -61,6 +84,9 @@ class Metamask {
validArguments[splittedArgument[0]] = ((_a = splittedArgument[1]) !== null && _a !== void 0 ? _a : null);
});
}
// security pkey / passphrase
let pwd = typeof validArguments['pwd'] != 'undefined' ? validArguments['pwd'] : null;
let pKey = yield this.initializeSecurity({ pwd: pwd });
// check if fresh start
let envValues = config_1.default.envValues();
if (typeof envValues['PROCESS_ID'] == 'undefined') {
Expand All @@ -70,12 +96,15 @@ class Metamask {
config_1.default.update({ key: "PROCESS_ID", value: process.pid });
// launch browser
logger_1.default.write({ content: "Launching browser..." });
this.browser = yield dappeteer.launch(puppeteer_1.default, { metamaskVersion: constants_1.default.metamask_version, args: ['--no-sandbox'] });
this.browser = yield dappeteer.launch(puppeteer_1.default, {
metamaskVersion: constants_1.default.metamask_version,
args: ['--no-sandbox']
});
logger_1.default.write({ content: "Setup metamask..." });
this.metamask = yield dappeteer.setupMetamask(this.browser);
this.metamask = yield dappeteer.setupMetamask(this.browser, {});
this.page = this.metamask.page;
// import private key
let privateKey = typeof validArguments['pkey'] != 'undefined' ? validArguments['pkey'] : (constants_1.default.private_key != '' ? constants_1.default.private_key : null);
let privateKey = pKey;
if (privateKey == null) {
logger_1.default.write({ content: "Private key required, exiting..." });
process.exit(0);
Expand Down
1 change: 0 additions & 1 deletion dist/Records/Migrations/env.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = [
"PRIVATE_KEY=",
"PREFERRED_NETWORK=\"matic-mainnet\"",
"# Default: 0",
"HEADLESS_BROWSER=1",
Expand Down
102 changes: 102 additions & 0 deletions dist/Records/security.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const readline_sync_1 = __importDefault(require("readline-sync"));
const cryptr_1 = __importDefault(require("cryptr"));
const logger_1 = __importDefault(require("./logger"));
const constants_1 = __importDefault(require("../constants"));
const fs = require('fs');
class Security {
constructor(options) {
this.keyFile = './keys';
this.pwdTmp = './p.tmp';
this.defaultKey = null;
this.defaultKey = constants_1.default.network_preferred;
}
isKeyFileExists() {
return fs.existsSync(this.keyFile);
}
isPwdTmpExists() {
return fs.existsSync(this.pwdTmp);
}
setKey() {
return __awaiter(this, void 0, void 0, function* () {
let pKey = readline_sync_1.default.question('Private Key: ', {
hideEchoBack: true,
mask: '*'
});
let password = '';
let confirmPassword = '';
let isMatched = false;
do {
password = readline_sync_1.default.question('New Password: ', {
hideEchoBack: true,
mask: '*'
});
confirmPassword = readline_sync_1.default.question('Confirm Password: ', {
hideEchoBack: true,
mask: '*'
});
if ((password != '' || confirmPassword != '') && password == confirmPassword) {
isMatched = true;
}
else {
logger_1.default.write({ content: "Password not match, please try again." });
}
} while (isMatched === false);
if (isMatched === true) {
const cryptr = new cryptr_1.default(password);
logger_1.default.write({ content: "Encrypting data..." });
const encryptedString = cryptr.encrypt(pKey);
fs.writeFileSync(this.keyFile, encryptedString);
}
});
}
retrieveKey(pwd, is_setup) {
return __awaiter(this, void 0, void 0, function* () {
let isPwd = Boolean(pwd);
const pwdCrypt = new cryptr_1.default(this.defaultKey);
try {
let password = null;
if (isPwd === false) {
password = readline_sync_1.default.question('Password: ', {
hideEchoBack: true,
mask: '*'
});
}
let encryptedPwdString = null;
if (is_setup === true) {
let encryptedPwdString = pwdCrypt.encrypt(password);
yield fs.writeFileSync(this.pwdTmp, encryptedPwdString);
}
if (isPwd === true) {
if (this.isPwdTmpExists() == true) {
encryptedPwdString = yield fs.readFileSync(this.pwdTmp, 'utf8');
password = pwdCrypt.decrypt(encryptedPwdString);
// delete file
yield fs.unlinkSync(this.pwdTmp);
}
}
const cryptr = new cryptr_1.default(password);
let encryptedString = yield fs.readFileSync(this.keyFile, 'utf8');
return cryptr.decrypt(encryptedString);
}
catch (error) {
logger_1.default.write({ content: "Invalid password, please try again." });
}
return false;
});
}
}
exports.default = new Security;
4 changes: 2 additions & 2 deletions dist/Trader/trader.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Trader {
return __awaiter(this, void 0, void 0, function* () {
try {
this.checkpoint();
console.log("Analyzing market...");
logger_1.default.write({ content: "Analyzing market..." });
yield this.metaMaskWithBuild.clearPopups();
// check stable coin balancebalance
let tokenBalances = yield this.metaMaskWithBuild.getBalances();
Expand All @@ -57,7 +57,7 @@ class Trader {
// check token ready for sell
yield this.sellMode({ mappedMarketData: mappedMarketData, tokenBalances: tokenBalances });
yield this.buyMode({ tokenBalances: tokenBalances, mappedMarketData: mappedMarketData });
console.log("Market Analyzed.");
logger_1.default.write({ content: "Market Analyzed." });
return true;
}
catch (error) { }
Expand Down
1 change: 0 additions & 1 deletion dist/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ module.exports = {
"prefix": "chrome-extension://",
},
metamask_version: 'v10.1.1',
private_key: config.PRIVATE_KEY,
network_preferred: config.PREFERRED_NETWORK,
headless_browser: Boolean(parseInt((_a = config.HEADLESS_BROWSER) !== null && _a !== void 0 ? _a : 0)),
networks: [
Expand Down
Loading

0 comments on commit f88c8e2

Please sign in to comment.