Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,17 @@ MODULE_litecoin-mweb_NODES[]=http://login:password@127.0.0.2:1234/
MODULE_litecoin-mweb_REQUESTER_TIMEOUT=60
MODULE_litecoin-mweb_REQUESTER_THREADS=12

##########################
## Minimal Polkadot Module
##########################

MODULES[]=polkadot-minimal
MODULE_polkadot-minimal_CLASS=PolkadotMinimalModule
MODULE_polkadot-minimal_NODES[]=http://login:password@127.0.0.1:1234/
MODULE_polkadot-minimal_NODES[]=http://login:password@127.0.0.2:1234/
MODULE_polkadot-minimal_REQUESTER_TIMEOUT=60
MODULE_polkadot-minimal_REQUESTER_THREADS=12

######################
## Main Polygon Module
######################
Expand Down Expand Up @@ -445,6 +456,7 @@ FRONT_bitcoin-cash_ECOSYSTEM_TITLE=Bitcoin Cash Ecosystem
FRONT_polygon_ECOSYSTEM_TITLE=Polygon Ecosystem
FRONT_zcash_ECOSYSTEM_TITLE=Zcash Ecosystem
FRONT_cardano_ECOSYSTEM_TITLE=Cardano Ecosystem
FRONT_polkadot_ECOSYSTEM_TITLE=Polkadot Ecosystem
FRONT_dash_ECOSYSTEM_TITLE=Dash Ecosystem
FRONT_handshake_ECOSYSTEM_TITLE=Handshake Ecosystem
FRONT_polygon-zkevm_ECOSYSTEM_TITLE=Polygon zkEVM Ecosystem
Expand All @@ -459,6 +471,7 @@ FRONT_bitcoin-cash_ECOSYSTEM_DESCRIPTION=Includes Bitcoin Cash only
FRONT_polygon_ECOSYSTEM_DESCRIPTION=Includes Polygon only
FRONT_zcash_ECOSYSTEM_DESCRIPTION=Includes Zcash only
FRONT_cardano_ECOSYSTEM_DESCRIPTION=Includes Cardano only
FRONT_polkadot_ECOSYSTEM_DESCRIPTION=Includes Polkadot only
FRONT_dash_ECOSYSTEM_DESCRIPTION=Includes Dash only
FRONT_handshake_ECOSYSTEM_DESCRIPTION=Includes Handshake only
FRONT_polygon-zkevm_ECOSYSTEM_DESCRIPTION=Includes Polygon zkEVM only
Expand All @@ -473,6 +486,7 @@ FRONT_bitcoin-cash_BLOCKCHAIN_TITLE=Bitcoin Cash
FRONT_polygon_BLOCKCHAIN_TITLE=Polygon
FRONT_zcash_BLOCKCHAIN_TITLE=Zcash
FRONT_cardano_BLOCKCHAIN_TITLE=Cardano
FRONT_polkadot_BLOCKCHAIN_TITLE=Polkadot
FRONT_dash_BLOCKCHAIN_TITLE=Dash
FRONT_handshake_BLOCKCHAIN_TITLE=Handshake
FRONT_polygon-zkevm_BLOCKCHAIN_TITLE=Polygon zkEVM
Expand All @@ -487,6 +501,7 @@ FRONT_bitcoin-cash_BLOCKCHAIN_DESCRIPTION=The first Bitcoin fork
FRONT_polygon_BLOCKCHAIN_DESCRIPTION=An EVM chain
FRONT_zcash_BLOCKCHAIN_DESCRIPTION=Supports transparent, Sprout, Sapling, and Orchard shielded pools
FRONT_cardano_BLOCKCHAIN_DESCRIPTION=Cardano
FRONT_polkadot_BLOCKCHAIN_DESCRIPTION=Polkadot
FRONT_dash_BLOCKCHAIN_DESCRIPTION=Dash
FRONT_handshake_BLOCKCHAIN_DESCRIPTION=Handshake
FRONT_polygon-zkevm_BLOCKCHAIN_DESCRIPTION=Polygon zkEVM
Expand Down Expand Up @@ -517,6 +532,7 @@ FRONT_polygon-erc-721_MODULE_TITLE=ERC-721
FRONT_polygon-erc-1155_MODULE_TITLE=ERC-1155
FRONT_zcash-main_MODULE_TITLE=Main
FRONT_cardano-main_MODULE_TITLE=Main
FRONT_polkadot-minimal_MODULE_TITLE=Minimal
FRONT_dash-main_MODULE_TITLE=Main
FRONT_dash-dip-2_MODULE_TITLE=DIP-2
FRONT_handshake-main_MODULE_TITLE=Main
Expand Down Expand Up @@ -552,6 +568,7 @@ FRONT_polygon-erc-721_MODULE_DESCRIPTION=ERC-721 token transfers
FRONT_polygon-erc-1155_MODULE_DESCRIPTION=ERC-1155 token transfers
FRONT_zcash-main_MODULE_DESCRIPTION=Supports transparent, Sprout, Sapling, and Orchard shielded pools
FRONT_cardano-main_MODULE_DESCRIPTION=Main Cardano transfers (UTXO)
FRONT_polkadot-minimal_MODULE_DESCRIPTION=Polkadot transfers between accounts
FRONT_dash-main_MODULE_DESCRIPTION=Main Dash transfers
FRONT_dash-dip-2_MODULE_DESCRIPTION=DIP-2 special transfers
FRONT_handshake-main_MODULE_DESCRIPTION=Main Handshake transfers
Expand Down
243 changes: 243 additions & 0 deletions Modules/Common/PolkadotLikeMinimalModule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
<?php declare(strict_types = 1);

