diff --git a/web/yaamp/modules/site/SiteController.php b/web/yaamp/modules/site/SiteController.php
index ee36e39..8e1f56d 100644
--- a/web/yaamp/modules/site/SiteController.php
+++ b/web/yaamp/modules/site/SiteController.php
@@ -138,6 +138,32 @@ class SiteController extends CommonController
/////////////////////////////////////////////////
+ public function actionTickets()
+ {
+ if(!$this->admin) return;
+ $coin = getdbo('db_coins', getiparam('id'));
+ if (!$coin) {
+ $this->goback();
+ }
+
+ $this->render('coin_tickets', array('coin'=>$coin));
+ }
+
+ public function actionTicketBuy()
+ {
+ if(!$this->admin) return;
+ $coin = getdbo('db_coins', getiparam('id'));
+ $maxamount = (double) arraySafeVal($_POST, 'maxamount');
+ if ($coin && $maxamount) {
+ $remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport);
+ $res = $remote->purchaseticket($coin->account, $maxamount);
+ user()->setFlash('message', is_string($res) ? "ticket txid: $res" : json_encode($res));
+ }
+ $this->goback();
+ }
+
+ /////////////////////////////////////////////////
+
public function actionIndex()
{
if(isset($_GET['address']))
diff --git a/web/yaamp/modules/site/coin_results.php b/web/yaamp/modules/site/coin_results.php
index 822d13e..736d50a 100644
--- a/web/yaamp/modules/site/coin_results.php
+++ b/web/yaamp/modules/site/coin_results.php
@@ -203,7 +203,7 @@ echo '
'.altcoinvaluetoa($balance).' | ';
$btc = bitcoinvaluetoa($balance*$coin->price);
echo "$btc | ";
if ($PoS) echo ''.$stake.' | ';
-if ($DCR) echo ''."$stake ($tickets)".' | ';
+if ($DCR) echo ''.CHtml::link("$stake ($tickets)", '/site/tickets?id='.$coin->id).' | ';
if ($DCR) echo ''.$ticketprice.' | ';
echo "$connections | ";
diff --git a/web/yaamp/modules/site/coin_tickets.php b/web/yaamp/modules/site/coin_tickets.php
new file mode 100644
index 0000000..15aff11
--- /dev/null
+++ b/web/yaamp/modules/site/coin_tickets.php
@@ -0,0 +1,224 @@
+goback();
+$DCR = ($coin->symbol == 'DCR');
+
+$this->pageTitle = 'Tickets - '.$coin->symbol;
+
+// last week
+$list_since = arraySafeVal($_GET,'since',time()-(7*24*3600));
+
+$maxrows = arraySafeVal($_GET,'rows', 100);
+$maxrows = min($maxrows, 2500);
+
+$remote = new Bitcoin($coin->rpcuser, $coin->rpcpasswd, $coin->rpchost, $coin->rpcport);
+$info = $remote->getinfo();
+$stakeinfo = $remote->getstakeinfo();
+
+echo getAdminSideBarLinks().'
';
+echo getAdminWalletLinks($coin, $info, 'tickets').'
';
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+JavascriptFile("/yaamp/ui/js/jquery.metadata.js");
+JavascriptFile("/yaamp/ui/js/jquery.tablesorter.widgets.js");
+
+echo <<
+td.missed { color: darkred; }
+tr.voted { color: darkgreen; }
+div.form { text-align: right; height: 30px; width: 350px; float: right; margin-top: -48px; margin-bottom: 16px; margin-right: -8px; }
+.main-submit-button { cursor: pointer; }
+
+
+
+
+
+end;
+
+showTableSorter('maintable', "{
+ tableClass: 'dataGrid',
+ widgets: ['zebra','Storage','saveSort'],
+ widgetOptions: {
+ saveSort: true
+ }
+}");
+
+echo <<
+
+
+Time |
+Category |
+Amount |
+Stake |
+Height |
+Confirm |
+Fees |
+Tx(s) |
+
+
+end;
+
+$account = $DCR ? '*' : '';
+
+$txs = $remote->listtransactions($account, 2500);
+
+if (empty($txs)) {
+ if (!empty($remote->error)) {
+ echo "RPC Error: {$remote->error}";
+ }
+ // retry...
+ $txs = $remote->listtransactions($account, 200);
+}
+
+$txs_array = array(); $lastday = '';
+
+if (!empty($txs)) {
+ // to hide truncated days sums
+ $tx = reset($txs);
+ if (count($txs) == 2500)
+ $lastday = strftime('%F', $tx['time']);
+
+ if (!empty($txs)) foreach($txs as $tx)
+ {
+ if (intval($tx['time']) > $list_since)
+ $txs_array[] = $tx;
+ }
+ krsort($txs_array);
+}
+
+$voted_txs = array();
+$tickets = $remote->gettickets(true);
+
+// extract stake txs from decred transactions
+if ($DCR) {
+
+ $prev_tx = array(); $lastday = '';
+ foreach($txs_array as $key => $tx)
+ {
+ $prev_txid = arraySafeVal($prev_tx,"txid");
+ $category = $tx['category'];
+ if ($category == 'send' && arraySafeVal($tx,'generated')) {
+ $txs_array[$key]['category'] = 'spent';
+ }
+ else if ($category == 'send' && $prev_txid === arraySafeVal($tx,"txid")) {
+ // if txid is the same as the last income... it's not a real "send"
+ if ($prev_tx['amount'] == 0 - $tx['amount'])
+ $txs_array[$key]['category'] = 'spent';
+ }
+ else if ($category == 'send' && $tx['amount'] == -0) {
+ $stx = $remote->getrawtransaction($tx['txid'], 1);
+
+ // voted (listed twice ? in listtransactions)
+ if ($tx['vout'] > 0) {
+ $category = 'ticket';
+ // ticket price
+ if ($stx && isset($stx['vin'][0])) {
+ $txs_array[$key]['input'] = $stx['vin'][0]['amountin'] * 0.00000001;
+ }
+ } else {
+ $category = 'stake';
+ if ($stx && isset($stx['vin'][0])) {
+ // won ticket value
+ $txs_array[$key]['amount'] = $stx['vin'][0]['amountin'] * 0.00000001;
+ }
+ if ($stx && isset($stx['vin'][1])) {
+ $voted_txs[] = $stx['vin'][1]['txid'];
+ }
+ }
+
+ $txs_array[$key]['category'] = $category;
+ $txs_array[$key]['stx'] = $stx;
+
+ }
+ else if ($category == 'receive') {
+ $prev_tx = $tx;
+ }
+ // for truncated day sums
+ if ($lastday == '' && count($txs) == 2500)
+ $lastday = strftime('%F', $tx['time']);
+ }
+ ksort($txs_array);
+}
+
+$rows = 0;
+foreach($txs_array as $tx)
+{
+ $category = arraySafeVal($tx, 'category');
+
+ if ($category != 'stake' && $category != 'ticket') continue;
+
+ $conf = arraySafeVal($tx,'confirmations');
+ if ($category == 'stake' && $conf < 256) $category = 'immature';
+
+ $stx = arraySafeVal($tx,'stx');
+ $stake = ''; $amount = '';
+ if ($category == 'ticket') {
+ $stake = $stx['vout'][0]['value'];
+ if ($stake == 0) continue;
+ if (in_array($tx['txid'], $voted_txs)) $category = 'voted';
+ else if (!in_array($tx['txid'], $tickets['hashes'])) $category = 'missed';
+ } else {
+ $amount = (double) arraySafeVal($tx,'amount');
+ $stake = $amount - $stx['vout'][2]['value'];
+ }
+
+ $block = null;
+ if(isset($tx['blockhash']))
+ $block = $remote->getblock($tx['blockhash']);
+
+ echo '';
+
+ $d = datetoa2($tx['time']);
+ echo ''.$d.' | ';
+
+ echo ''.$category.' | ';
+ echo ''.$amount.' | ';
+ echo ''.$stake.' | ';
+
+ if($block)
+ echo ''.$block['height'].' | ';
+ else
+ echo ' | ';
+
+ echo ''.$conf.' | ';
+
+ echo ''.abs(arraySafeVal($tx,'fee')).' | ';
+
+ echo '';
+ if(!empty($block)) {
+ $txid = arraySafeVal($tx, 'txid');
+ $label = substr($txid, 0, 7);
+ echo CHtml::link($label, '/explorer?id='.$coin->id.'&txid='.$txid, array('target'=>'_blank'));
+ echo ' ('.count($block['tx']).')';
+ }
+ echo ' | ';
+
+ echo '
';
+
+ $rows++;
+ if ($rows >= $maxrows) break;
+}
+
+echo '
';
+
+echo 'Ticket price: '.$stakeinfo['difficulty'].' + '.$remote->getticketfee().' '.$coin->symbol.'/kB
';
+echo 'Tickets: '.$stakeinfo['live'];
+if ($stakeinfo['immature']) echo ' + '.$stakeinfo['immature'].' immature';
+if ($stakeinfo['ownmempooltix']) echo ' + '.$stakeinfo['ownmempooltix'].' purchased';
+echo '
';
+echo 'Total won: '.$stakeinfo['totalsubsidy'].' '.$coin->symbol.' ('.$stakeinfo['voted'].')
';
+if ($stakeinfo['missed']) echo 'Missed: '.$stakeinfo['missed'].'
';
+
+$staking = $remote->getgenerate() > 0 ? 'enabled' : 'disabled';
+echo '
';
+echo 'Staking: '.$staking.'
';
+echo 'Auto buy ticket(s) < '.$remote->getticketmaxprice().' '.$coin->symbol.'
';
+
+echo '';
+//echo json_encode($stakeinfo, 128);
+echo '
';