/** * 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; }
$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']); }
$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'); }
/** * 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); }