FlareForge

Guide · FDC

Verify a cross-chain payment in 20 lines of Solidity

Flare Data Connector (FDC) lets your contract answer questions like "did this wallet pay that wallet 100 XRP last week?" without a bridge, without wrapping, and without a trusted intermediary. A committee of Flare validators signs off on a Merkle root of off-chain facts every voting round; your contract checks one Merkle branch against the committed root and uses the result. This guide walks through the verification snippet, the off-chain request flow, and the mock-proof shortcut we shipped so you can develop without waiting for a real attestation round.

Verify a Payment proof in Solidity

The whole integration is three imports, one verifycall, and a couple of sanity checks on the decoded fields. Caller supplies the proof as calldata; your contract validates and acts.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {IPayment} from
    "@flarenetwork/flare-periphery-contracts/flare/IPayment.sol";
import {ContractRegistry} from
    "@flarenetwork/flare-periphery-contracts/flare/ContractRegistry.sol";

contract InvoiceSettler {
    mapping(bytes32 => bool) public settled;

    function settle(IPayment.Proof calldata proof) external {
        require(
            ContractRegistry.auxiliaryGetIFdcVerification()
                .verifyPayment(proof),
            "FDC: invalid Merkle proof"
        );
        require(proof.data.responseBody.status == 0, "payment not successful");
        bytes32 reference = proof.data.responseBody.standardPaymentReference;
        require(!settled[reference], "already settled");
        settled[reference] = true;
        // ... release goods, mint tokens, unlock escrow, etc.
    }
}

verifyPayment hashes the response body against the supplied Merkle branch and checks it against the on-chain voting round root. Anyone can submit the proof; only genuinely-attested payments pass. standardPaymentReference is the off-chain-controlled 32-byte reference the sender attaches to the payment, which is how you idempotently link the on-chain settle to a specific invoice.

The full request flow

Getting from "I want to verify this payment" to "I have a proof" is a four-step off-chain dance:

  1. Submit request. Call FdcHub.requestAttestation(bytes requestBody) on Flare. Pay the fee, which is currently tiny but varies by type. This emits an AttestationRequest event that FlareForge indexes.
  2. Wait one voting round. Rounds are 90 seconds. The committee sees your request, checks the source chain, and signs.
  3. Fetch proof off-chain. Pull the signed response + Merkle branch from any Flare attestation client HTTP API once the round finalizes. You pass the request hash and round id.
  4. Submit to your contract. Pass the proof struct as calldata to whatever function uses it (the settle(...) above, or your own flow).

The typical attestation-client endpoint looks like this. Replace the host with your preferred provider (Flare Foundation runs one, several node operators run their own):

curl -X POST https://attestation-client.example/attestation-client/api/proof/get-specific-proof \
  -H "Content-Type: application/json" \
  -d '{
    "votingRoundId": 1000042,
    "requestBytes": "0x5061796d656e7400..."
  }'

Mock proofs for local development

Waiting 90 seconds per round while you iterate on a contract is slow. FlareForge Sandbox generates structurally-identical mock proofs offline so you can unit test the verification flow without network round-trips. The Merkle root is deterministic and self-consistent inside the payload, so your contract's verifyPayment will accept or reject based on the same logic as real FDC, except no on-chain round is involved.

curl -X POST https://flareforge.io/api/v1/sandbox/generate-proof \
  -H "Content-Type: application/json" \
  -d '{
    "type_id": "Payment",
    "source_id": "XRP",
    "fields": {
      "transactionId": "0x...",
      "inUtxo": 0,
      "utxo": 0
    }
  }'

The response is a { merkleLeaf, merkleProof[], response }tuple that maps directly onto the IPayment.Proofstruct. Plug it into a Foundry test and you can fuzz your settlement logic without any RPC calls.

EVMTransaction in one screen

Same shape, different response body. Use this when you care about an on-chain event on Ethereum, Polygon, Arbitrum, or any other EVM source:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {IEVMTransaction} from
    "@flarenetwork/flare-periphery-contracts/flare/IEVMTransaction.sol";
import {ContractRegistry} from
    "@flarenetwork/flare-periphery-contracts/flare/ContractRegistry.sol";

contract CrossChainClaim {
    function claim(IEVMTransaction.Proof calldata proof) external {
        require(
            ContractRegistry.auxiliaryGetIFdcVerification()
                .verifyEVMTransaction(proof),
            "FDC: invalid proof"
        );
        require(proof.data.responseBody.status == 1, "source tx reverted");
        // Walk proof.data.responseBody.events[] for the log you care about.
    }
}

Attestation types at a glance

TypeWhat it proves
PaymentAssert that an account paid another account a specific amount on an external chain. Most common for escrow release, token bridges, invoice settlement.
EVMTransactionProve an EVM transaction happened on a source chain (Ethereum, Polygon, BSC, etc.) and read its logs or return value inside your Flare contract.
ReferencedPaymentNonexistenceProve that an expected payment did NOT happen before a deadline. Drives escrow timeouts and slashing.
ConfirmedBlockHeightExistsProve a block with a given height is finalized on the source chain, which is how you finalize 'past a given depth' logic.
AddressValidityCheck that an off-chain address (XRP, BTC, etc.) is syntactically valid before accepting a user-supplied destination.
BalanceDecreasingTransactionProve a balance-decreasing outflow from a specific address. Used to detect unauthorized spends by escrow agents.

Live feed of every AttestationRequest on Flare mainnet, filterable by type and source chain, is on the FDC Explorer.

Things that bite on the first integration

Related on the site