Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.algovoi.co.uk/llms.txt

Use this file to discover all available pages before exploring further.

The AlgoVoi Facilitator Service is a stateless on-chain transaction verifier. It occupies a single well-defined position in the platform trust boundary: given a transaction identifier, a chain, and a set of expected settlement parameters, it returns a deterministic verdict on whether that transaction settled as expected. It holds no tenant state, no checkout state, and no business logic. All compliance, audit, and delivery work happens upstream in the gateway. The facilitator runs in an active-passive pair and is the only service in the platform that holds RPC credentials for external chain infrastructure.

Overview

PropertyValue
RoleStateless on-chain transaction verifier
Chains supportedAlgorand, VOI, Hedera, Stellar, Base, Solana, Tempo
State storedNone — fully stateless
Tenant awarenessNone
The facilitator is purpose-built for a single call pattern: the gateway sends a verification request and expects back either a settled verdict with chain-extracted facts or a rejection with a reason code. Nothing in the facilitator touches the payment database, the tenant table, the audit chain, or any webhook queue.

Active-passive failover

Two facilitator instances run in parallel, serving identical verification logic from the same codebase with the same RPC credentials. The gateway detects a primary failure (timeout, 5xx, or connection refused) and transparently retries the same request against the failover instance. Because both instances share the same underlying infrastructure, the fallback adds negligible latency. No state replication. The two facilitator instances hold no state. A request that hits the primary and a retry that hits the failover are independently executed against the same chain; both return the same result because they read from the same blockchain.

Verification flow

A verification request proceeds through five stages inside the facilitator. Stage 1 — Request ingestion. The facilitator receives a structured request from the gateway. All fields are validated against the per-chain schema for the declared chain. Unknown chain values, malformed transaction ID formats, or missing required fields are rejected immediately with a 400 before any RPC call is made. Stage 2 — Chain client selection. The facilitator selects the appropriate chain client based on the chain field. Each chain has a dedicated client module that encapsulates the connection pool, authentication, retry logic, and response parsing specific to that chain’s RPC or REST interface. Stage 3 — On-chain query. The selected chain client queries the relevant RPC endpoint or indexer for the transaction identified by tx_id. The query strategy differs by chain — some chains use their native RPC, others use REST indexers or mirror nodes. The client applies exponential backoff on transient failures and distinguishes between “transaction not found” (which may be a timing issue) and “transaction found but does not match” (which is a definitive failure). Stage 4 — Field extraction and comparison. The raw chain response is parsed to extract: actual_amount, actual_asset, actual_sender, actual_receiver, block_height, and finality_status. Each extracted value is compared against the corresponding expected value from the request. The comparison is type-aware: amounts are compared as bigints in microunits; asset identifiers are normalised before comparison; addresses are normalised to canonical form for the relevant chain. Stage 5 — Response construction. The facilitator builds a structured response object carrying the extracted chain facts and the settled boolean. It does not add any gateway-level metadata (audit chain position, webhook status, compliance outcome). That enrichment happens exclusively in the gateway after the facilitator response is received.

Verification request schema

Sent by the gateway to the facilitator over the internal VPC channel.
FieldTypeRequiredDescription
tx_idstringYesChain-native transaction identifier
chainenumYesOne of: algorand, voi, hedera, stellar, base, solana, tempo
asset_idstringYesCAIP-19 asset identifier for the expected payment asset
expected_amount_microunitsbigintYesExpected settlement amount in millionths of the asset unit
expected_receiverstringYesMerchant wallet address that should have received the payment
network_idenumNomainnet (default) or testnet
sender_hintstringNoDeclared payer address; used as indexer query hint, not for verification
timeout_msintegerNoMaximum time to wait for RPC response; defaults to 8000ms

Verification response schema

Returned by the facilitator to the gateway.
FieldTypeDescription
settledbooleanTrue if the transaction was found, confirmed, and all values matched expectations
amount_microunitsbigintActual settled amount extracted from the chain, in microunits
asset_idstringCAIP-19 asset identifier as extracted from the chain
sender_addressstringPayer wallet address as extracted from the chain
receiver_addressstringReceiver wallet address as confirmed on-chain
block_heightintegerBlock or slot height at which the transaction was included
finality_statusenumconfirmed or pending_finality
verified_atstringISO-8601 timestamp of the facilitator-side verification
chainenumEcho of the input chain field
tx_idstringEcho of the input transaction ID
rejection_reasonenumPresent only when settled is false; see rejection reason table

