Wait Incoming Tx (Read Blocks)

This example demonstrates how to monitor incoming transactions

// In-memory cache mapping token symbol to its decimals
private 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...";

    StartCoroutine(WaitIncomingTransfers(api, address));
}

// Coroutine that scans blocks by height and processes TokenReceive events for the specified address
private IEnumerator WaitIncomingTransfers(PhantasmaAPI api, string address)
{
    const string chain = "main";

    long height = -1;
    bool gotHeight = false;

    // --- Step 1: Initialize starting block height ---
    while (!gotHeight)
    {
        // Fetch latest block height for the chain
        yield return api.GetBlockHeight(chain,
            h => { height = h; gotHeight = true; },
            // Callback for RPC errors (network issue, chain not found, etc.)
            (code, msg) => { Debug.LogError($"[Error][{code}] GetBlockHeight failed: {msg}"); });

        if (!gotHeight)
            yield return new WaitForSeconds(1f); // Retry delay before trying again
    }

    Debug.Log($"[Init] Starting from block #{height}");

    // --- Step 2: Continuous block scanning loop ---
    while (true)
    {
        // Fetch block data for the current height
        BlockResult block = null;
        yield return api.GetBlockByHeight(chain, height,
            b => block = b,
            // Callback for RPC errors (invalid height, network error, etc.)
            (code, msg) => Debug.LogError($"[Error][{code}] GetBlockByHeight({height}) failed: {msg}"));

        if (block?.Txs != null)
        {
            foreach (var tx in block.Txs)
            {
                // Only process successful transactions
                if (tx.State != ExecutionState.Halt)
                    continue;

                if (tx.Events == null) continue;

                foreach (var e in tx.Events)
                {
                    // Expect TokenReceive for the monitored address
                    var eventKind = Enum.Parse<EventKind>(e.Kind);
                    if (eventKind != EventKind.TokenReceive || !string.Equals(e.Address, address, StringComparison.OrdinalIgnoreCase))
                        continue;

                    // Decode 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;
                    if (!_tokenDecimals.TryGetValue(data.Symbol, out decimals))
                    {
                        bool fetched = false;
                        yield return api.GetToken(data.Symbol,
                            t =>
                            {
                                decimals = t.Decimals;
                                _tokenDecimals[data.Symbol] = decimals; // cache for reuse
                                fetched = true;
                            },
                            // Callback for RPC errors (invalid token, network error, etc.)
                            (code, msg) =>
                            {
                                Debug.LogError($"[Error][{code}] GetToken({data.Symbol}) failed: {msg}");
                                decimals = 0; // fallback to 0 to continue
                                fetched = true;
                            });
                        while (!fetched) yield return null;
                    }

                    // Convert chain amount to human-readable format
                    var human = UnitConversion.ToDecimal(data.Value, decimals);

                    Debug.Log($"Address {e.Address} received {human} {data.Symbol}");
                }
            }
        }

        // --- Step 3: Wait until a new block is produced ---
        while (true)
        {
            long newHeight = height;
            bool got = false;

            // Fetch latest block height again
            yield return api.GetBlockHeight(chain,
                h => { newHeight = h; got = true; },
                // Callback for RPC errors (network issue, etc.)
                (code, msg) => { Debug.LogError($"[Error][{code}] GetBlockHeight wait failed: {msg}"); got = true; });

            // If chain height increased, move to next block
            if (got && newHeight > height)
            {
                height += 1;
                break;
            }

            // Delay before re-checking block height
            yield return new WaitForSeconds(1f);
        }
    }
}

Last updated