...
 
Commits (10)
......@@ -42,12 +42,17 @@ class fetchMergeRequests extends Command
$projects = $connection->mergeRequests('all');
foreach ($projects as $project) {
$state = 'OPENED';
if (strpos($project->title, '[IDEA]') !== false) {
$state = 'IDEA';
}
$title = str_replace('[IDEA]','',$project->title);
// create requests that are still pending
$project = Project::firstOrNew([
'merge_request_id' => $project->id,
],[
'title' => $title,
'state' => $state,
'title' => trim($title),
'gitlab_state' => $project->state,
'gitlab_username' => $project->author->username,
'gitlab_url' => $project->web_url,
......@@ -55,4 +60,10 @@ class fetchMergeRequests extends Command
$project->save();
}
}
// fetch the idea
// check for merged merges.
// if proposal merged search for its md file
//issue payment_id and payment page
//
}
<?php
namespace App\Console\Commands;
use App\Project;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Monero\WalletOld;
class GenerateAddresses extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'generate:addresses';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generates monero addresses for any merged proposals';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$projects = Project::whereNotNull('filename')->whereNull('payment_id')->where('state', 'FUNDING-REQUIRED')->get();
$wallet = new WalletOld();
foreach ($projects as $project) {
$addressDetails = $wallet->getPaymentAddress();
$project->address_uri = $wallet->createQrCodeString($addressDetails['address']);
$project->address = $addressDetails['address'];
$project->payment_id = $addressDetails['paymentId'];
Storage::disk('public')->put("/img/qrcodes/{$project->payment_id}.png", $project->generateQrcode());
$project->qr_code = "img/qrcodes/{$project->payment_id}.png";
$project->raised_amount = 0;
$project->save();
}
}
}
......@@ -37,7 +37,6 @@ class ProcessProposals extends Command
*/
public function handle()
{
$projects = Project::whereNull('filename');
$details = [];
$files = Storage::files('ffs-proposals');
foreach ($files as $file) {
......@@ -45,9 +44,12 @@ class ProcessProposals extends Command
$detail['name'] = $file;
$detail['values'] = $this->getAmountFromText($file);
$details[] = $detail['values']['title'];
$project = $projects->where('title', $detail['values']['title'])->first();
$project = Project::where('title', $detail['values']['title'])->first();
if ($project) {
$project->filename = $file;
if ($project->state === 'IDEA') {
$project->state = 'FUNDING-REQUIRED';
}
$project->target_amount = $detail['values']['amount'];
$project->save();
}
......
......@@ -6,9 +6,9 @@ use App\Deposit;
use App\Project;
use Illuminate\Console\Command;
use Monero\Transaction;
use Monero\Wallet;
use Monero\WalletOld;
class walletnotify extends Command
class walletNotify extends Command
{
/**
* The name and signature of the console command.
......@@ -41,7 +41,7 @@ class walletnotify extends Command
*/
public function handle()
{
$wallet = new Wallet();
$wallet = new WalletOld();
$blockheight = $wallet->blockHeight();
if ($blockheight < 1) {
......@@ -135,7 +135,7 @@ class walletnotify extends Command
*
* @param Transaction $transaction
*
* @return bool
* @return Deposit
*/
public function createDeposit(Transaction $transaction)
{
......
......@@ -51,10 +51,6 @@ class Project extends Model
return $this->deposits->sum('amount');
}
public function getUriAttribute() {
return 'monero:'.env('WALLET_ADDRESS').'tx_payment_id='.$this->payment_id;
}
public function getPercentageFundedAttribute() {
return round($this->amount_received / $this->target_amount * 100);
}
......@@ -63,7 +59,12 @@ class Project extends Model
return $this->deposits->count() ?? 0;
}
public function getQrcodeAttribute() {
public function generateQrcode() {
return QrCode::format('png')->size(500)->generate($this->uri);
}
public function getQrCodeSrcAttribute() {
$encoded = base64_encode($this->generateQrcode());
return "data:image/png;base64, {$encoded}";
}
}
......@@ -8,8 +8,7 @@
"php": "^7.1.3",
"ext-curl": "*",
"ext-json": "*",
"barryvdh/laravel-ide-helper": "^2.5",
"doctrine/dbal": "^2.8",
"ext-openssl": "*",
"fideloper/proxy": "^4.0",
"guzzlehttp/guzzle": "^6.3",
"laravel/framework": "5.7.*",
......@@ -17,6 +16,8 @@
"simplesoftwareio/simple-qrcode": "2.0.*"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^2.5",
"doctrine/dbal": "^2.8",
"beyondcode/laravel-dump-server": "^1.0",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
......
This diff is collapsed.
......@@ -8,7 +8,11 @@ $factory->define(\App\Project::class, function (Faker $faker) {
return [
'title' => $faker->sentence(),
'payment_id' => $faker->sha256,
'address' => $faker->sha256,
'address_uri' => "monero:{$faker->sha256}",
'qr_code' => $faker->file(),
'target_amount' => $faker->randomFloat(2, 0, 2000),
'raised_amount' => $faker->randomFloat(2, 0, 2000),
'state' => $state,
'merge_request_id' => $faker->randomNumber(6),
'gitlab_username' => $faker->userName,
......
......@@ -18,7 +18,10 @@ class CreateProjectsTable extends Migration
$table->string('title');
$table->string('payment_id')->nullable();
$table->string('address')->nullable();
$table->string('address_uri')->nullable();
$table->string('qr_code')->nullable();
$table->string('target_amount')->nullable();
$table->string('raised_amount')->nullable();
$table->string('state')->default('OPENED');
$table->string('filename')->nullable();
$table->unsignedInteger('merge_request_id')->unique();
......
<?php
namespace Monero\Contracts;
/**
* Interface WalletManager
* @package Monero
*/
interface WalletManager
{
/**
* Gets the balance
*
* @return int the overall value after inputs unlock
*/
public function balance();
/**
* Gets the unlocked balance
*
* @return int the spendable balance
*/
public function unlockedBalance();
/**
* Gets the primary address
*
* @return string wallets primary address
*/
public function address();
/**
* Gets the current block height
*
* @return int block height
*/
public function blockHeight();
/**
* Creates a new integrated address
*
* @return array ['integrated_address', 'payment_id']
*/
public function createIntegratedAddress();
/**
* Gets any incoming transactions
*
* @return array
*/
public function incomingTransfers();
/**
* Checks for any payments made to the paymentIds
*
* @param array $paymentIds list of payment ids to be searched for
* @param int $minHeight the lowest block the search should start with
*
* @return array payments received since min block height with a payment id provided
*/
public function payments($paymentIds, $minHeight);
/**
* creates a uri for easier wallet parsing
*
* @param string $address address comprising of primary, sub or integrated address
* @param string $paymentId payment id when not using integrated addresses
* @param int $amount atomic amount requested
*
* @return string the uri string which can be used to generate a QR code
*/
public function createUri($address, $paymentId = null, $amount = null);
/**
* creates a random 64 char payment id
*
* @return string
*/
public function generatePaymentId();
}
<?php
namespace Monero;
use App\Project;
use Carbon\Carbon;
use Illuminate\Support\Collection;
class WalletOld
{
/**
* WalletOld constructor.
*
* @param null $client
*/
public function __construct($client = null)
{
$this->client = $client ?: new jsonRPCClient(env('RPC_URL'));
}
/**
* Gets a Payment address for receiving payments
*
* @return array
*
* @internal param \WalletOld $wallet
*/
public function getPaymentAddress()
{
$integratedAddress = $this->createIntegratedAddress();
if (!$integratedAddress) {
return ['address' => 'not valid'];
}
return ['address' => $integratedAddress['integrated_address'], 'paymentId' => $integratedAddress['payment_id']];
}
/**
* Returns the actual available and useable balance (unlocked balance)
*
* @return float|int|mixed
*/
public function balance()
{
return $this->client->balance();
}
public function mempoolTransfers()
{
return $this->client->incomingTransfers();
}
public function bulkPayments($paymentIds)
{
$blockBuffer = 10;
return $this->client->payments($paymentIds, intval($this->wallet->last_scanned_block_height) - $blockBuffer);
}
/**
* Scans the monero blockchain for transactions for the payment ids
*
* @param $blockheight
* @param $paymentIDs
*
* @return array|Transaction
*/
public function scanBlocks($blockheight, $paymentIDs)
{
$response = $this->bulkPayments($paymentIDs);
$address = $this->getAddress();
$transactions = [];
if ($response && isset($response['payments'])) {
foreach ($response['payments'] as $payment) {
$transaction = new Transaction(
$payment['tx_hash'],
$payment['amount'],
$address,
$blockheight - $payment['block_height'],
0,
Carbon::now(),
$payment['payment_id'],
$payment['block_height']
);
$transactions[] = $transaction;
}
}
return collect($transactions);
}
/**
* @param $blockheight
*
* @return \Illuminate\Support\Collection
*/
public function scanMempool($blockheight)
{
$address = $this->getAddress();
$transactions = [];
$response = $this->mempoolTransfers();
if ($response && isset($response['pool'])) {
foreach ($response['pool'] as $payment) {
$transaction = new Transaction(
$payment['txid'],
$payment['amount'],
$address,
0,
0,
Carbon::now(),
$payment['payment_id'],
$blockheight
);
$transactions[] = $transaction;
}
}
return collect($transactions);
}
/**
* Gets the current blockheight of xmr
*
* @return int
*/
public function blockHeight()
{
return $this->client->blockHeight();
}
/**
* Returns monero wallet address
*
* @return string
*/
public function getAddress()
{
return $this->client->address();
}
/**
* Returns XMR integrated address
*
* @return mixed
*/
public function createIntegratedAddress()
{
return $this->client->createIntegratedAddress();
}
/**
* @param $amount
* @param $address
* @param $paymentId
*
* @return string
*/
public function createQrCodeString($address, $amount = null, $paymentId = null): string
{
return $this->client->createUri($address, $amount, $paymentId);
}
/**
* gets all the payment_ids outstanding from the address_pool, we use these to check against the latest mined blocks
*
* @return Collection
*/
public function getPaymentIds()
{
return Project::pluck('payment_id'); //stop scanning for payment_ids after 24h
}
}
......@@ -10,31 +10,41 @@ use Illuminate\Support\Facades\Log;
* Class jsonRPCClient
* JSON 2.0 RPC Client for cryptocurrency wallet
*/
class jsonRPCClient
class jsonRPCClient implements Contracts\WalletManager
{
/** @var string */
private $username;
private $username = 'test2';
/** @var string */
private $password;
private $password = 'test2';
/** @var string */
private $url = 'http://127.0.0.1:28080/json_rpc';
/** @var Client|null */
private $client;
/**
* JsonRPCClient constructor.
* @param array $options
* @param null $client
*/
public function __construct($client = null)
public function __construct($options, $client = null)
{
$this->username = $options['username'] ?? $this->username;
$this->password = $options['password'] ?? $this->password;
$this->url = $options['url'] ?? $this->url;
if (empty($client)) {
$client = new Client([
'base_uri' => env('RPC_URL'),
'base_uri' => $this->url,
'headers' => [
'Content-Type' => 'application/json',
]
]);
}
$this->username = env('MONERO_USERNAME');
$this->password = env('MONERO_PASSWORD');
$this->client = $client;
}
......@@ -135,6 +145,16 @@ class jsonRPCClient
return $response['uri'];
}
/**
* creates a random 64 char payment id
*
* @return string
*/
public function generatePaymentId(): string
{
return bin2hex(openssl_random_pseudo_bytes(32));
}
/**
* Sets up the request data body
*
......@@ -155,6 +175,7 @@ class jsonRPCClient
}
/**
* Send off request to rpc server
*
* @param string $method name of the rpc command
* @param array $params associative array of variables being passed to the method
......
server {
listen 80;
listen [::]:80;
root /var/www/moneroffs/public;
index index.php index.html index.htm;
server_name moneroffs.test;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
\ No newline at end of file
This diff is collapsed.
......@@ -106,7 +106,7 @@
<div class="row middle-xs between-xs">
<p class="author-list"><span><img src="/img/author-filled.png"></span>{{$project->gitlab_username}}</p>
<p class="date-list"><span><img src="/img/calendar.png"></span>{{$project->github_created_at}}</p>
<p class="bar-fund-status">Raised <span class="progress-number-funded">{{$project->target_amount}}</span> of <span class="progress-number-goal">[AMOUNT RAISED HERE]</span> XMR</p>
<p class="bar-fund-status">Raised <span class="progress-number-funded">{{$project->raised_amount}}</span> of <span class="progress-number-goal">{{$project->target_amount}}</span> XMR</p>
</div>
<div class="progress-bar">
<span class="fund-progress" style="width: [PERCENTAGE HERE]%"></span>
......@@ -129,7 +129,7 @@
<p>1. Choose the amount of XMR you wish to contribute to this proposal</p>
<p>2. Scan this QR code or tap to open in your Monero wallet app:</p>
<p>
<a href="[MONERO LINK HERE]" class="qr"><img src="[PATH TO QR CODE HERE]"/></a>
<a href="{{$project->address_uri}}" class="qr"><img src="{{ $project->qrCodeSrc}}"/></a>
</p>
<p>3. Send! Thank you! You are amazing!</p>
</div>
......