Commit 41ae7d2a authored by Emiliano Balbuena's avatar Emiliano Balbuena

(wip): Onchain payments

1 merge request!235WIP: Boost Campaigns (&24)
Pipeline #69968069 running with stages
......@@ -10,6 +10,7 @@ namespace Minds\Core\Blockchain;
use Minds\Core\Blockchain\Events\BlockchainEventInterface;
use Minds\Core\Blockchain\Events\BoostEvent;
use Minds\Core\Blockchain\Events\TokenEvent;
use Minds\Core\Blockchain\Events\TokenSaleEvent;
use Minds\Core\Blockchain\Events\WireEvent;
use Minds\Core\Blockchain\Events\WithdrawEvent;
......@@ -20,9 +21,10 @@ class Events
{
protected static $handlers = [
TokenSaleEvent::class,
TokenEvent::class,
WireEvent::class,
BoostEvent::class,
WithdrawEvent::class
WithdrawEvent::class,
];
public function register()
......
<?php
/**
* TokenEvent
* @author edgebal
*/
namespace Minds\Core\Blockchain\Events;
use Exception;
use Minds\Core\Blockchain\Transactions\Transaction;
use Minds\Core\Blockchain\Util;
use Minds\Core\Boost\Campaigns\Manager as BoostCampaignsManager;
use Minds\Core\Boost\Campaigns\Payments\Payment;
use Minds\Core\Config;
use Minds\Core\Di\Di;
use Minds\Core\Util\BigNumber;
class TokenEvent implements BlockchainEventInterface
{
/** @var array */
public static $eventsMap = [
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' => 'tokenTransfer',
'blockchain:fail' => 'tokenFail',
];
/** @var Config */
protected $config;
/** @var BoostCampaignsManager */
protected $boostCampaignsManager;
/**
* TokenEvent constructor.
* @param Config $config
* @param BoostCampaignsManager $boostCampaignsManager
*/
public function __construct(
$config = null,
$boostCampaignsManager = null
)
{
$this->config = $config ?: Di::_()->get('Config');
$this->boostCampaignsManager = $boostCampaignsManager ?: Di::_()->get('Boost\Campaigns\Manager');
}
/**
* @return array
*/
public function getTopics()
{
return array_keys(static::$eventsMap);
}
/**
* @param $topic
* @param array $log
* @param Transaction $transaction
* @return void
* @throws Exception
*/
public function event($topic, array $log, $transaction)
{
$method = static::$eventsMap[$topic];
if ($log['address'] != $this->config->get('blockchain')['token_address']) {
throw new Exception('Event does not match address');
}
if (method_exists($this, $method)) {
$this->{$method}($log, $transaction);
} else {
throw new Exception('Method not found');
}
}
/**
* @param array $log
* @param Transaction $transaction
* @throws Exception
*/
public function tokenTransfer($log, $transaction)
{
list($amount) = Util::parseData($log['data'], [Util::NUMBER]);
list($destination) = Util::parseData($log['topics'][2], [Util::ADDRESS]);
$data = $transaction->getData();
if (!$destination) {
throw new Exception('Invalid transfer destination');
}
switch ($transaction->getContract()) {
case 'boost_campaign':
$wallet = $this->config->get('blockchain')['contracts']['boost_campaigns']['wallet_address'] ?? null;
if ($destination != $wallet) {
throw new Exception('Invalid Boost Campaign wallet address');
}
$payment = new Payment();
$payment
->setOwnerGuid($data['payment']['owner_guid'])
->setCampaignGuid($data['payment']['campaign_guid'])
->setTx($data['payment']['tx'])
->setAmount(BigNumber::fromPlain(BigNumber::fromHex($amount), 18)->toDouble());
$this->boostCampaignsManager->onPaymentSuccess($payment);
break;
}
}
/**
* @param array $log
* @param Transaction $transaction
* @throws Exception
*/
public function tokenFail($log, $transaction)
{
list($amount) = Util::parseData($log['data'], [Util::NUMBER]);
list($destination) = Util::parseData($log['topics'][2], [Util::ADDRESS]);
$data = $transaction->getData();
if (!$destination) {
throw new Exception('Invalid transfer destination');
}
switch ($transaction->getContract()) {
case 'boost_campaign':
$wallet = $this->config->get('blockchain')['contracts']['boost_campaigns']['wallet_address'] ?? null;
if ($destination != $wallet) {
throw new Exception('Invalid Boost Campaign wallet address');
}
$payment = new Payment();
$payment
->setOwnerGuid($data['payment']['owner_guid'])
->setCampaignGuid($data['payment']['campaign_guid'])
->setTx($data['payment']['tx'])
->setAmount(BigNumber::fromPlain(BigNumber::fromHex($amount), 18)->toDouble());
$this->boostCampaignsManager->onPaymentFailed($payment);
break;
}
}
}
......@@ -154,7 +154,7 @@ class Manager
(new $topicHandlerClass())->event($topic, $log, $transaction);
error_log("Tx[{$this->tx}][{$topicHandlerClass}] {$topic}... OK!");
} catch (\Exception $e) {
error_log("Tx[{$this->tx}][{$topicHandlerClass}] {$topic} threw " . get_class($e) . ": {$e->getMessage()}");
error_log("Tx[{$this->tx}][{$topicHandlerClass}] {$topic} threw {$e}");
}
}
}
......@@ -182,4 +182,14 @@ class Manager
]);
}
/**
* @param $tx
* @return int
* @throws \Exception
*/
public function exists($tx)
{
return $this->repo->exists($tx);
}
}
......@@ -2,8 +2,6 @@
namespace Minds\Core\Boost;
use Minds\Core\Boost\Campaigns\Manager as CampaignsManager;
use Minds\Core\Boost\Campaigns\Repository as CampaignsRepository;
use Minds\Core\Boost\Network;
use Minds\Core\Data;
use Minds\Core\Data\Client;
......@@ -62,10 +60,10 @@ class BoostProvider extends Provider
return new Payment();
}, ['useFactory' => true]);
$this->di->bind('Boost\Campaigns\Manager', function ($di) {
return new CampaignsManager();
return new Campaigns\Manager();
}, ['useFactory' => true]);
$this->di->bind('Boost\Campaigns\Repository', function ($di) {
return new CampaignsRepository();
return new Campaigns\Repository();
}, ['useFactory' => true]);
}
......
......@@ -44,7 +44,7 @@ class PaymentsDelegate
*/
public function onCreate(Campaign $campaign, $paymentPayload = null)
{
// NOTE: Do not spec test
// NOTE: Do not spec test. Individually test the other methods.
$this->validateBudget($campaign);
$this->registerCampaignPayment($campaign, $paymentPayload);
......@@ -63,7 +63,7 @@ class PaymentsDelegate
*/
public function onUpdate(Campaign $campaign, Campaign $campaignRef, $paymentPayload = null)
{
// NOTE: Do not spec test
// NOTE: Do not spec test. Individually test the other methods.
$campaignRef
->setBudgetType($campaign->getBudgetType());
......@@ -82,7 +82,7 @@ class PaymentsDelegate
*/
public function onStateChange(Campaign $campaign)
{
// NOTE: Do not spec test
// NOTE: Do not spec test. Individually test the other methods.
// TODO: Check that campaign is in a final complete or incomplete status (revoked/rejected)
// TODO: Refund!
......
......@@ -9,6 +9,7 @@ namespace Minds\Core\Boost\Campaigns;
use Exception;
use Minds\Common\Repository\Response;
use Minds\Common\Urn;
use Minds\Core\Boost\Campaigns\Payments\Payment;
use Minds\Core\Boost\Campaigns\Payments\Repository as PaymentsRepository;
use Minds\Entities\User;
......@@ -182,11 +183,6 @@ class Manager
$campaign = $this->normalizeEntityUrnsDelegate->onCreate($campaign);
$campaign = $this->normalizeHashtagsDelegate->onCreate($campaign);
// Add
$campaign
->setCreatedTimestamp(time() * 1000);
// Run payments delegate (should be ALWAYS called after normalizing dates)
$campaign = $this->paymentsDelegate->onCreate($campaign, $paymentPayload);
......@@ -436,4 +432,20 @@ class Manager
return $campaign;
}
/**
* @param Payment $paymentRef
*/
public function onPaymentSuccess(Payment $paymentRef)
{
}
/**
* @param Payment $paymentRef
*/
public function onPaymentFailed(Payment $paymentRef)
{
error_log(print_r(['FAIL!', $paymentRef], true));
}
}
......@@ -8,7 +8,7 @@ namespace Minds\Core\Boost\Campaigns\Payments;
use Exception;
use Minds\Core\Blockchain\Services\Ethereum;
use Minds\Core\Blockchain\Transactions\Repository as TransactionsRepository;
use Minds\Core\Blockchain\Transactions\Manager as TransactionsManager;
use Minds\Core\Blockchain\Transactions\Transaction;
use Minds\Core\Boost\Campaigns\Campaign;
use Minds\Core\Config;
......@@ -26,20 +26,20 @@ class Onchain
/** @var Repository */
protected $repository;
/** @var TransactionsRepository */
protected $txRepository;
/** @var TransactionsManager */
protected $txManager;
public function __construct(
$config = null,
$eth = null,
$repository = null,
$txRepository = null
$txManager = null
)
{
$this->config = $config ?: Di::_()->get('Config');
$this->eth = $eth ?: Di::_()->get('Blockchain\Services\Ethereum');
$this->repository = $repository ?: new Repository();
$this->txRepository = $txRepository ?: Di::_()->get('Blockchain\Transactions\Repository');
$this->txManager = $txManager ?: Di::_()->get('Blockchain\Transactions\Manager');
}
/**
......@@ -49,7 +49,7 @@ class Onchain
*/
public function register(Payment $payment)
{
if ($this->txRepository->exists($payment->getTx())) {
if ($this->txManager->exists($payment->getTx())) {
throw new Exception('Payment transaction already exists');
}
......@@ -61,44 +61,13 @@ class Onchain
->setWalletAddress($payment->getSource())
->setTimestamp($payment->getTimeCreated())
->setUserGuid($payment->getOwnerGuid())
->setData([]);
->setData([
'payment' => $payment->export(),
]);
$this->repository->add($payment);
$this->txRepository->add($transaction);
$this->txManager->add($transaction);
return true;
}
// public function validate($from, $tx)
// {
// $tokenAddress = $this->config->get('blockchain')['token_address'] ?? null;
// $boostCampaignWalletAddress = $this->config->get('blockchain')['contracts']['boost_campaigns']['wallet_address'] ?? null;
//
// if (!$tokenAddress || !$boostCampaignWalletAddress) {
// throw new Exception('Missing token or boost campaign wallet addresses');
// }
//
// $receipt = $this->eth->request('eth_getTransactionReceipt', [ $tx ]);
//
// // TODO: Skip failed transactions
//
// // TODO: Review this
// $destinationAddress = sprintf("0x%s", substr($receipt['logs'][0]['topics'][2] ?? '', -40));
//
// // TODO: Review this
// $hexWei = ltrim(str_replace('0x', '', $receipt['logs'][0]['data'] ?? ''), '0');
// $amount = BigNumber::fromPlain(BigNumber::fromHex($hexWei), 18)->toDouble();
//
// if (!$receipt) {
// throw new Exception("Invalid payment: {$tx}");
// } elseif (strtolower($receipt['from']) !== strtolower($from)) {
// throw new Exception("Invalid payment source for {$tx}");
// } elseif (strtolower($receipt['to']) !== strtolower($tokenAddress)) {
// throw new Exception("Invalid token address for {$tx}");
// } elseif (strtolower($destinationAddress) !== strtolower($boostCampaignWalletAddress)) {
// throw new Exception("Invalid boost campaign address for {$tx}");
// }
//
// return $amount;
// }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment