First working version of PPLNS payouts
* Based PPLNS on Prop Payout script * Using defaults from prop payout, no class adjustments * Added more methods required for PPLNS * Added block methods for dynamic payout calculations * Added PPLNS Sidebar that also displays the PPLNS Target * Shares beyond this target will not be included in payouts * Shares missing to this target will be added from archives * Enabled archiving by default for PPLNS * Added configuration options for PPLNS * Documented the usage for PPLNS, defaults are sane * Added pplns_payout to run-crons Addresses #143 and if accepted will fix it
This commit is contained in:
parent
dc51d874a7
commit
2f2acdad6d
@ -68,11 +68,11 @@ Features
|
||||
|
||||
The following feature have been implemented so far:
|
||||
|
||||
* Mobile WebUI **NEW**
|
||||
* Mobile WebUI
|
||||
* Reward Systems
|
||||
* Propotional
|
||||
* PPS
|
||||
* (Planned) PPLNS
|
||||
* PPLNS **NEW**
|
||||
* Use of memcache for statistics instead of a cronjob
|
||||
* Web User accounts
|
||||
* Re-Captcha protected registration form
|
||||
|
||||
142
cronjobs/pplns_payout.php
Executable file
142
cronjobs/pplns_payout.php
Executable file
@ -0,0 +1,142 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright:: 2013, Sebastian Grewe
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
// Check if we are set as the payout system
|
||||
if ($config['payout_system'] != 'pplns') {
|
||||
verbose("Please activate this cron in configuration via payout_system = pplns\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Fetch all unaccounted blocks
|
||||
$aAllBlocks = $block->getAllUnaccounted('ASC');
|
||||
if (empty($aAllBlocks)) {
|
||||
verbose("No new unaccounted blocks found\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// We support some dynamic share targets but fall back to our fixed value
|
||||
if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) {
|
||||
$pplns_target = round($block->getAvgBlockShares($config['pplns']['type']['blockavg']['blockcount']));
|
||||
} else {
|
||||
$pplns_target = $config['pplns']['shares']['default'] ;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
foreach ($aAllBlocks as $iIndex => $aBlock) {
|
||||
if (!$aBlock['accounted']) {
|
||||
$iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0;
|
||||
$iCurrentUpstreamId = $aBlock['share_id'];
|
||||
$iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']);
|
||||
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];
|
||||
$aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);
|
||||
|
||||
if ($iRoundShares >= $pplns_target) {
|
||||
verbose("Matching or exceeding PPLNS target of $pplns_target\n");
|
||||
$aAccountShares = $share->getSharesForAccounts($aBlock['share_id'] - $pplns_target + 1, $aBlock['share_id']);
|
||||
} else {
|
||||
verbose("Not able to match PPLNS target of $pplns_target\n");
|
||||
// We need to fill up with archived shares
|
||||
// Grab the full current round shares since we didn't match target
|
||||
$aAccountShares = $aRoundAccountShares;
|
||||
// Grab only the most recent shares from Archive that fill the missing shares
|
||||
$aArchiveShares = $share->getArchiveShares($share->getMaxArchiveShareId() - ($pplns_target- $iRoundShares) + 1, $share->getMaxArchiveShareId());
|
||||
// Add archived shares to users current shares, if we have any in archive
|
||||
if (is_array($aArchiveShares)) {
|
||||
foreach($aAccountShares as $key => $aData) {
|
||||
if (array_key_exists($aData['username'], $aArchiveShares)) {
|
||||
$aAccountShares[$key]['valid'] += $aArchiveShares[$aData['username']]['valid'];
|
||||
$aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($aAccountShares)) {
|
||||
verbose("\nNo shares found for this block\n\n");
|
||||
sleep(2);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Table header for account shares
|
||||
verbose("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee\t\tStatus\n");
|
||||
|
||||
// Loop through all accounts that have found shares for this round
|
||||
foreach ($aAccountShares as $key => $aData) {
|
||||
// Payout based on PPLNS target shares, proportional payout for all users
|
||||
$aData['percentage'] = number_format(round(( 100 / $pplns_target) * $aData['valid'], 8), 8);
|
||||
$aData['payout'] = number_format(round(( $aData['percentage'] / 100 ) * $dReward, 8), 8);
|
||||
// Defaults
|
||||
$aData['fee' ] = 0;
|
||||
$aData['donation'] = 0;
|
||||
|
||||
if ($config['fees'] > 0)
|
||||
$aData['fee'] = number_format(round($config['fees'] / 100 * $aData['payout'], 8), 8);
|
||||
// Calculate donation amount, fees not included
|
||||
$aData['donation'] = number_format(round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8), 8);
|
||||
|
||||
// Verbose output of this users calculations
|
||||
verbose($aData['id'] . "\t" .
|
||||
$aData['username'] . "\t" .
|
||||
$aData['valid'] . "\t" .
|
||||
$aData['invalid'] . "\t" .
|
||||
$aData['percentage'] . "\t" .
|
||||
$aData['payout'] . "\t" .
|
||||
$aData['donation'] . "\t" .
|
||||
$aData['fee'] . "\t");
|
||||
|
||||
$strStatus = "OK";
|
||||
// Add full round share statistics, not just PPLNS
|
||||
foreach ($aRoundAccountShares as $key => $aRoundData) {
|
||||
if ($aRoundData['username'] == $aData['username'])
|
||||
if (!$statistics->updateShareStatistics($aRoundData, $aBlock['id']))
|
||||
$strStatus = "Stats Failed";
|
||||
}
|
||||
// Add new credit transaction
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id']))
|
||||
$strStatus = "Transaction Failed";
|
||||
// Add new fee debit for this block
|
||||
if ($aData['fee'] > 0 && $config['fees'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id']))
|
||||
$strStatus = "Fee Failed";
|
||||
// Add new donation debit
|
||||
if ($aData['donation'] > 0)
|
||||
if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id']))
|
||||
$strStatus = "Donation Failed";
|
||||
verbose("\t$strStatus\n");
|
||||
}
|
||||
|
||||
// Move counted shares to archive before this blockhash upstream share
|
||||
$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId);
|
||||
// Delete all accounted shares
|
||||
if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) {
|
||||
verbose("\nERROR : Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!\n");
|
||||
exit(1);
|
||||
}
|
||||
// Mark this block as accounted for
|
||||
if (!$block->setAccounted($aBlock['id'])) {
|
||||
verbose("\nERROR : Failed to mark block as accounted! Aborting!\n");
|
||||
}
|
||||
|
||||
verbose("------------------------------------------------------------------------\n\n");
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,7 @@ PIDFILE='/tmp/mmcfe-ng-cron.pid'
|
||||
CRONHOME='.'
|
||||
|
||||
# List of cruns to execute
|
||||
CRONS="findblock.php proportional_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php"
|
||||
CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php"
|
||||
|
||||
# Additional arguments to pass to cronjobs
|
||||
CRONARGS="-v"
|
||||
|
||||
@ -79,6 +79,30 @@ class Block {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total amount of blocks in our table
|
||||
* @param noone
|
||||
* @return data int Count of rows
|
||||
**/
|
||||
public function getBlockCount() {
|
||||
$stmt = $this->mysqli->prepare("SELECT COUNT(id) AS blocks FROM $this->table");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return (int)$result->fetch_object()->blocks;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch our average share count for the past N blocks
|
||||
* @param limit int Maximum blocks to check
|
||||
* @return data float Float value of average shares
|
||||
**/
|
||||
public function getAvgBlockShares($limit=10) {
|
||||
$stmt = $this->mysqli->prepare("SELECT AVG(shares) AS average FROM $this->table LIMIT ?");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return (float)$result->fetch_object()->average;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all unconfirmed blocks from table
|
||||
* @param confirmations int Required confirmations to consider block confirmed
|
||||
|
||||
@ -13,9 +13,10 @@ class Share {
|
||||
// This defines each share
|
||||
public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time;
|
||||
|
||||
public function __construct($debug, $mysqli, $salt) {
|
||||
public function __construct($debug, $mysqli, $user) {
|
||||
$this->debug = $debug;
|
||||
$this->mysqli = $mysqli;
|
||||
$this->user = $user;
|
||||
$this->debug->append("Instantiated Share class", 2);
|
||||
}
|
||||
|
||||
@ -86,47 +87,67 @@ class Share {
|
||||
* Fetch all shares grouped by accounts to count share per account
|
||||
* @param previous_upstream int Previous found share accepted by upstream to limit results
|
||||
* @param current_upstream int Current upstream accepted share
|
||||
* @param limit int Limit to this amount of shares for PPLNS
|
||||
* @return data array username, valid and invalid shares from account
|
||||
**/
|
||||
public function getSharesForAccounts($previous_upstream=0, $current_upstream) {
|
||||
$stmt = $this->mysqli->prepare("SELECT
|
||||
a.id,
|
||||
validT.account AS username,
|
||||
sum(validT.valid) as valid,
|
||||
IFNULL(sum(invalidT.invalid),0) as invalid
|
||||
FROM
|
||||
(
|
||||
SELECT DISTINCT
|
||||
SUBSTRING_INDEX( `username` , '.', 1 ) as account,
|
||||
COUNT(id) AS valid
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?
|
||||
AND our_result = 'Y'
|
||||
GROUP BY account
|
||||
) validT
|
||||
LEFT JOIN
|
||||
(
|
||||
SELECT DISTINCT
|
||||
SUBSTRING_INDEX( `username` , '.', 1 ) as account,
|
||||
COUNT(id) AS invalid
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?
|
||||
AND our_result = 'N'
|
||||
GROUP BY account
|
||||
) invalidT
|
||||
ON validT.account = invalidT.account
|
||||
INNER JOIN accounts a ON a.username = validT.account
|
||||
GROUP BY a.username DESC");
|
||||
if ($this->checkStmt($stmt)) {
|
||||
$stmt->bind_param('iiii', $previous_upstream, $current_upstream, $previous_upstream, $current_upstream);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
a.id,
|
||||
SUBSTRING_INDEX( s.username , '.', 1 ) as username,
|
||||
SUM(IF(our_result='Y', 1, 0)) AS valid,
|
||||
SUM(IF(our_result='N', 1, 0)) AS invalid
|
||||
FROM $this->table AS s
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
|
||||
WHERE s.id BETWEEN ? AND ?
|
||||
GROUP BY username DESC
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the highest available share ID from archive
|
||||
**/
|
||||
function getMaxArchiveShareId() {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT MAX(share_id) AS share_id FROM $this->tableArchive
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||
return $result->fetch_object()->share_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We need a certain amount of valid archived shares
|
||||
* param left int Left/lowest share ID
|
||||
* param right int Right/highest share ID
|
||||
* return array data Returns an array with usernames as keys for easy access
|
||||
**/
|
||||
function getArchiveShares($left, $right) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
SELECT
|
||||
a.id,
|
||||
SUBSTRING_INDEX( s.username , '.', 1 ) as username,
|
||||
SUM(IF(our_result='Y', 1, 0)) AS valid,
|
||||
SUM(IF(our_result='N', 1, 0)) AS invalid
|
||||
FROM $this->tableArchive AS s
|
||||
LEFT JOIN " . $this->user->getTableName() . " AS a
|
||||
ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
|
||||
WHERE s.id BETWEEN ? AND ?
|
||||
GROUP BY username DESC
|
||||
");
|
||||
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $left, $right) && $stmt->execute() && $result = $stmt->get_result()) {
|
||||
$aData = NULL;
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$aData[$row['username']] = $row;
|
||||
}
|
||||
if (is_array($aData)) return $aData;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Move accounted shares to archive table, this step is optional
|
||||
* @param previous_upstream int Previous found share accepted by upstream to limit results
|
||||
@ -135,10 +156,11 @@ class Share {
|
||||
* @return bool
|
||||
**/
|
||||
public function moveArchive($current_upstream, $block_id, $previous_upstream=0) {
|
||||
$archive_stmt = $this->mysqli->prepare("INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time)
|
||||
SELECT id, username, our_result, upstream_result, ?, time
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?");
|
||||
$archive_stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time)
|
||||
SELECT id, username, our_result, upstream_result, ?, time
|
||||
FROM $this->table
|
||||
WHERE id BETWEEN ? AND ?");
|
||||
if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) {
|
||||
$archive_stmt->close();
|
||||
return true;
|
||||
@ -267,4 +289,4 @@ class Share {
|
||||
}
|
||||
}
|
||||
|
||||
$share = new Share($debug, $mysqli, SALT);
|
||||
$share = new Share($debug, $mysqli, $user);
|
||||
|
||||
@ -183,6 +183,30 @@ $config['chaininfo'] = 'http://allchains.info';
|
||||
// Pool fees applied to users in percent, default: 0 (disabled)
|
||||
$config['fees'] = 0;
|
||||
|
||||
/**
|
||||
* PPLNS requires some settings to run properly. First we need to define
|
||||
* a default shares count that is applied if we don't have a proper type set.
|
||||
* Different dynamic types can be applied, or you can run a fixed scheme.
|
||||
*
|
||||
* Explanation
|
||||
* default : Default target shares for PPLNS
|
||||
* type : Payout type used in PPLNS
|
||||
* blockcount : Amount of blocks to check for avg shares
|
||||
*
|
||||
* Available Options:
|
||||
* default : amount of shares, integeger
|
||||
* type : blockavg or fixed
|
||||
* blockcount : amount of blocks, any integer
|
||||
*
|
||||
* Defaults:
|
||||
* default = 4000000
|
||||
* type = `blockavg`
|
||||
* blockcount = 10
|
||||
**/
|
||||
$config['pplns']['shares']['default'] = 4000000;
|
||||
$config['pplns']['shares']['type'] = 'blockavg';
|
||||
$config['pplns']['blockavg']['blockcount'] = 10;
|
||||
|
||||
// Pool target difficulty as set in pushpoold configuration file
|
||||
// Please also read this for stratum: https://github.com/TheSerapher/php-mmcfe-ng/wiki/FAQ
|
||||
$config['difficulty'] = 20;
|
||||
|
||||
@ -83,6 +83,13 @@ if (@$_SESSION['USERDATA']['id']) {
|
||||
$aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']);
|
||||
|
||||
switch ($config['payout_system']) {
|
||||
case 'pplns':
|
||||
if ($iAvgBlockShares = round($block->getAvgBlockShares($config['pplns']['type']['blockavg']['blockcount']))) {
|
||||
$aGlobal['pplns']['target'] = $iAvgBlockShares;
|
||||
} else {
|
||||
$aGlobal['pplns']['target'] = $config['pplns']['shares']['default'];
|
||||
}
|
||||
break;
|
||||
case 'pps':
|
||||
break;
|
||||
default:
|
||||
|
||||
74
public/templates/mmcFE/global/sidebar_pplns.tpl
Normal file
74
public/templates/mmcFE/global/sidebar_pplns.tpl
Normal file
@ -0,0 +1,74 @@
|
||||
<div class="block" style="clear:none; margin-top:15px; margin-left:13px;">
|
||||
<div class="block_head">
|
||||
<div class="bheadl"></div>
|
||||
<div class="bheadr"></div>
|
||||
<h1>Dashboard</h1>
|
||||
</div>
|
||||
<div class="block_content" style="padding-top:10px;">
|
||||
<table class="sidebar" style="width: 196px">
|
||||
<tr>
|
||||
<td><b>PPLNS Target</b></td>
|
||||
<td class="right">{$GLOBAL.pplns.target|number_format}</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Your Stats</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Hashrate</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.hashrate|number_format} KH/s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Unpaid Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares between the last 120 confirms block until now.'></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Valid<b></td>
|
||||
<td class="right"><i>{$GLOBAL.userdata.shares.valid|number_format}</i><font size='1px'></font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Valid</td>
|
||||
<td class="right"><i>{$GLOBAL.roundshares.valid|number_format}</i> <font size='1px'></font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Round Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares since last found block (ie. round shares)'></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Valid</b></td>
|
||||
<td class="right"><i>{$GLOBAL.roundshares.valid|number_format}</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Invalid</b></td>
|
||||
<td class="right"><i>{$GLOBAL.roundshares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.roundshares.invalid)|number_format:"2"}%)</font>{/if}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Invalid</b></td>
|
||||
<td class="right"><i>{$GLOBAL.userdata.shares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.userdata.shares.invalid)|number_format:"2"}%)</font>{/if}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>{$GLOBAL.config.currency} Round Estimate</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Block</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.est_block|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Fees</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.est_fee|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Donation</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.est_donation|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Payout</b></td>
|
||||
<td class="right">{$GLOBAL.userdata.est_payout|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr><td colspan="2"><b><u>{$GLOBAL.config.currency} Account Balance</u></b></td></tr>
|
||||
<tr><td>Confirmed</td><td class="right"><b>{$GLOBAL.userdata.balance.confirmed|default:"0"}</td></tr>
|
||||
<tr><td>Unconfirmed</td><td class="right"><b>{$GLOBAL.userdata.balance.unconfirmed|default:"0"}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="bendl"></div>
|
||||
<div class="bendr"></div>
|
||||
</div>
|
||||
63
public/templates/mobile/global/sidebar_pplns.tpl
Normal file
63
public/templates/mobile/global/sidebar_pplns.tpl
Normal file
@ -0,0 +1,63 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td><b>PPLNS Target</b></td>
|
||||
<td align="right">{$GLOBAL.pplns.target|number_format}</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Your Stats</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Hashrate</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.hashrate|number_format} KH/s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Unpaid Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares between the last 120 confirms block until now.'></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Valid<b></td>
|
||||
<td align="right"><i>{$GLOBAL.userdata.shares.valid|number_format}</i><font size='1px'></font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Valid</td>
|
||||
<td align="right"><i>{$GLOBAL.roundshares.valid|number_format}</i> <font size='1px'></font></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>Round Shares</u></b> <span id='tt'><img src='{$PATH}/images/questionmark.png' height='15px' width='15px' title='Submitted shares since last found block (ie. round shares)'></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Valid</b></td>
|
||||
<td align="right"><i>{$GLOBAL.roundshares.valid|number_format}</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Pool Invalid</b></td>
|
||||
<td align="right"><i>{$GLOBAL.roundshares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.roundshares.invalid)|number_format:"2"}%)</font>{/if}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Your Invalid</b></td>
|
||||
<td align="right"><i>{$GLOBAL.userdata.shares.invalid|number_format}</i>{if $GLOBAL.roundshares.valid > 0}<font size='1px'> ({(100 / $GLOBAL.roundshares.valid * $GLOBAL.userdata.shares.invalid)|number_format:"2"}%)</font>{/if}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><b><u>{$GLOBAL.config.currency} Round Estimate</u></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Block</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.est_block|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Fees</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.est_fee|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Donation</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.est_donation|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Payout</b></td>
|
||||
<td align="right">{$GLOBAL.userdata.est_payout|number_format:"3"}</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr><td colspan="2"><b><u>{$GLOBAL.config.currency} Account Balance</u></b></td></tr>
|
||||
<tr><td>Confirmed</td><td align="right"><b>{$GLOBAL.userdata.balance.confirmed|default:"0"}</td></tr>
|
||||
<tr><td>Unconfirmed</td><td align="right"><b>{$GLOBAL.userdata.balance.unconfirmed|default:"0"}</td></tr>
|
||||
</table>
|
||||
Loading…
Reference in New Issue
Block a user