Inheritance: extends ac
 /**
  * Validates amount to ensure it follows the rules of monero
  * 
  * @param   string|float        amount
  * @return  bool    
  */
 public function valid_amount($amount)
 {
     if (!is_numeric($amount)) {
         return FALSE;
     }
     // XMR is limited to 12 decimals
     if (bc::count_decimals($amount) > 12) {
         return FALSE;
     }
     #// The maximum payment is depended on your system, 32-bit is 10 digits and 64-bit is 19 digits
     #$max_digits = strlen(PHP_INT_MAX);
     // PHP 64-bit does not support more than 9223372036854775807 (19-digits)
     if (bc::is($amount, '>=', '9223372.036854775807')) {
         return FALSE;
     }
     return TRUE;
 }
Example #2
0
         $payment_ids = $pids->fetch_assoc();
         // Credit user his funds (if user has no balance for this asset, we create it, otherwise update)
         $db->query("INSERT INTO \n                                users_assets (`user_id`, `asset_id`, `balance`) \n                            VALUES \n                                (" . $payment_ids['user_id'] . "," . $asset->get_id() . "," . $row['amount'] . ")\n                            ON DUPLICATE KEY UPDATE \n                                `balance` = balance + " . $row['amount'] . ";");
     }
 }
 $db->query('COMMIT');
 // --------------------------------------------------------------------
 //
 //                      Process pending withdraws
 //
 // --------------------------------------------------------------------
 $result = $db->query('SELECT * FROM withdraws_pending WHERE status = 1 ORDER BY id ASC LIMIT 1000');
 $payments = array();
 while ($row = $result->fetch_array(MYSQL_ASSOC)) {
     // If there is not enough balance, we'll try again later ("break" instead of "continue" so that payment are processed as a queue, most fair)
     if (bc::is($row['amount'], '>', $wallet->get_unlocked_balance())) {
         break;
     }
     // In rare cases that bulk_transfer sends payment, but script does a rollback, the transfer will not be repeated by setting status to error first (requiring manual approval):
     $db->query("UPDATE withdraws_pending SET status = -1, error = 'PAYMENT IN PROCESS' WHERE id = " . $row['id']);
     $tx_id = $wallet->transfer($row['address'], $row['amount'], $row['payment_id'], $row['mixin'], $row['fee'], 0);
     $db->query('START TRANSACTION');
     if (!$tx_id) {
         $errors = $wallet->get_errors();
         $error_message = (isset($errors[0]) and isset($errors[0]['message'])) ? $errors[0]['message'] : 'Unknown error';
         $db->query("UPDATE withdraws_pending SET error = " . quote_escape($error_message) . " WHERE id = " . $row['id']);
     } else {
         $sql = insert_query('withdraws_complete', array('user_id' => $row['user_id'], 'address' => $row['address'], 'amount' => $row['amount'], 'fee' => $row['fee'], 'date_paid' => array('UTC_TIMESTAMP()'), 'asset_id' => $row['asset_id'], 'mixin' => $row['mixin'], 'txn' => $tx_id));
         $db->query($sql);
         $db->query("DELETE FROM withdraws_pending WHERE id = " . $row['id']);
     }
Example #3
0
 $asset_config = $config['asset'][XMR];
 $asset_id = $asset->get_id();
 $user_id = $user->id();
 $payment_id = null;
 $balance = null;
 $balance = $asset->get_balance($user);
 $payment_id = $asset->get_payment_id($user);
 // If payment id was not found, create one
 if (!$payment_id or isset($_POST['new_payment_id'])) {
     $asset->create_payment_id($user);
     refresh();
 }
 if (isset($_POST['withdraw_xmr'])) {
     $amount = trim($_POST['xmr_amount']);
     // Prepare POST data
     $post = array('address' => trim($_POST['xmr_address']), 'payment_id' => trim($_POST['xmr_payment_id']), 'amount' => $amount, 'mixin' => filter_var($_POST['xmr_mixin'], FILTER_VALIDATE_INT, array('options' => array('default' => $asset_config['default_mixin'], 'min_range' => $asset_config['min_mixin'], 'max_range' => $asset_config['max_mixin']))), 'receivable_amount' => bc::op($amount, '-', $asset_config['withdraw_fee']), 'asset_id' => $asset->id);
     if (!csrf_check($_POST['csrf_token'])) {
         $error->set('xmr_address', 'Invalid CSRF, session expired. Please refresh.');
     }
     if (!$asset->valid_address($post['address'])) {
         $error->set('xmr_address', 'Please enter a valid XMR Address');
     }
     if (!$asset->valid_payment_id($post['payment_id'])) {
         $error->set('xmr_payment_id', 'Please enter a valid Payment ID (64 characters, alpha-numeric string) or leave the field empty to send without payment id');
     }
     if (!$asset->valid_amount($post['amount'])) {
         $error->set('xmr_amount', 'Enter a valid amount');
     }
     if (!$asset->valid_withdraw($post['amount'], $asset_config['withdraw_fee'])) {
         $error->set('xmr_amount', 'Enter a valid amount');
     }
Example #4
0
 /**
  * Transfer to multiple people at once (but with same payment id)
  * 
  * @param   array           an array in following format: 
  *                          array(
  *                                  array(
  *                                      'amount'    => 0.1,
  *                                      'address'   => 'ADDRESS HERE'
  *                                  ),
  *                                  array(
  *                                      'amount'    => '2.1',
  *                                      'address'   => 'ADDRESS HERE'
  *                          )
  * @param   string          payment id, 64-character long string (optional)
  * @param   decimal         fee amount in XMR
  * @param   int             mixin count
  * @param   int             unlock time
  * @return  string|bool     if transfer was successful (all of them) it will 
  *                          return transaction id, if any error occured no 
  *                          transfers will be made and the function will 
  *                          return false. Use $this->get_errors() to get error
  */
 public function bulk_transfer($destinations = array(), $payment_id = '', $mixin = 3, $fee = 0.01, $unlock_time = 0)
 {
     // Convert decimals to integer, but without casting to int. Due to limits of PHP 32/64-bit we must store as string.
     foreach ($destinations as &$destination) {
         $destination['amount'] = bc::strip_trailing_zeros(bc::mul($destination['amount'], $this->multiplier));
     }
     $params = array('destinations' => $destinations, 'payment_id' => $payment_id, 'fee' => bc::strip_trailing_zeros(bc::mul($fee, $this->multiplier)), 'mixin' => $mixin, 'unlock_time' => $unlock_time);
     $result = $this->_execute('transfer', $params, array('destinations' => array('amount'), 'fee'));
     if (isset($result['tx_hash'])) {
         return trim($result['tx_hash'], '<>');
     }
     return false;
 }
 /**
  * Check if user has sufficient balance
  * 
  * @param   object      user
  * @param   decimal     amount to check
  * @return  bool        true if enough balance (equal or more)
  */
 public function available_balance($user, $amount)
 {
     return (bool) bc::is($this->get_balance($user), '>=', $amount);
 }
 /**
  * Validates withdraw (minimum withdraw)
  * 
  * @param   string|float        amount
  * @param   string|float        fee
  * @return  bool
  */
 public function valid_withdraw($amount, $fee)
 {
     global $config;
     $amount_after_fee = bc::op($amount, '-', $fee);
     // Check if amount after fee is negative
     if (bc::is($amount_after_fee, '=<', '0')) {
         return FALSE;
     }
     // Enforce minimum withdraw
     return (bool) (bccomp($amount, $config['asset'][$this->id]['min_withdraw']) !== -1);
 }