Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f3a1bc63e | ||
|
|
81216de5dd | ||
|
|
f185e971c4 | ||
|
|
d55efbd227 | ||
|
|
53432ba8fe | ||
|
|
4ebff37820 | ||
|
|
2061e666c9 | ||
|
|
0b238adccd | ||
|
|
e41cb8d8f4 | ||
|
|
67254f63ba | ||
|
|
8a725c0438 | ||
|
|
f83f5b371b | ||
|
|
f84ee142c2 |
25
coins/00_example.json
Normal file
25
coins/00_example.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "Example",
|
||||||
|
"symbol": "EXM",
|
||||||
|
"algorithm": "x99",
|
||||||
|
"peerMagic": "0f0f0f0f",
|
||||||
|
"peerMagicTestnet": "f0f0f0f0",
|
||||||
|
|
||||||
|
"mainnet": {
|
||||||
|
"bech32": "ex",
|
||||||
|
"bip32": {
|
||||||
|
"public": "0f0f0f0f"
|
||||||
|
},
|
||||||
|
"pubKeyHash": "00",
|
||||||
|
"scriptHash": "ff"
|
||||||
|
},
|
||||||
|
|
||||||
|
"testnet": {
|
||||||
|
"bech32": "tex",
|
||||||
|
"bip32": {
|
||||||
|
"public": "f0f0f0f0"
|
||||||
|
},
|
||||||
|
"pubKeyHash": "0f",
|
||||||
|
"scriptHash": "f0"
|
||||||
|
}
|
||||||
|
}
|
||||||
25
coins/chaincoin.json
Normal file
25
coins/chaincoin.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "Chaincoin",
|
||||||
|
"symbol": "CHC",
|
||||||
|
"algorithm": "c11",
|
||||||
|
"peerMagic": "a3d27a03",
|
||||||
|
"peerMagicTestnet": "fbc21102",
|
||||||
|
|
||||||
|
"mainnet": {
|
||||||
|
"bech32": "chc",
|
||||||
|
"bip32": {
|
||||||
|
"public": "0488b21e"
|
||||||
|
},
|
||||||
|
"pubKeyHash": "1c",
|
||||||
|
"scriptHash": "04"
|
||||||
|
},
|
||||||
|
|
||||||
|
"testnet": {
|
||||||
|
"bech32": "tchc",
|
||||||
|
"bip32": {
|
||||||
|
"public": "043587cf"
|
||||||
|
},
|
||||||
|
"pubKeyHash": "50",
|
||||||
|
"scriptHash": "2c"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,5 +2,5 @@
|
|||||||
"name": "Fastcoin",
|
"name": "Fastcoin",
|
||||||
"symbol": "FST",
|
"symbol": "FST",
|
||||||
"algorithm": "scrypt",
|
"algorithm": "scrypt",
|
||||||
"peerMagic": "fdc2b5dc"
|
"peerMagic": "fbc0b6db"
|
||||||
}
|
}
|
||||||
@ -1,7 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "Litecoin",
|
"name": "Litecoin",
|
||||||
"symbol": "LTC",
|
"symbol": "LTC",
|
||||||
"algorithm": "scrypt",
|
"algorithm": "scrypt",
|
||||||
"peerMagic": "fbc0b6db",
|
"peerMagic": "fbc0b6db",
|
||||||
"peerMagicTestnet": "fdd2c8f1"
|
"peerMagicTestnet": "fdd2c8f1",
|
||||||
|
|
||||||
|
"mainnet": {
|
||||||
|
"bech32": "ltc",
|
||||||
|
"bip32": {
|
||||||
|
"public": "0488B21E"
|
||||||
|
},
|
||||||
|
"pubKeyHash": "30",
|
||||||
|
"scriptHash": "05"
|
||||||
|
},
|
||||||
|
|
||||||
|
"testnet": {
|
||||||
|
"bech32": "tltc",
|
||||||
|
"bip32": {
|
||||||
|
"public": "043587CF"
|
||||||
|
},
|
||||||
|
"pubKeyHash": "6F",
|
||||||
|
"scriptHash": "C4"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@
|
|||||||
"hashrateWindow": 300
|
"hashrateWindow": 300
|
||||||
},
|
},
|
||||||
"adminCenter": {
|
"adminCenter": {
|
||||||
"enabled": false,
|
"enabled": true,
|
||||||
"password": "password"
|
"password": "password"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
31
init.js
31
init.js
@ -82,6 +82,10 @@ if (cluster.isWorker){
|
|||||||
case 'profitSwitch':
|
case 'profitSwitch':
|
||||||
new ProfitSwitch(logger);
|
new ProfitSwitch(logger);
|
||||||
break;
|
break;
|
||||||
|
case 'switchingPaymentProcessor':
|
||||||
|
var SwitchingPaymentProcessor = require('./libs/switchingPaymentProcessor.js');
|
||||||
|
new SwitchingPaymentProcessor(logger);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -143,6 +147,17 @@ var buildPoolConfigs = function(){
|
|||||||
var coinProfile = JSON.parse(JSON.minify(fs.readFileSync(coinFilePath, {encoding: 'utf8'})));
|
var coinProfile = JSON.parse(JSON.minify(fs.readFileSync(coinFilePath, {encoding: 'utf8'})));
|
||||||
poolOptions.coin = coinProfile;
|
poolOptions.coin = coinProfile;
|
||||||
poolOptions.coin.name = poolOptions.coin.name.toLowerCase();
|
poolOptions.coin.name = poolOptions.coin.name.toLowerCase();
|
||||||
|
if (coinProfile.mainnet) {
|
||||||
|
poolOptions.coin.mainnet.bip32.public = Buffer.from(coinProfile.mainnet.bip32.public, 'hex').readUInt32LE(0);
|
||||||
|
poolOptions.coin.mainnet.pubKeyHash = Buffer.from(coinProfile.mainnet.pubKeyHash, 'hex').readUInt8(0);
|
||||||
|
poolOptions.coin.mainnet.scriptHash = Buffer.from(coinProfile.mainnet.scriptHash, 'hex').readUInt8(0);
|
||||||
|
}
|
||||||
|
if (coinProfile.testnet) {
|
||||||
|
poolOptions.coin.testnet.bip32.public = Buffer.from(coinProfile.testnet.bip32.public, 'hex').readUInt32LE(0);
|
||||||
|
poolOptions.coin.testnet.pubKeyHash = Buffer.from(coinProfile.testnet.pubKeyHash, 'hex').readUInt8(0);
|
||||||
|
poolOptions.coin.testnet.scriptHash = Buffer.from(coinProfile.testnet.scriptHash, 'hex').readUInt8(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (poolOptions.coin.name in configs){
|
if (poolOptions.coin.name in configs){
|
||||||
|
|
||||||
@ -356,7 +371,21 @@ var processCoinSwitchCommand = function(params, options, reply){
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var startSwitchingPaymentProcessor = function(){
|
||||||
|
if (!fs.existsSync('libs/switchingPaymentProcessor.js')) return;
|
||||||
|
|
||||||
|
var worker = cluster.fork({
|
||||||
|
workerType: 'switchingPaymentProcessor',
|
||||||
|
pools: JSON.stringify(poolConfigs),
|
||||||
|
portalConfig: JSON.stringify(portalConfig)
|
||||||
|
});
|
||||||
|
worker.on('exit', function(code, signal){
|
||||||
|
logger.error('Master', 'Switching Payment Processor', 'Died, spawning replacement...');
|
||||||
|
setTimeout(function(){
|
||||||
|
startSwitchingPaymentProcessor();
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var startPaymentProcessor = function(){
|
var startPaymentProcessor = function(){
|
||||||
|
|
||||||
@ -434,6 +463,8 @@ var startProfitSwitch = function(){
|
|||||||
|
|
||||||
startPaymentProcessor();
|
startPaymentProcessor();
|
||||||
|
|
||||||
|
startSwitchingPaymentProcessor();
|
||||||
|
|
||||||
startWebsite();
|
startWebsite();
|
||||||
|
|
||||||
startProfitSwitch();
|
startProfitSwitch();
|
||||||
|
|||||||
@ -8,7 +8,7 @@ module.exports = function() {
|
|||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
var version = '0.0.1',
|
var version = '0.0.1',
|
||||||
PUBLIC_API_URL = 'http://www.coinwarz.com/v1/api/profitability/?apikey=YOUR_API_KEY&algo=all',
|
PUBLIC_API_URL = 'https://www.coinwarz.com/v1/api/profitability/?apikey=YOUR_API_KEY&algo=all',
|
||||||
USER_AGENT = 'nomp/node-open-mining-portal'
|
USER_AGENT = 'nomp/node-open-mining-portal'
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
|
|||||||
@ -8,7 +8,7 @@ module.exports = function() {
|
|||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
var version = '0.1.0',
|
var version = '0.1.0',
|
||||||
PUBLIC_API_URL = 'http://pubapi.cryptsy.com/api.php',
|
PUBLIC_API_URL = 'https://pubapi.cryptsy.com/api.php',
|
||||||
PRIVATE_API_URL = 'https://api.cryptsy.com/api',
|
PRIVATE_API_URL = 'https://api.cryptsy.com/api',
|
||||||
USER_AGENT = 'nomp/node-open-mining-portal'
|
USER_AGENT = 'nomp/node-open-mining-portal'
|
||||||
|
|
||||||
|
|||||||
@ -85,7 +85,7 @@ module.exports = function(logger, poolConfig){
|
|||||||
shareData.worker,
|
shareData.worker,
|
||||||
isValidShare ? 'Y' : 'N',
|
isValidShare ? 'Y' : 'N',
|
||||||
isValidBlock ? 'Y' : 'N',
|
isValidBlock ? 'Y' : 'N',
|
||||||
shareData.difficulty * (poolConfig.coin.mposDiffMultiplier || 1),
|
shareData.shareDiff,
|
||||||
typeof(shareData.error) === 'undefined' ? null : shareData.error,
|
typeof(shareData.error) === 'undefined' ? null : shareData.error,
|
||||||
shareData.blockHash ? shareData.blockHash : (shareData.blockHashInvalid ? shareData.blockHashInvalid : '')
|
shareData.blockHash ? shareData.blockHash : (shareData.blockHashInvalid ? shareData.blockHashInvalid : '')
|
||||||
];
|
];
|
||||||
|
|||||||
@ -64,6 +64,7 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
|||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(callback){
|
function(callback){
|
||||||
|
if (poolOptions.address != false) {
|
||||||
daemon.cmd('validateaddress', [poolOptions.address], function(result) {
|
daemon.cmd('validateaddress', [poolOptions.address], function(result) {
|
||||||
if (result.error){
|
if (result.error){
|
||||||
logger.error(logSystem, logComponent, 'Error with payment processing daemon ' + JSON.stringify(result.error));
|
logger.error(logSystem, logComponent, 'Error with payment processing daemon ' + JSON.stringify(result.error));
|
||||||
@ -90,6 +91,8 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
|||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
}
|
||||||
|
else callback();
|
||||||
},
|
},
|
||||||
function(callback){
|
function(callback){
|
||||||
daemon.cmd('getbalance', [], function(result){
|
daemon.cmd('getbalance', [], function(result){
|
||||||
@ -529,12 +532,37 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function handleAddress(address) {
|
||||||
|
if (address.length === 40){
|
||||||
|
return util.addressFromEx(poolOptions.address, address);
|
||||||
|
}
|
||||||
|
else return address;
|
||||||
|
}
|
||||||
|
|
||||||
var getProperAddress = function(address){
|
var getProperAddress = function(address){
|
||||||
if (address.length === 40){
|
if (address != false) {
|
||||||
return util.addressFromEx(poolOptions.address, address);
|
return handleAddress(address);
|
||||||
}
|
} else {
|
||||||
else return address;
|
var addressToPay = '';
|
||||||
|
|
||||||
|
daemon.cmd('getnewaddress', [], function(result){
|
||||||
|
if (result.error){
|
||||||
|
callback(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
addressToPay = result.data;
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
logger.error(logSystem, logComponent, 'Error getting a new address. Got: ' + result.data);
|
||||||
|
callback(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, true, true);
|
||||||
|
|
||||||
|
return handleAddress(addressToPay);
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,33 @@ module.exports = function(logger){
|
|||||||
|
|
||||||
var proxySwitch = {};
|
var proxySwitch = {};
|
||||||
|
|
||||||
|
var switchingDaemons = (function(){
|
||||||
|
var daemons = {};
|
||||||
|
|
||||||
|
for (var switchName in portalConfig.switching){
|
||||||
|
if (!portalConfig.switching[switchName].singleCoinPayout) continue;
|
||||||
|
var daemonConfig = portalConfig.switching[switchName].singleCoinPayout.daemon;
|
||||||
|
var daemon = new Stratum.daemon.interface([daemonConfig], function(severity, message){
|
||||||
|
logger[severity](logSystem, logComponent, message);
|
||||||
|
});
|
||||||
|
daemons[switchName] = daemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return daemons;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var singleCoinPayoutPorts = (function(){
|
||||||
|
var ports = {};
|
||||||
|
for (var switchName in portalConfig.switching){
|
||||||
|
if (!portalConfig.switching[switchName].singleCoinPayout) continue;
|
||||||
|
for (var port in portalConfig.switching[switchName].ports){
|
||||||
|
ports[parseInt(port)] = switchName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ports;
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);
|
var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);
|
||||||
|
|
||||||
//Handle messages from master process sent via IPC
|
//Handle messages from master process sent via IPC
|
||||||
@ -128,10 +155,13 @@ module.exports = function(logger){
|
|||||||
//Functions required for internal payment processing
|
//Functions required for internal payment processing
|
||||||
else{
|
else{
|
||||||
|
|
||||||
var shareProcessor = new ShareProcessor(logger, poolOptions);
|
var shareProcessor = new ShareProcessor(logger, portalConfig, poolOptions, singleCoinPayoutPorts);
|
||||||
|
|
||||||
handlers.auth = function(port, workerName, password, authCallback){
|
handlers.auth = function(port, workerName, password, authCallback){
|
||||||
if (poolOptions.validateWorkerUsername !== true)
|
|
||||||
|
var switchDaemon = switchingDaemons[singleCoinPayoutPorts[port]];
|
||||||
|
|
||||||
|
if (!switchDaemon && poolOptions.validateWorkerUsername !== true)
|
||||||
authCallback(true);
|
authCallback(true);
|
||||||
else {
|
else {
|
||||||
if (workerName.length === 40) {
|
if (workerName.length === 40) {
|
||||||
@ -144,7 +174,9 @@ module.exports = function(logger){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pool.daemon.cmd('validateaddress', [workerName], function (results) {
|
var daemon = switchDaemon || pool.daemon;
|
||||||
|
|
||||||
|
daemon.cmd('validateaddress', [workerName], function (results) {
|
||||||
var isValid = results.filter(function (r) {
|
var isValid = results.filter(function (r) {
|
||||||
return r.response.isvalid
|
return r.response.isvalid
|
||||||
}).length > 0;
|
}).length > 0;
|
||||||
|
|||||||
@ -15,7 +15,7 @@ value: a hash with..
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = function(logger, poolConfig){
|
module.exports = function(logger, portalConfig, poolConfig, singleCoinPayoutPorts){
|
||||||
|
|
||||||
var redisConfig = poolConfig.redis;
|
var redisConfig = poolConfig.redis;
|
||||||
var coin = poolConfig.coin.name;
|
var coin = poolConfig.coin.name;
|
||||||
@ -68,10 +68,25 @@ module.exports = function(logger, poolConfig){
|
|||||||
|
|
||||||
this.handleShare = function(isValidShare, isValidBlock, shareData){
|
this.handleShare = function(isValidShare, isValidBlock, shareData){
|
||||||
|
|
||||||
|
|
||||||
|
/*var shareKey = (function(){
|
||||||
|
var port = shareData.port.toString();
|
||||||
|
for (var switchName in portalConfig.switching){
|
||||||
|
if (!portalConfig.switching[switchName]['singleCoinPayout']) continue;
|
||||||
|
var ports = Object.keys(portalConfig.switching[switchName].ports);
|
||||||
|
if (ports.indexOf(port) !== -1) return switchName;
|
||||||
|
}
|
||||||
|
return coin;
|
||||||
|
})();*/
|
||||||
|
|
||||||
|
var shareKey = singleCoinPayoutPorts[shareData.port] || coin;
|
||||||
|
|
||||||
|
console.log('share key ' + shareKey);
|
||||||
|
|
||||||
var redisCommands = [];
|
var redisCommands = [];
|
||||||
|
|
||||||
if (isValidShare){
|
if (isValidShare){
|
||||||
redisCommands.push(['hincrbyfloat', coin + ':shares:roundCurrent', shareData.worker, shareData.difficulty]);
|
redisCommands.push(['hincrbyfloat', shareKey + ':shares:roundCurrent', shareData.worker, shareData.difficulty]);
|
||||||
redisCommands.push(['hincrby', coin + ':stats', 'validShares', 1]);
|
redisCommands.push(['hincrby', coin + ':stats', 'validShares', 1]);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|||||||
12
package.json
12
package.json
@ -12,9 +12,9 @@
|
|||||||
"litecoin",
|
"litecoin",
|
||||||
"scrypt"
|
"scrypt"
|
||||||
],
|
],
|
||||||
"homepage": "https://github.com/ranchimall/node-open-mining-portal",
|
"homepage": "https://github.com/zone117x/node-open-mining-portal",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/ranchimall/node-open-mining-portal/issues"
|
"url": "https://github.com/zone117x/node-open-mining-portal/issues"
|
||||||
},
|
},
|
||||||
"license": "GPL-2.0",
|
"license": "GPL-2.0",
|
||||||
"author": "Matthew Little",
|
"author": "Matthew Little",
|
||||||
@ -28,10 +28,10 @@
|
|||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/ranchimall/node-open-mining-portal.git"
|
"url": "https://github.com/zone117x/node-open-mining-portal.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"stratum-pool": "git://github.com/ranchimall/node-stratum-pool.git",
|
"stratum-pool": "git://github.com/zone117x/node-stratum-pool.git#dev",
|
||||||
"dateformat": "1.0.12",
|
"dateformat": "1.0.12",
|
||||||
"node-json-minify": "*",
|
"node-json-minify": "*",
|
||||||
"redis": "0.12.1",
|
"redis": "0.12.1",
|
||||||
@ -42,10 +42,10 @@
|
|||||||
"compression": "*",
|
"compression": "*",
|
||||||
"dot": "*",
|
"dot": "*",
|
||||||
"colors": "*",
|
"colors": "*",
|
||||||
"node-watch": "0.5.9",
|
"node-watch": "*",
|
||||||
"request": "2.69.0",
|
"request": "2.69.0",
|
||||||
"nonce": "*",
|
"nonce": "*",
|
||||||
"bignum": "0.12.1",
|
"bignum": "0.13.0",
|
||||||
"extend": "*"
|
"extend": "*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<link rel="icon" type="image/png" href="/static/favicon.png"/>
|
<link rel="icon" type="image/png" href="/static/favicon.png"/>
|
||||||
|
|
||||||
<link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
|
||||||
|
|
||||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.min.css">
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.min.css">
|
||||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.min.css">
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.min.css">
|
||||||
@ -76,7 +76,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
This site is powered by the open source <a target="_blank" href="https://github.com/zone117x/node-open-mining-portal/">NOMP</a>
|
This site is powered by the open source <a target="_blank" href="https://github.com/zone117x/node-open-mining-portal/">NOMP</a>
|
||||||
project created by Matthew Little and licensed under the <a href="http://www.gnu.org/licenses/gpl-2.0.html">GPL</a>
|
project created by Matthew Little and licensed under the <a href="https://www.gnu.org/licenses/gpl-2.0.html">GPL</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<i class="fa fa-heart"></i> Support this project by donating <i class="fa fa-btc"></i> BTC: 1KRotMnQpxu3sePQnsVLRy3EraRFYfJQFR
|
<i class="fa fa-heart"></i> Support this project by donating <i class="fa fa-btc"></i> BTC: 1KRotMnQpxu3sePQnsVLRy3EraRFYfJQFR
|
||||||
@ -84,9 +84,9 @@
|
|||||||
<div id="communityFooter">
|
<div id="communityFooter">
|
||||||
Community <i class="fa fa-comment"></i> : <a target="_blank" href="https://kiwiirc.com/client/irc.freenode.net/nomp">#nomp IRC</a>
|
Community <i class="fa fa-comment"></i> : <a target="_blank" href="https://kiwiirc.com/client/irc.freenode.net/nomp">#nomp IRC</a>
|
||||||
|
|
|
|
||||||
<a target="_blank" href="http://reddit.com/r/nomp">/r/nomp</a>
|
<a target="_blank" href="https://reddit.com/r/nomp">/r/nomp</a>
|
||||||
|
|
|
|
||||||
<iframe src="http://ghbtns.com/github-btn.html?user=zone117x&repo=node-open-mining-portal&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="140" height="20"></iframe>
|
<iframe src="https://ghbtns.com/github-btn.html?user=zone117x&repo=node-open-mining-portal&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="140" height="20"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user