Rejection reasons

rejection_reasonMeaning
tx_not_foundTransaction ID not found on the chain at query time
amount_mismatchTransaction found but settled amount does not match expected
asset_mismatchTransaction found but asset does not match expected
receiver_mismatchTransaction found but receiver does not match expected
finality_pendingTransaction found but has not yet reached required confirmation depth
rpc_errorChain RPC returned an error or timed out
chain_unsupportedDeclared chain is not supported by this facilitator instance
tx_malformedTransaction ID format is invalid for the declared chain

Per-chain verification approach

Each chain requires a distinct verification strategy because the data models, query interfaces, and finality semantics differ substantially across the seven supported chains.
ChainQuery methodAddress formatAsset representationFinality criteria
AlgorandAlgod + Indexer RPCBase32 with checksumASA ID (integer)1 confirmed round
VOIAlgod-compatible RPCBase32 with checksumARC-200 contract address1 confirmed round
HederaMirror node REST API0.0.{num} formatHTS token IDMirror node inclusion
StellarHorizon REST APIStrkey (G…)Asset code + issuerHorizon ledger close
BaseAlchemy EVM JSON-RPCEIP-55 checksummed hexERC-20 contract address6 block confirmations
SolanaRPC + token programBase58 pubkeySPL mint address32 slot confirmations
TempoEVM-compatible JSON-RPCEIP-55 checksummed hexERC-20 contract address6 block confirmations

Algorand

Algorand transaction verification uses the Algod REST API for recent transactions and the Indexer REST API for historical lookups. The facilitator looks for an axfer (asset transfer) transaction matching the declared ASA ID. For native ALGO payments, it looks for a pay transaction. The note field is extracted and decoded if present, providing the memo value for verification. Transaction finality is established on the first confirmed round — Algorand produces one block per approximately 3.9 seconds with immediate finality.

VOI

VOI uses an Algod-compatible API, making the verification path structurally identical to Algorand. The primary difference is in asset representation: VOI uses ARC-200, a smart-contract token standard, meaning asset transfers appear as application call transactions rather than axfer transactions. The facilitator inspects inner transactions and application state deltas to confirm the ARC-200 transfer. AVM compatibility means the same indexer query patterns apply.

Hedera

Hedera transaction verification queries the Hedera Mirror Node REST API. HTS (Hedera Token Service) transfers are returned as structured transfer records with explicit sender, receiver, token ID, and amount fields. Topic memo extraction is used for payment reference correlation. The mirror node does not have a configurable confirmation-depth concept; inclusion in the mirror node response is treated as the finality signal.

Stellar

Stellar transaction verification uses the Horizon REST API. The facilitator looks for a payment or path-payment operation with the expected asset code and issuer address. The canonical Stellar USDC issuer is GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVV. The memo hash field is extracted for payment reference correlation. Horizon ledger close is treated as finality — Stellar produces one ledger per approximately 5 seconds.

Base

Base transaction verification uses the Alchemy EVM JSON-RPC endpoint. The facilitator fetches the transaction receipt and scans the logs for an ERC-20 Transfer(address indexed from, address indexed to, uint256 value) event matching the expected USDC contract address. Amount comparison uses bigint arithmetic on the raw uint256 value. Six block confirmations are required before finality_status is set to confirmed; below that threshold the response carries finality_status: pending_finality.

Solana

Solana transaction verification uses the Solana JSON-RPC endpoint. The facilitator fetches the transaction by signature and parses the token program instructions to find an SPL token transfer matching the expected mint address. The memo program instruction is parsed separately to extract the payment reference. Thirty-two slot confirmations are required for finality_status: confirmed, matching Solana’s practical finality threshold for USDC transfers.

Tempo

