Wait Incoming Tx (Read Blocks)
This example demonstrates how to monitor incoming transactions
using PhantasmaPhoenix.Core;
using PhantasmaPhoenix.Cryptography;
using PhantasmaPhoenix.Protocol;
using PhantasmaPhoenix.RPC;
// In-memory cache mapping token symbol to its decimals
private static readonly Dictionary<string, uint> _tokenDecimals = new(StringComparer.OrdinalIgnoreCase);
public void WaitIncomingTx_ReadBlocks()
{
// Initialize PhantasmaAPI instance
var api = new PhantasmaAPI("https://testnet.phantasma.info/rpc");
// Address to monitor
var address = "P2K...";
using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) => { e.Cancel = true; cts.Cancel(); };
await WaitIncomingTransfers(api, address, null, "main", cts.Token);
}
public static async Task WaitIncomingTransfers(PhantasmaAPI api, string address, string chain, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(address))
{
throw new ArgumentException("Empty address provided");
}
if (string.IsNullOrWhiteSpace(chain))
{
throw new ArgumentException("Empty chain provided");
}
// Initialize from current chain height
// Current block height will be our starting point for monitoring
long height = await api.GetBlockHeightAsync(chain);
Console.WriteLine($"[Init] Starting monitoring from block #{height}");
// Continuous block scanning loop
while (!ct.IsCancellationRequested)
{
try
{
// Fetch block data for the current height
var block = await api.GetBlockByHeightAsync(chain, height);
if (block?.Txs != null)
{
foreach (var tx in block.Txs)
{
// Only process successful transactions
if (tx.State != ExecutionState.Halt)
{
continue;
}
// Empty tx, should not happen
if (tx.Events == null)
{
continue;
}
foreach (var e in tx.Events)
{
if (!Enum.TryParse<EventKind>(e.Kind, true, out var kind))
{
Console.WriteLine($"[Error] Unsupported event kind '{e.Kind}'");
continue;
}
if (kind != EventKind.TokenReceive)
{
// In this example we only log token receive events
continue;
}
if (!string.Equals(e.Address, address, StringComparison.OrdinalIgnoreCase))
{
// We only monitor events for provided address
continue;
}
// Decode and unserialize TokenEventData from event payload
var dataBytes = Base16.Decode(e.Data);
var data = Serialization.Unserialize<TokenEventData>(dataBytes);
// Get decimals from cache or fetch from API
uint decimals = await GetDecimalsAsync(api, data.Symbol);
// Convert chain amount to human-readable format
var decimalAmount = UnitConversion.ToDecimal(data.Value, decimals);
Console.WriteLine($"Address {e.Address} received {decimalAmount} {data.Symbol} (tx: {tx.Hash})");
}
}
}
// Wait until a new block is produced (or loop is cancelled)
while (!ct.IsCancellationRequested)
{
// Fetch latest block height again
var newHeight = await api.GetBlockHeightAsync(chain);
// If chain height increased, move to next block
if (newHeight > height)
{
height += 1;
break;
}
// Delay before re-checking block height - sleep for 1 second
await Task.Delay(1000, ct);
}
}
catch (OperationCanceledException)
{
// graceful exit
break;
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
await Task.Delay(1000, ct);
}
}
Console.WriteLine("Stopped");
}
// Gets decimals for token from cache or requests it from chain and stores it in the cache
private static async Task<uint> GetDecimalsAsync(PhantasmaAPI api, string symbol)
{
// Checking cache first
if (_tokenDecimals.TryGetValue(symbol, out var d))
{
return d;
}
// Not found in cache, querying from the chain
var tok = await api.GetTokenAsync(symbol);
d = tok?.Decimals ?? 0;
// Storing in cache
_tokenDecimals[symbol] = d;
return d;
}
Last updated