Skip to content

Commit

Permalink
Merge pull request #27 from Adamant-im/dev
Browse files Browse the repository at this point in the history
v1.9.0: ComServer, FameEX support
  • Loading branch information
just-software-dev authored Nov 24, 2023
2 parents 4c01ad7 + ab940fe commit cee29c4
Show file tree
Hide file tree
Showing 40 changed files with 4,906 additions and 2,407 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/trade/settings
/trade/tests
/trade/co_test.js
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module.exports = {
'quote-props': ['error', 'as-needed'],
'object-curly-spacing': ['error', 'always'],
'max-len': ['error',
{ code: 131,
{ code: 133,
ignoreTrailingComments: true,
ignoreComments: true,
ignoreUrls: true,
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ See trades history example with a 3% price step:
* [Bitfinex](https://www.bitfinex.com/sign-up?refcode=4k5uFSBLZ)
* [Bittrex](https://global.bittrex.com/discover/join?referralCode=TGD-P0Z-F5W)
* [Coinstore](https://h5.coinstore.com/h5/signup?invitCode=o951vZ)
* [FameEX](https://www.fameex.com/en-US/commissiondispense?code=MKKAWV)

# Usage and Installation

Expand Down Expand Up @@ -92,7 +93,7 @@ npm i

Update `config.jsonc` if `config.default.jsonc` changed.

Then `pm2 restart tradebot`.
Then `pm2 restart coinoptimus`.

## Commands and starting a strategy

Expand Down
11 changes: 10 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const checker = require('./modules/checkerTransactions');
const doClearDB = process.argv.includes('clear_db');
const config = require('./modules/config/reader');
const txParser = require('./modules/incomingTxsParser');
const { botInterchange } = require('./modules/botInterchange');
const { initApi } = require('./routes/init');

// Socket connection
Expand All @@ -18,6 +19,12 @@ function init() {
initApi();
}

// Comserver init
if (config.com_server) {
botInterchange.connect();
botInterchange.initHandlers();
}

if (doClearDB) {
console.log('Clearing database…');
db.systemDb.db.drop();
Expand All @@ -28,7 +35,9 @@ function init() {
checker();
require('./trade/co_ladder').run();
require('./trade/co_test').test();
notify(`*${config.notifyName} started* for address _${config.address}_ (ver. ${config.version}).`, 'info');

const addressInfo = config.address ? ` for address _${config.address}_` : ' in CLI mode';
notify(`${config.notifyName} *started*${addressInfo} (${config.projectBranch}, v${config.version}).`, 'info');
}
} catch (e) {
notify(`${config.notifyName} is not started. Error: ${e}`, 'error');
Expand Down
54 changes: 41 additions & 13 deletions config.default.jsonc
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
/**
The bot's secret passphrase. Create separate ADM account for the bot.
Bot's ADM address will correspond this passphrase.
The bot's secret passphrase. Create a separate ADM account for the bot.
Bot's ADM address will correspond to this passphrase.
You'll contact this address via ADAMANT Messenger to manage the bot.
**/
"passPhrase": "distance expect praise frequent..",

/**
List of nodes to connect to ADAMANT blockchain.
List of nodes to connect to the ADAMANT blockchain.
It ensures secure communication with the bot.
If one become unavailable, bot will choose live one.
If one becomes unavailable, the bot will choose a live one.
**/
"node_ADM": [
"https://bid.adamant.im",
Expand Down Expand Up @@ -40,7 +40,7 @@
"U123.."
],

/** Notify non-admins that they are not admins. If false, bot will be silent. **/
/** Notify non-admins that they are not admins. If false, the bot will be silent. **/
"notify_non_admins": false,

/** List of supported exchanges **/
Expand All @@ -51,7 +51,8 @@
"StakeCube",
"Bitfinex",
"Bittrex",
"Coinstore"
"Coinstore",
"FameEX"
],

/** Exchange to work with. Case insensitive. **/
Expand All @@ -60,20 +61,35 @@
/** Pair to trade in regular Base/Quote format **/
"pair": "ADM/USDT",

/** Exchange's custom restrictions to override `traderapi.features()`, if you have a special account **/
"exchange_restrictions": {
/** Max number of open orders. Set 'false' to skip **/
"orderNumberLimit": false
},

/**
A short name which helps you to understand which exchange account you use.
Letters and digits only.
**/
"account": "acc1",

/** Exchange's account API key for connection **/
"apikey": "YOUR-KEY..",

/** Exchange's account API secret for connection **/
"apisecret": "YOUR-SECRET..",

/** Exchange's account trade password (if required by exchange) **/
/** Exchange's account trade password or memo (if required by exchange) **/
"apipassword": "YOUR-TRADE-PASS",

/** How to reply to admin in-chat, if first unknown command received **/
"welcome_string": "Hi! 😊 I'm a CoinOptimus trade bot. ℹ️ Learn more on https://github.com/Adamant-im/adamant-coinoptimus or type **/help**.",
/** Override project name for notifications. Letters, digits, - and ~ only. By default, it's derived from a repository name, CoinOptimus. **/
"project_name": "",

/** Bot's name for notifications. Keep it empty if you want the default format ADM/USDT@Coinstore-acc1 CoinOptimus **/
"bot_name": "",

/** Bot's name for notifications **/
"bot_name": "Lovely CoinOptimus Bot",
/** How to reply to user in-chat if the first unknown command is received. **/
"welcome_string": "Hi! 😊 I'm the {bot_name} trading bot. ℹ️ Learn more on https://marketmaking.app or type **/help**.",

/** ADAMANT addresses for notifications and monitoring. Optional. **/
"adamant_notify": [""],
Expand Down Expand Up @@ -105,18 +121,30 @@

/**
Enables health API
Allows to check if bot is running with http://ip:port/ping
Allows to check if a bot is running with http://ip:port/ping
**/
"health": false,

/**
Enables debug API
Do not set for live bots, use only for debugging.
Allows to get DBs records like http://ip:port/db?tb=incomingTxsDb
Allows to get DB records like http://ip:port/db?tb=incomingTxsDb
**/
"debug": false
},

/**
Server:Port for communication across the bots on different exchanges and pairs.
Set 'false' to disable.
**/
"com_server": false,

/**
The secret key for encrypting and decrypting messages between bot and comServer.
Set the same key in the '.env' file of comServer.
**/
"com_server_secret_key": "",

/** Minimal amount of USDT to confirm buy/sell/fill commands **/
"amount_to_confirm_usd": 100
}
8 changes: 5 additions & 3 deletions helpers/cryptos/adm_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ module.exports = class admCoin extends baseCoin {
this.account.passPhrase = config.passPhrase;
this.account.keyPair = config.keyPair;
this.account.address = config.address;
this.getBalance().then((balance) => {
log.log(`Initial ${this.token} balance: ${balance ? balance.toFixed(constants.PRINT_DECIMALS) : 'unable to receive'}`);
});
if (this.account.passPhrase) {
this.getBalance().then((balance) => {
log.log(`Initial ${this.token} balance: ${balance ? balance.toFixed(constants.PRINT_DECIMALS) : 'unable to receive'}`);
});
}
}

get FEE() {
Expand Down
6 changes: 3 additions & 3 deletions helpers/cryptos/exchanger.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ module.exports = {
async updateCryptoRates() {
const url = config.infoservice + '/get';
const rates = await axios.get(url, {})
.then(function(response) {
.then((response) => {
return response.data ? response.data.result : undefined;
})
.catch(function(error) {
.catch((error) => {
log.warn(`Unable to fetch crypto rates in updateCryptoRates() of ${utils.getModuleName(module.id)} module. Request to ${url} failed with ${error.response ? error.response.status : undefined} status code, ${error.toString()}${error.response && error.response.data ? '. Message: ' + error.response.data.toString().trim() : ''}.`);
});

Expand Down Expand Up @@ -86,7 +86,7 @@ module.exports = {
if (this.isERC20(to)) {
networkFee = this.convertCryptos('ETH', to, networkFee).outAmount;
}
};
}
const value = rate * +amount - networkFee;
return {
outAmount: +value.toFixed(constants.PRECISION_DECIMALS),
Expand Down
111 changes: 52 additions & 59 deletions helpers/dbModel.js
Original file line number Diff line number Diff line change
@@ -1,85 +1,78 @@
module.exports = (db) => {
return class {
constructor(data = {}, isSave) {
class Model {
constructor(data = {}, shouldSave) {
this.db = db;

Object.assign(this, data);
if (isSave) {

if (shouldSave) {
this.save();
}
}
static get db() {
return db;
}
static find(a) { // return Array
return new Promise((resolve, reject) => {
this.db.find(a).toArray((err, data) => {
resolve(data.map((d)=>new this(d)));
});
});
static async find(req) {
const data = await db.find(req).toArray();

return data.map((d) => new this(d));
}
static aggregate(a) { // return Array
return new Promise((resolve, reject) => {
this.db.aggregate(a).toArray((err, data) => {
resolve(data.map((d)=>new this(d)));
});
});
static async aggregate(req) {
const data = await db.aggregate(req).toArray();

return data.map((d) => new this(d));
}
static findOne(a) {
return new Promise((resolve, reject) => {
db.findOne(a).then((doc, b) => {
if (!doc) {
resolve(doc);
} else {
resolve(new this(doc));
}
});
});
static async findOne(req) {
const doc = await db.findOne(req);

return doc ? new this(doc) : doc;
}
static deleteOne(a) {
return new Promise((resolve, reject) => {
delete a.db;
db.deleteOne(a).then((res) => {
resolve(res.deletedCount);
});
});
static async deleteOne(req) {
delete req.db;

const { deletedCount } = await db.deleteOne(req);

return deletedCount;
}
static count(a) {
return new Promise((resolve, reject) => {
db.count(a).then((count) => {
resolve(count);
});
});
static async count(req) {
const count = await db.count(req);

return count;
}
_data() {
const data = {};
for (const field in this) {
if (!['db', '_id'].includes(field)) {
data[field] = this[field];

for (const fieldName in this) {
if (Object.prototype.hasOwnProperty.call(this, fieldName)) {
if (!['db', '_id'].includes(fieldName)) {
data[fieldName] = this[fieldName];
}
}
}

return data;
}
async update(obj, isSave) {
async update(obj, shouldSave) {
Object.assign(this, obj);
if (isSave) {

if (shouldSave) {
await this.save();
}
}
save() {
return new Promise((resolve, reject) => {
if (!this._id) {
db.insertOne(this._data(), (err, res) => {
this._id = res.insertedId;
resolve(this._id);
});
} else {
db.updateOne({ _id: this._id }, {
$set: this._data(),
}, { upsert: true }).then(() => {
resolve(this._id);
});
}
});
async save() {
if (!this._id) {
const res = await db.insertOne(this._data());
this._id = res.insertedId;
return this._id;
} else {
await db.updateOne({ _id: this._id }, {
$set: this._data(),
}, { upsert: true });

return this._id;
}
}
};
}

return Model;
};
28 changes: 28 additions & 0 deletions helpers/encryption.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const crypto = require('crypto');
const config = require('../modules/config/reader');

const iv = crypto.randomBytes(16);
const secretKey = crypto
.createHash('sha256')
.update(String(config.com_server_secret_key))
.digest('base64')
.substr(0, 32);

const encrypt = (text) => {
const cipher = crypto.createCipheriv('aes-256-ctr', secretKey, iv);
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);

return {
iv: iv.toString('hex'),
content: encrypted.toString('hex'),
};
};

const decrypt = (hash) => {
const decipher = crypto.createDecipheriv('aes-256-ctr', secretKey, Buffer.from(hash.iv, 'hex'));
const decrypted = Buffer.concat([decipher.update(Buffer.from(hash.content, 'hex')), decipher.final()]);

return decrypted.toString();
};

module.exports = { encrypt, decrypt };
Loading

0 comments on commit cee29c4

Please sign in to comment.