Tempo uses an EVM-compatible RPC interface, making its verification path structurally identical to Base. The settlement asset is USDCe (an ERC-20 token). The facilitator applies the same log-scanning approach, the same six-block confirmation threshold, and the same bigint amount comparison. The only configuration difference is the chain ID and RPC endpoint.

Address normalisation

Address comparison is performed against normalised forms to avoid false negatives caused by checksum variants or encoding differences.
ChainNormalisation applied
AlgorandBase32 decoded and re-encoded; trailing checksum verified
VOISame as Algorand
HederaEntity ID parsed to {shard}.{realm}.{num} canonical form
StellarStrkey decoded and re-encoded to strip variant encodings
BasetoLowerCase() applied after EIP-55 checksum stripping
SolanaBase58 decoded and re-encoded
TempoSame as Base

Amount representation

All amounts in the verification protocol are expressed in microunits — millionths of the base asset unit. This convention is applied uniformly across all chains regardless of the native on-chain decimal convention.
ChainAssetOn-chain decimalsMicrounit scale
AlgorandUSDC61:1 with on-chain value
VOIUSDC61:1 with on-chain value
HederaUSDC61:1 with on-chain value
StellarUSDC7Divide by 10 to get microunits
BaseUSDC61:1 with on-chain value
SolanaUSDC61:1 with on-chain value
TempoUSDCe61:1 with on-chain value
The gateway always submits expected_amount_microunits as a microunit value; the facilitator’s per-chain client performs any necessary scaling before comparison.

Finality handling

Finality semantics vary by chain and are handled explicitly rather than treating all transactions as immediately final. finality_status: confirmed means the transaction has met the chain-specific confirmation threshold and is treated as irreversible under normal network conditions. finality_status: pending_finality means the transaction exists on-chain and all field values match, but the confirmation depth has not yet been reached. The facilitator returns settled: false with rejection_reason: finality_pending in this case, not settled: true with finality_status: pending_finality — the gateway handles pending payments by re-queuing and re-polling. The gateway queues pending-finality payments and re-polls the facilitator at a configurable interval until either the confirmation threshold is reached or the checkout expires. During this window the checkout status remains open from the merchant’s perspective. Native-asset price oracle interaction. If a payment is made in a native asset (ALGO, VOI, etc.) rather than a stablecoin, the gateway must convert the verified microunit amount to USD cents using the price oracle before marking the checkout as paid. If the price oracle is unavailable at the moment of facilitator response, the payment is queued and the USD conversion is performed when the oracle recovers. This queuing does not affect the facilitator — the verification verdict is already stored and the facilitator is not re-queried for the oracle-pending case.

Trust boundary

The facilitator’s position in the trust hierarchy is deliberately narrow. What the facilitator knows: transaction IDs, chain identifiers, asset identifiers, expected amounts, expected receivers. It queries external chain infrastructure and returns extracted facts. What the facilitator does not know: tenant identities, checkout state, payment link metadata, mandate details, API keys, webhook endpoints, KYC status, trial balances. What the facilitator cannot do: write to the payment database, trigger webhooks, update checkout status, insert into the audit chain, modify any gateway state. The facilitator is read-only against external chain infrastructure and returns a response. All state mutations happen in the gateway. This separation means that a compromise of the facilitator exposes RPC credentials and the ability to query chain state, but not tenant data, payment history, API keys, or audit records. The gateway trusts the facilitator’s response but does not give the facilitator any write capability over platform state.

Operational characteristics

Stateless restart safety. Because no state is stored, facilitator processes can be restarted, replaced, or scaled without coordination. There are no pending in-memory queues to drain before restart. A request in flight at restart time will time out at the gateway; the gateway’s failover logic routes the retry to the standby instance. Healthcheck endpoint. Both facilitator instances expose GET /health. The response carries a status field (ok or degraded) and per-chain RPC reachability flags. The gateway polls this endpoint to detect degraded state proactively. No logging of tenant data. Facilitator access logs record transaction IDs, chain identifiers, and verdict outcomes. They do not record expected receiver addresses, API keys, tenant IDs, or any fields that would constitute personal or commercially sensitive data. Log retention is 30 days rolling.