/* Copyright (c) 2023 Nikita Zhavoronkov, nikzh@nikzh.com
* Copyright (c) 2023 3xpl developers, 3@3xpl.com
* Distributed under the MIT software license, see the accompanying file LICENSE.md */

/* This module processes some basic Polkadot transfers. It requires `subscan-explorer/subscan-essentials` to operate,
* see https://github.com/subscan-explorer/subscan-essentials/blob/master/docs/index.md for details. Please note that
* Polkadot has extrinsics instead of transactions, and not of them have "transaction hashes". Generally, this module
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix typo pls: "... not all of them..."

* is still WIP and requires many improvements, thus it's called "minimal" instead of "main". */

abstract class PolkadotLikeMinimalModule extends CoreModule
{
use UTXOTraits;

public ?BlockHashFormat $block_hash_format = BlockHashFormat::HexWith0x;
public ?AddressFormat $address_format = AddressFormat::AlphaNumeric;
public ?TransactionHashFormat $transaction_hash_format = TransactionHashFormat::HexWith0x; // We process transactions with
// transfers only, so we don't take extrinsics without hashes into account
public ?TransactionRenderModel $transaction_render_model = TransactionRenderModel::Even;
public ?CurrencyFormat $currency_format = CurrencyFormat::Static;
public ?CurrencyType $currency_type = CurrencyType::FT;
public ?FeeRenderModel $fee_render_model = FeeRenderModel::ExtraF;
public ?bool $hidden_values_only = false;

public ?array $events_table_fields = ['block', 'transaction', 'sort_key', 'time', 'address', 'effect', 'failed', 'extra'];
public ?array $events_table_nullable_fields = ['extra'];

public ?ExtraDataModel $extra_data_model = ExtraDataModel::Default;

public ?bool $should_return_events = true;
public ?bool $should_return_currencies = false;
public ?bool $allow_empty_return_events = true;

public ?bool $mempool_implemented = false;
public ?bool $forking_implemented = true;

//

private ?array $block_data = null;

//

final public function pre_initialize()
{
$this->version = 1;
}

final public function post_post_initialize()
{
//
}

final public function inquire_latest_block()
{
return (int)requester_single($this->select_node(),
endpoint: 'api/scan/metadata',
params: true,
result_in: 'data',
timeout: $this->timeout)['blockNum'];
}

final public function ensure_block($block_id, $break_on_first = false)
{
$this->block_data = requester_single($this->select_node(),
endpoint: 'api/scan/block',
params: ['block_num' => $block_id],
result_in: 'data',
timeout: $this->timeout);

$this->block_hash = $this->block_data['hash'];
$this->block_time = date('Y-m-d H:i:s', (int)$this->block_data['block_timestamp']);
}

final public function pre_process_block($block_id)
{
if ($block_id === 0)
{
$this->set_return_events([]);
return;
}

$extrinsics = $extrinsics_with_hashes = [];

foreach ($this->block_data['extrinsics'] as $extrinsic)
if ($extrinsic['extrinsic_hash'])
$extrinsics_with_hashes[] = $extrinsic['extrinsic_hash'];

if (!$extrinsics_with_hashes)
{
$this->set_return_events([]);
return;
}

$events = [];
$multi_curl = [];

foreach ($extrinsics_with_hashes as $extrinsic_hash)
$multi_curl[] = requester_multi_prepare($this->select_node(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, this is the wrong way, as the hash depends on extrisics body, i.e. there can be many extrinsics with the same hash.
Why don't you use block_id-extrinsic_id,i.e. extrinsic_index.
Also subscan API returns a single extrinsic for the specified hash, returning the array of extrinsics for the hash was one of our customization.

Anyway using current strategy with hash leads to reprocess extrinsics, that were already processed.
Use extrinsic_index instead of hash, as it's a unique key

endpoint: 'api/scan/extrinsic',
params: ['hash' => $extrinsic_hash],
timeout: $this->timeout);

$curl_results = requester_multi($multi_curl, limit: envm($this->module, 'REQUESTER_THREADS'), timeout: $this->timeout);

foreach ($curl_results as $v)
{
$extrinsics[] = requester_multi_process($v, result_in: 'data');
}

$transfers = [];

foreach ($extrinsics as $i)
{
$this_i = $fee = null;

if (count($i) !== 1)
{
$found = false;
foreach ($i as $j => $k)
{
if ((int)$k['block_num'] === $block_id)
{
if ($found)
throw new ModuleError('Two extrinsics with the same hash in one block');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not an error, this is a documented behaviour for polkadot, there's no need for the logging this as error.


$found = true;
$this_i = $j;
}
}
}
else
{
$this_i = 0;
}

if (!isset($this_i))
continue; // Block 12831012 contains 0xa36adc0ddedb4b9f9f8de5ef59fa6c99bbb8ca8bbc44d189dd0bedf6bf61b92e
// which is present in 12857725, 12856380, 12855736, and many more, but not in 12831012 ¯\_(ツ)_/¯

if (!$i[$this_i]['transfers'])
continue;

$initiating_address = $i[$this_i]['account_id'];

if ($initiating_address === '') // This is a bit strange
$initiating_address = $i[$this_i]['transfers'][0]['from'];

$fee_found = false;
$extrinsic_failed = $i[$this_i]['failed'];

foreach ($i[$this_i]['event'] as $event)
{
if ($event['event_id'] === 'TransactionFeePaid')
{
if ($fee_found)
throw new ModuleError('Two fees');

$fee_data = json_decode($event['params'], true);

if (count($fee_data) !== 3)
throw new ModuleError('Wrong fee data');
if ($fee_data[1]['type_name'] !== 'BalanceOf')
throw new ModuleError('Wrong fee data');

$fee = $fee_data[1]['value'];
$fee_found = true;
$fee_idx = $event['event_idx'];
}
}

if (!$fee_found) // Uh-oh
{
$fee = $i[$this_i]['fee'];
$fee_idx = $i[$this_i]['event'][0]['event_idx'];
}

foreach ($i[$this_i]['transfers'] as $this_transfer)
{
if ((int)$this_transfer['block_num'] !== $block_id)
throw new ModuleError('Wrong block');

if ($extrinsic_failed && !$this_transfer['failed'])
throw new ModuleError('Not failed');

$transfers[] =
['index' => $this_transfer['event_idx'],
'hash' => $this_transfer['hash'],
'from' => $this_transfer['from'],
'to' => $this_transfer['to'],
'amount' => $this_transfer['amount'],
'failed' => $this_transfer['failed'],
'type' => 'transfer'
];
}

$transfers[] =
['index' => $fee_idx,
'hash' => $this_transfer['hash'],
'from' => $initiating_address,
'to' => 'the-void',
'amount' => $fee,
'failed' => false,
'type' => 'fee'
];
}

usort($transfers, function($a, $b) {
return [$a['index']] <=> [$b['index']];
});

$sort_key = 0;

foreach ($transfers as $transfer)
{
$events[] = [
'transaction' => $transfer['hash'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, this is not a unique identifier, use block_number-extrinsic_number instead, it can also be interpreted as a string.

'address' => $transfer['from'],
'sort_key' => $sort_key++,
'effect' => '-' . $transfer['amount'],
'failed' => $transfer['failed'],
'extra' => ($transfer['type'] === 'fee') ? 'f' : null,
];

$events[] = [
'transaction' => $transfer['hash'],
'address' => $transfer['to'],
'sort_key' => $sort_key++,
'effect' => $transfer['amount'],
'failed' => $transfer['failed'],
'extra' => ($transfer['type'] === 'fee') ? 'f' : null,
];
}

foreach ($events as &$event)
{
$event['block'] = $block_id;
$event['time'] = $this->block_time;
}

$this->set_return_events($events);
}
}
22 changes: 22 additions & 0 deletions Modules/PolkadotMinimalModule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php declare(strict_types = 1);

/* Copyright (c) 2023 Nikita Zhavoronkov, nikzh@nikzh.com
* Copyright (c) 2023 3xpl developers, 3@3xpl.com
* Distributed under the MIT software license, see the accompanying file LICENSE.md */

/* This is a module for Polkadot. See PolkadotLikeMinimalModule.php for details. */

final class PolkadotMinimalModule extends PolkadotLikeMinimalModule implements Module
{
function initialize()
{
// CoreModule
$this->blockchain = 'polkadot';
$this->module = 'polkadot-minimal';
$this->is_main = true;
$this->currency = 'polkadot';
$this->currency_details = ['name' => 'DOT', 'symbol' => 'DOT', 'decimals' => 10, 'description' => null];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to keep a comment about redenomination on block 1 248 328

$this->first_block_date = '2020-05-26';
$this->first_block_id = 0;
}
}