diff --git a/.env.example b/.env.example index 98102f8..423ce7c 100644 --- a/.env.example +++ b/.env.example @@ -875,6 +875,18 @@ MODULE_rootstock-erc-1155_NODES[]=http://login:password@127.0.0.1:1234/ MODULE_rootstock-erc-1155_REQUESTER_TIMEOUT=60 MODULE_rootstock-erc-1155_REQUESTER_THREADS=12 +###################### +## Main Liquid Module +###################### + +MODULES[]=liquid-network-main +MODULE_liquid-network-main_CLASS=LiquidNetworkMainModule +MODULE_liquid-network-main_NODES[]=http://login:password@127.0.0.1:1234/ +MODULE_liquid-network-main_NODES[]=http://login:password@127.0.0.1:1234/ +MODULE_liquid-network-main_ASSETS_REGISTRY=http://login:password@127.0.0.1:12345/ +MODULE_liquid-network-main_REQUESTER_TIMEOUT=60 +MODULE_liquid-network-main_REQUESTER_THREADS=12 + ############################ # Titles, descriptions, etc. ############################ diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f041e1f..a8630df 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -11,4 +11,4 @@ * [Oleg Makaussov](https://github.com/Lorgansar) - Cardano Tokens modules * [Kirill Kuzminykh](https://github.com/Oskal174) - - Rootstock modules + - Rootstock modules, Liquid modules diff --git a/Modules/Common/UTXOLiquidModule.php b/Modules/Common/UTXOLiquidModule.php new file mode 100644 index 0000000..a8bf338 --- /dev/null +++ b/Modules/Common/UTXOLiquidModule.php @@ -0,0 +1,425 @@ + 'Peg-in transaction', + 'po' => 'Peg-out transaction', + ]; + + // For back compatibility with UTXO traits + public array $extra_features = []; + + // Liquid-specific + + public ?string $native_asset = null; + public ? array $native_asset_meta = null; + public ?string $asset_registry = null; + + // + + final public function pre_initialize() + { + $this->version = 1; + } + + final public function post_post_initialize() + { + if (is_null($this->native_asset)) + throw new DeveloperError("Native asset is not set"); + + if (is_null($this->native_asset_meta)) + throw new DeveloperError("Native asset meta is not set"); + + $this->asset_registry = envm( + $this->module, + 'ASSETS_REGISTRY', + new DeveloperError('ASSETS_REGISTRY not set in the env config') + ); + } + + final public function pre_process_block($block_id) + { + if ($block_id !== MEMPOOL) + { + $block_hash = $this->block_hash; + $block = requester_single($this->select_node(), endpoint: "rest/block/{$block_hash}.json", timeout: $this->timeout); + $this->block_time = date('Y-m-d H:i:s', (int)$block['time']); + } + else // Processing mempool + { + $block = []; + $block['tx'] = []; + $multi_curl = []; + + $mempool = requester_single($this->select_node(), params: ['method' => 'getrawmempool', 'params' => [false]], result_in: 'result', timeout: $this->timeout); + + $islice = 0; + + foreach ($mempool as $tx_hash) + { + if (!isset($this->processed_transactions[$tx_hash])) + { + $multi_curl[] = requester_multi_prepare($this->select_node(), + params: ['method' => 'getrawtransaction', 'params' => [$tx_hash, 1]], + timeout: $this->timeout); + + $islice++; + if ($islice >= 100) break; // For debug purposes, we limit the number of mempool transactions to process + } + } + + $curl_results = requester_multi($multi_curl, limit: envm($this->module, 'REQUESTER_THREADS'), timeout: $this->timeout); + + foreach ($curl_results as $v) + { + $block['tx'][] = requester_multi_process($v, result_in: 'result'); + } + } + + $events = []; + $currencies = []; + + $currencies_to_process = []; + + $previous_outputs_lib = []; + $populate_outputs_lib_with = []; + $GLOBALS['populate_outputs_lib_with_indexes'] = []; + $inputs_to_check = []; + $sort_in_block_lib = []; + $fees = []; + + $block_n = 0; + $coinbase_transaction_output = '0'; + + $this_is_coinbase = true; // Coinbase transaction is always the first one + if ($this->block_id === MEMPOOL) $this_is_coinbase = false; + + // Process outputs + + foreach ($block['tx'] as $transaction) + { + $previous_outputs_lib[($transaction['txid'])] = $transaction['vout']; + $fees[($transaction['txid'])] = ['amount' => '0', 'currency' => null]; + + foreach ($transaction['vout'] as $output) + { + if (isset($output['scriptPubKey']['address'])) + $address = $output['scriptPubKey']['address']; + else + $address = 'script-' . substr(hash('sha256', $output['scriptPubKey']['hex']), 0, 32); + + $extra = $extra_indexed = null; + + if (isset($output['scriptPubKey']['pegout_address'])) + { + $extra = 'po'; + $extra_indexed = $output['scriptPubKey']['pegout_address']; + } + + $asset = $output['asset'] ?? null; + $effect = isset($output['value']) ? satoshi($output['value'], $this) : '+?'; + $type = $output['scriptPubKey']['type'] ?? ''; + + if ($type === 'fee') // Skip additional event for fee out + { + if (is_null($asset)) + throw new ModuleError('Null asset for fee output type'); + + $fees[($transaction['txid'])]['amount'] = satoshi($output['value'], $this); + $fees[($transaction['txid'])]['currency'] = $asset; + } + else + { + $address = ($extra === 'po') ? 'the-bridge' : $address; + + $events[] = [ + 'transaction' => $transaction['txid'], + 'currency' => $asset, + 'address' => $address, + 'effect' => $effect, + 'sort_in_transaction' => ((int)$output['n'] + 1), + 'extra' => $extra, + 'extra_indexed' => $extra_indexed, + ]; + } + + if (!is_null($asset)) + $currencies_to_process[] = $asset; + + if ($this_is_coinbase) + $coinbase_transaction_output = bcsub($coinbase_transaction_output, satoshi($output['value'], $this)); + } + + $this_is_coinbase = false; + + $sort_in_block_lib[($transaction['txid'])] = $block_n; + $block_n++; + } + + // Process inputs + + foreach ($block['tx'] as $transaction) + { + $this_n = 1; + + foreach ($transaction['vin'] as $input) + { + if (isset($input['coinbase'])) + { + if ($coinbase_transaction_output === '0') $coinbase_transaction_output = '-0'; // E.g. block #501726 in Bitcoin + + $events[] = [ + 'transaction' => $transaction['txid'], + 'currency' => $this->native_asset, + 'address' => 'the-void', + 'effect' => $coinbase_transaction_output, + 'sort_in_transaction' => -1, + 'extra' => null, + 'extra_indexed' => null, + ]; + } + else + { + // In LiquidBitcoin we cant get the previous tx vouts for pegin txs + if ($input['is_pegin']) + { + // Calculate exact amount from bitcoin + $value = '0'; + + foreach ($transaction['vout'] as $out) + { + $type = $out['scriptPubKey']['type'] ?? ''; + + if (!isset($out['value'])) // Example: fd36f216be666d43ec861feb756b1c5f48fb54f98bfeed25e5367b05cccc96e8 + continue; + + if ($type !== 'fee') + $value = bcadd($value, satoshi($out['value'], $this)); + } + + // Set event for pegin and skip check for this input + $events[] = [ + 'transaction' => $transaction['txid'], + 'currency' => $this->native_asset, + 'address' => 'the-bridge', + 'effect' => '-' . $value, + 'sort_in_transaction' => -1, + 'extra' => 'pi', + 'extra_indexed' => $input['txid'], + ]; + + $currencies_to_process[] = $this->native_asset; + continue; + } + + if (!isset($previous_outputs_lib[($input['txid'])])) + { + $populate_outputs_lib_with[] = $input['txid']; + $GLOBALS['populate_outputs_lib_with_indexes'][$input['txid']][] = $input['vout']; + } + + $inputs_to_check[] = [ + 'this_transaction' => $transaction['txid'], + 'previous_transaction' => $input['txid'], + 'previous_n' => $input['vout'], + 'this_n' => -$this_n, + 'asset' => $input['asset'] ?? null, + ]; + + $this_n++; + } + } + } + + $multi_curl = []; + + $populate_outputs_lib_with = array_unique($populate_outputs_lib_with); + + // Get input data from the node + + foreach ($populate_outputs_lib_with as $tx_hash) + { + $multi_curl[] = requester_multi_prepare($this->select_node(), + params: ['method' => 'getrawtransaction', 'params' => [$tx_hash, 1]], + timeout: $this->timeout); + } + + $curl_results = requester_multi($multi_curl, + limit: envm($this->module, 'REQUESTER_THREADS'), + timeout: $this->timeout, post_process: function($output) { + $output = requester_multi_process($output, result_in: 'result'); + + foreach ($output['vout'] as $vok => $vov) + { + if (!in_array($vok, $GLOBALS['populate_outputs_lib_with_indexes'][$output['txid']])) + unset($output['vout'][$vok]); + } + + return ['txid' => $output['txid'], 'vout' => $output['vout']]; + }); + + foreach ($curl_results as $output) + { + $previous_outputs_lib[$output['txid']] = $output['vout']; + } + + foreach ($inputs_to_check as $input) + { + if (!isset($previous_outputs_lib[($input['previous_transaction'])])) + throw new ModuleError('Input is not in the library'); + + $previous_output = $previous_outputs_lib[($input['previous_transaction'])]; + + if (isset($previous_output[($input['previous_n'])]['scriptPubKey']['address'])) + $address = $previous_output[($input['previous_n'])]['scriptPubKey']['address']; + else + $address = 'script-' . substr(hash('sha256', $previous_output[($input['previous_n'])]['scriptPubKey']['hex']), 0, 32); + + $asset = $previous_output[($input['previous_n'])]['asset'] ?? null; + $effect = isset($previous_output[($input['previous_n'])]['value']) ? satoshi($previous_output[($input['previous_n'])]['value'], $this) : '?'; + + $events[] = [ + 'transaction' => $input['this_transaction'], + 'currency' => $asset, + 'address' => $address, + 'effect' => '-' . $effect, + 'sort_in_transaction' => (int)$input['this_n'], + 'extra' => null, + 'extra_indexed' => null, + ]; + + if (!is_null($asset)) + $currencies_to_process[] = $asset; + } + + foreach ($fees as $txid => $fee_transfer) + { + if ($fee_transfer['amount'] !== '0') + { + $events[] = [ + 'transaction' => $txid, + 'currency' => $fee_transfer['currency'], + 'address' => 'the-void', + 'effect' => $fee_transfer['amount'], + 'sort_in_transaction' => PHP_INT_MAX, + 'extra' => null, + 'extra_indexed' => null, + ]; + } + } + + // Process currencies + + $currencies_to_process = array_values(array_unique($currencies_to_process)); // Removing duplicates + $currencies_to_process = check_existing_currencies($currencies_to_process, $this->currency_format); // Removes already known currencies + + // Get metadata + foreach ($currencies_to_process as $currency) + { + try + { + if ($currency === $this->native_asset) + $meta = $this->native_asset_meta; + else + $meta = requester_single($this->asset_registry, endpoint: $currency, timeout: $this->timeout, valid_codes: [200, 404]); + } + catch (RequesterException $e) + { + // For unknown assets the registry returns an HTML page with 404 code + if (str_contains($e->getMessage(), 'bad JSON')) + $meta = [ + 'name' => null, + 'ticker' => null, + 'precision' => 0, + ]; + else + throw $e; + } + + $currencies[] = [ + 'id' => $currency, + 'name' => $meta['name'] ?? null, + 'symbol' => $meta['ticker'] ?? null, + 'decimals' => $meta['precision'] ?? 0, + ]; + } + + foreach ($events as &$event) + { + $event['block'] = $block_id; + $event['sort_in_block'] = $sort_in_block_lib[($event['transaction'])]; + $event['time'] = date('Y-m-d H:i:s', (($this->block_id !== MEMPOOL) ? (int)$block['time'] : time())); + } + + // Resort + + usort($events, function($a, $b) { + return [$a['sort_in_block'], + !str_starts_with($a['effect'], '-'), + abs($a['sort_in_transaction']), + ] + <=> + [$b['sort_in_block'], + !str_starts_with($b['effect'], '-'), + abs($b['sort_in_transaction']), + ]; + }); + + $sort_key = 0; + $latest_tx_hash = ''; // This is for mempool + + foreach ($events as &$event) + { + if ($this->block_id === MEMPOOL && $event['transaction'] !== $latest_tx_hash) + { + $latest_tx_hash = $event['transaction']; + $sort_key = 0; + } + + $event['sort_key'] = $sort_key++; + + unset($event['sort_in_block']); + unset($event['sort_in_transaction']); + } + + $this->set_return_events($events); + if ($this->block_id !== MEMPOOL) $this->set_return_currencies($currencies); + } +} diff --git a/Modules/LiquidNetworkMainModule.php b/Modules/LiquidNetworkMainModule.php new file mode 100644 index 0000000..29845ed --- /dev/null +++ b/Modules/LiquidNetworkMainModule.php @@ -0,0 +1,36 @@ +blockchain = 'liquid-network'; + $this->module = 'liquid-network-main'; + $this->is_main = true; + $this->first_block_date = '2018-09-27'; + + // Liquid-specific + $this->native_asset = '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d'; + $this->native_asset_meta = ['name' => 'Liquid Bitcoin', 'ticker' => 'L-BTC', 'precision' => 8]; + + // Tests + $this->tests = [ + // With a non-native asset + ['block' => 2774298, 'result' => 'a:2:{s:6:"events";a:8:{i:0;a:9:{s:11:"transaction";s:64:"f318eda796f0dce947fb18a21fc4cfeeaee7dedc83989e3721aa09c82760b32f";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:3:"-60";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2774298;s:4:"time";s:19:"2024-03-18 20:38:08";s:8:"sort_key";i:0;}i:1;a:9:{s:11:"transaction";s:64:"f318eda796f0dce947fb18a21fc4cfeeaee7dedc83989e3721aa09c82760b32f";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:39:"script-58f7fda4425dd61e07568702c58b3fc2";s:6:"effect";s:1:"0";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2774298;s:4:"time";s:19:"2024-03-18 20:38:08";s:8:"sort_key";i:1;}i:2;a:9:{s:11:"transaction";s:64:"f318eda796f0dce947fb18a21fc4cfeeaee7dedc83989e3721aa09c82760b32f";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:34:"QLFdUboUPJnUzvsXKu83hUtrQ1DuxyggRg";s:6:"effect";s:2:"60";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2774298;s:4:"time";s:19:"2024-03-18 20:38:08";s:8:"sort_key";i:2;}i:3;a:9:{s:11:"transaction";s:64:"f318eda796f0dce947fb18a21fc4cfeeaee7dedc83989e3721aa09c82760b32f";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:39:"script-95baa1886566d84b5271a295d3a773b3";s:6:"effect";s:1:"0";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2774298;s:4:"time";s:19:"2024-03-18 20:38:08";s:8:"sort_key";i:3;}i:4;a:9:{s:11:"transaction";s:64:"97d3a20cb85209167de9ff354be7a6b7f10506db35a7ca5dfeae81cdfce73433";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:42:"ex1qpwdf7qxr5tg0amtz8xwfha9wclmhkahyypc9gr";s:6:"effect";s:8:"-1134077";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2774298;s:4:"time";s:19:"2024-03-18 20:38:08";s:8:"sort_key";i:4;}i:5;a:9:{s:11:"transaction";s:64:"97d3a20cb85209167de9ff354be7a6b7f10506db35a7ca5dfeae81cdfce73433";s:8:"currency";s:64:"71e30c59c03e78d064757befa459ae7a0e9c4b8a5ae16cc90b3d70fc73b40ce4";s:7:"address";s:42:"ex1qh59jvreqevgl2jwrx325zcp5lmdksaettgr62h";s:6:"effect";s:9:"101899500";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2774298;s:4:"time";s:19:"2024-03-18 20:38:08";s:8:"sort_key";i:5;}i:6;a:9:{s:11:"transaction";s:64:"97d3a20cb85209167de9ff354be7a6b7f10506db35a7ca5dfeae81cdfce73433";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:42:"ex1qx7tzr5ztcex6f229z4pat96mpdvwadwf7r3stx";s:6:"effect";s:7:"1134017";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2774298;s:4:"time";s:19:"2024-03-18 20:38:08";s:8:"sort_key";i:6;}i:7;a:9:{s:11:"transaction";s:64:"97d3a20cb85209167de9ff354be7a6b7f10506db35a7ca5dfeae81cdfce73433";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:2:"60";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2774298;s:4:"time";s:19:"2024-03-18 20:38:08";s:8:"sort_key";i:7;}}s:10:"currencies";a:2:{i:0;a:4:{s:2:"id";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:4:"name";s:14:"Liquid Bitcoin";s:6:"symbol";s:5:"L-BTC";s:8:"decimals";i:8;}i:1;a:4:{s:2:"id";s:64:"71e30c59c03e78d064757befa459ae7a0e9c4b8a5ae16cc90b3d70fc73b40ce4";s:4:"name";s:26:"Taxkredit project 68cbc66a";s:6:"symbol";s:5:"tWaZe";s:8:"decimals";s:1:"2";}}}'], + // With L-BTC only + ['block' => 2766919, 'result' => 'a:2:{s:6:"events";a:13:{i:0;a:9:{s:11:"transaction";s:64:"4f6762c0a0c639b452fe5de2272cfa59348ca36580b83cc942838a580b55cbdc";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:4:"-446";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:0;}i:1;a:9:{s:11:"transaction";s:64:"4f6762c0a0c639b452fe5de2272cfa59348ca36580b83cc942838a580b55cbdc";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:39:"script-efcb3e8b994fe44d9b6dd0b55ab213a0";s:6:"effect";s:1:"0";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:1;}i:2;a:9:{s:11:"transaction";s:64:"4f6762c0a0c639b452fe5de2272cfa59348ca36580b83cc942838a580b55cbdc";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:34:"QLFdUboUPJnUzvsXKu83hUtrQ1DuxyggRg";s:6:"effect";s:3:"446";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:2;}i:3;a:9:{s:11:"transaction";s:64:"4f6762c0a0c639b452fe5de2272cfa59348ca36580b83cc942838a580b55cbdc";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:39:"script-287938b14253c57813474cb5eaf51d63";s:6:"effect";s:1:"0";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:3;}i:4;a:9:{s:11:"transaction";s:64:"7a731e79ae494ce4540322ea17a700eb050e1026009d49effea59b6b4985975c";s:8:"currency";N;s:7:"address";s:42:"ex1qq2wh460mj56u5dunv8g9e4demrfa3sh723hums";s:6:"effect";s:2:"-?";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:4;}i:5;a:9:{s:11:"transaction";s:64:"7a731e79ae494ce4540322ea17a700eb050e1026009d49effea59b6b4985975c";s:8:"currency";N;s:7:"address";s:42:"ex1qn84sc3540g7v8qr4j0tdsfa6qlshzsn47rz3yh";s:6:"effect";s:2:"-?";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:5;}i:6;a:9:{s:11:"transaction";s:64:"7a731e79ae494ce4540322ea17a700eb050e1026009d49effea59b6b4985975c";s:8:"currency";N;s:7:"address";s:42:"ex1qwmyrwfm2fkcf82qmayhg22nh455ecyk4pqdp69";s:6:"effect";s:2:"-?";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:6;}i:7;a:9:{s:11:"transaction";s:64:"7a731e79ae494ce4540322ea17a700eb050e1026009d49effea59b6b4985975c";s:8:"currency";N;s:7:"address";s:62:"ex1qemcjtseyldmga7axcv4sfq4dz3vap96snpe70huyalv84hxmn8wsty4yuv";s:6:"effect";s:2:"+?";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:7;}i:8;a:9:{s:11:"transaction";s:64:"7a731e79ae494ce4540322ea17a700eb050e1026009d49effea59b6b4985975c";s:8:"currency";N;s:7:"address";s:42:"ex1qzhftnsrlr9x3vn9du9lgtqqllux2elpe2mva6w";s:6:"effect";s:2:"+?";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:8;}i:9;a:9:{s:11:"transaction";s:64:"7a731e79ae494ce4540322ea17a700eb050e1026009d49effea59b6b4985975c";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:3:"294";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:9;}i:10;a:9:{s:11:"transaction";s:64:"57b56f5b9ec72c95cf3431e1fe9e1ec528ebbbc4b59819fd701484fca5d43b88";s:8:"currency";N;s:7:"address";s:62:"ex1qemcjtseyldmga7axcv4sfq4dz3vap96snpe70huyalv84hxmn8wsty4yuv";s:6:"effect";s:2:"-?";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:10;}i:11;a:9:{s:11:"transaction";s:64:"57b56f5b9ec72c95cf3431e1fe9e1ec528ebbbc4b59819fd701484fca5d43b88";s:8:"currency";N;s:7:"address";s:34:"GteSSbj2k7EmNCXqnNnagGsTPVQ6S93N6n";s:6:"effect";s:2:"+?";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:11;}i:12;a:9:{s:11:"transaction";s:64:"57b56f5b9ec72c95cf3431e1fe9e1ec528ebbbc4b59819fd701484fca5d43b88";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:3:"152";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766919;s:4:"time";s:19:"2024-03-13 14:45:08";s:8:"sort_key";i:12;}}s:10:"currencies";a:1:{i:0;a:4:{s:2:"id";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:4:"name";s:14:"Liquid Bitcoin";s:6:"symbol";s:5:"L-BTC";s:8:"decimals";i:8;}}}'], + // With a pegin + ['block' => 2766423, 'result' => 'a:2:{s:6:"events";a:10:{i:0;a:9:{s:11:"transaction";s:64:"eac7bddebcc5c76b9bc548115c7e3144d6c6654abb4ab76227374816436a15e6";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:3:"-86";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:0;}i:1;a:9:{s:11:"transaction";s:64:"eac7bddebcc5c76b9bc548115c7e3144d6c6654abb4ab76227374816436a15e6";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:39:"script-14b005d6cde92956f8acef08cd554ebd";s:6:"effect";s:1:"0";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:1;}i:2;a:9:{s:11:"transaction";s:64:"eac7bddebcc5c76b9bc548115c7e3144d6c6654abb4ab76227374816436a15e6";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:34:"QLFdUboUPJnUzvsXKu83hUtrQ1DuxyggRg";s:6:"effect";s:2:"86";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:2;}i:3;a:9:{s:11:"transaction";s:64:"eac7bddebcc5c76b9bc548115c7e3144d6c6654abb4ab76227374816436a15e6";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:39:"script-08be25e98319c0bfe137f3102fbc496c";s:6:"effect";s:1:"0";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:3;}i:4;a:9:{s:11:"transaction";s:64:"9d9209113c3ded3a1a9a28be5b1227206d94b49df8f3adbbb343002d2d90fe00";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:10:"the-bridge";s:6:"effect";s:8:"-2497468";s:5:"extra";s:2:"pi";s:13:"extra_indexed";s:64:"e1efb6717354c548c981ed266922e045a85a0fe489df84f301364532f3306c49";s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:4;}i:5;a:9:{s:11:"transaction";s:64:"9d9209113c3ded3a1a9a28be5b1227206d94b49df8f3adbbb343002d2d90fe00";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:42:"ex1q4q240cjajpc2jy7kcs09zqsqh3xalfzeectgay";s:6:"effect";s:7:"2497468";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:5;}i:6;a:9:{s:11:"transaction";s:64:"9d9209113c3ded3a1a9a28be5b1227206d94b49df8f3adbbb343002d2d90fe00";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:2:"43";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:6;}i:7;a:9:{s:11:"transaction";s:64:"cf2e9f28f41e6a5ebdaee32c17123b36616283728d850842206b4204291717a5";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:10:"the-bridge";s:6:"effect";s:8:"-1548364";s:5:"extra";s:2:"pi";s:13:"extra_indexed";s:64:"82e2d8f6bbe7d252c18e96f931033075d5eb0b5c79e0ed346eae6c024f40973d";s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:7;}i:8;a:9:{s:11:"transaction";s:64:"cf2e9f28f41e6a5ebdaee32c17123b36616283728d850842206b4204291717a5";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:42:"ex1qvtp03vtzeru6lff556xx7xssvm3a3mlzjtxdme";s:6:"effect";s:7:"1548364";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:8;}i:9;a:9:{s:11:"transaction";s:64:"cf2e9f28f41e6a5ebdaee32c17123b36616283728d850842206b4204291717a5";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:2:"43";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2766423;s:4:"time";s:19:"2024-03-13 06:29:08";s:8:"sort_key";i:9;}}s:10:"currencies";a:1:{i:0;a:4:{s:2:"id";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:4:"name";s:14:"Liquid Bitcoin";s:6:"symbol";s:5:"L-BTC";s:8:"decimals";i:8;}}}'], + // With a pegout + ['block' => 2787954, 'result' => 'a:2:{s:6:"events";a:9:{i:0;a:9:{s:11:"transaction";s:64:"e42d0f3164d14c7cdc02cde92277509a68610d0bc1cd03c8187e6741da74b13d";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:4:"-210";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2787954;s:4:"time";s:19:"2024-03-28 09:01:08";s:8:"sort_key";i:0;}i:1;a:9:{s:11:"transaction";s:64:"e42d0f3164d14c7cdc02cde92277509a68610d0bc1cd03c8187e6741da74b13d";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:39:"script-f06c5e8c739fe0c69173165dddd34ab3";s:6:"effect";s:1:"0";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2787954;s:4:"time";s:19:"2024-03-28 09:01:08";s:8:"sort_key";i:1;}i:2;a:9:{s:11:"transaction";s:64:"e42d0f3164d14c7cdc02cde92277509a68610d0bc1cd03c8187e6741da74b13d";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:34:"QLFdUboUPJnUzvsXKu83hUtrQ1DuxyggRg";s:6:"effect";s:3:"210";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2787954;s:4:"time";s:19:"2024-03-28 09:01:08";s:8:"sort_key";i:2;}i:3;a:9:{s:11:"transaction";s:64:"e42d0f3164d14c7cdc02cde92277509a68610d0bc1cd03c8187e6741da74b13d";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:39:"script-15bc9143ad760bce10eb170125090af2";s:6:"effect";s:1:"0";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2787954;s:4:"time";s:19:"2024-03-28 09:01:08";s:8:"sort_key";i:3;}i:4;a:9:{s:11:"transaction";s:64:"0499e19709e4fe9b4c03fc6ae0b167d193fdd99f79c49dc757e4546d95823aef";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:42:"ex1q35lcg2rpl0jka4vdqs7dw34lcnqrp9sj9pl0ct";s:6:"effect";s:6:"-99955";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2787954;s:4:"time";s:19:"2024-03-28 09:01:08";s:8:"sort_key";i:4;}i:5;a:9:{s:11:"transaction";s:64:"0499e19709e4fe9b4c03fc6ae0b167d193fdd99f79c49dc757e4546d95823aef";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:42:"ex1qmuta6zj9hkzs4uzjghyhzuh5xre69cggculku6";s:6:"effect";s:8:"-1083380";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2787954;s:4:"time";s:19:"2024-03-28 09:01:08";s:8:"sort_key";i:5;}i:6;a:9:{s:11:"transaction";s:64:"0499e19709e4fe9b4c03fc6ae0b167d193fdd99f79c49dc757e4546d95823aef";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:42:"ex1qc7hnm2t8p3qhmtcdalak47hdyxafevqr2xlkd8";s:6:"effect";s:7:"1083125";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2787954;s:4:"time";s:19:"2024-03-28 09:01:08";s:8:"sort_key";i:6;}i:7;a:9:{s:11:"transaction";s:64:"0499e19709e4fe9b4c03fc6ae0b167d193fdd99f79c49dc757e4546d95823aef";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:10:"the-bridge";s:6:"effect";s:6:"100000";s:5:"extra";s:2:"po";s:13:"extra_indexed";s:34:"1PUNRRzJC667NvE2n5MqBi9uzhfQhh8KDV";s:5:"block";i:2787954;s:4:"time";s:19:"2024-03-28 09:01:08";s:8:"sort_key";i:7;}i:8;a:9:{s:11:"transaction";s:64:"0499e19709e4fe9b4c03fc6ae0b167d193fdd99f79c49dc757e4546d95823aef";s:8:"currency";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:7:"address";s:8:"the-void";s:6:"effect";s:3:"210";s:5:"extra";N;s:13:"extra_indexed";N;s:5:"block";i:2787954;s:4:"time";s:19:"2024-03-28 09:01:08";s:8:"sort_key";i:8;}}s:10:"currencies";a:1:{i:0;a:4:{s:2:"id";s:64:"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d";s:4:"name";s:14:"Liquid Bitcoin";s:6:"symbol";s:5:"L-BTC";s:8:"decimals";i:8;}}}'], + ]; + } +}