netFLARE / 14
block—
blktime—
ftso—
fdc/24h—
tvl—
indexer—
FlareForgeconsole
AgentsFDCFlowSandboxDocsEmbedEcosystemAbout
mainnet live
docs / fdc-sandbox

Guide · FDC

Mock FDC proofs for Foundry tests

A real FDC attestation round is 90 seconds. A Foundry fuzz run with 1000 iterations waits 25 hours. The FlareForge CrossChain Sandbox gives you a POST endpoint that returns a structurally-valid proof payload in under 50 ms, so your local tests can cover the happy path, the malformed-proof path, and every edge case in between without touching the real attestation committee.

Generate a Payment proof

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": "0x49cf...",
      "inUtxo": 0,
      "utxo": 0
    }
  }'

Response shape matches the IPayment.Proof struct one-to-one, { merkleProof[], data: { ... } }, so you can pipe it into a Solidity calldata decoder without transforms.

{
  "merkleProof": ["0xaabb...", "0xccdd..."],
  "data": {
    "attestationType": "0x5061796d656e7400...",
    "sourceId": "0x5852500000...",
    "votingRound": 0,
    "lowestUsedTimestamp": 0,
    "requestBody": { ... },
    "responseBody": {
      "blockNumber": 12345,
      "blockTimestamp": 1700000000,
      "sourceAddressHash": "0x...",
      "receivingAddressHash": "0x...",
      "standardPaymentReference": "0x...",
      "spentAmount": "100000000",
      "receivedAmount": "100000000",
      "status": 0
    }
  }
}

Plug it into a Foundry test

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

import {Test} from "forge-std/Test.sol";
import {InvoiceSettler} from "src/InvoiceSettler.sol";
import {IPayment} from
    "@flarenetwork/flare-periphery-contracts/flare/IPayment.sol";

contract InvoiceSettlerTest is Test {
    InvoiceSettler settler;

    function setUp() public {
        settler = new InvoiceSettler();
        // Bypass on-chain verification for unit tests: mock the
        // ContractRegistry.auxiliaryGetIFdcVerification() to always return true.
        vm.mockCall(
            address(0x...ContractRegistry),
            abi.encodeWithSignature("verifyPayment((bytes32[],(bytes32,bytes32,uint32,uint64,(bytes32),(uint64,uint64,bytes32,bytes32,bytes32,int256,int256,uint8))))"),
            abi.encode(true)
        );
    }

    function test_settles_valid_payment() public {
        IPayment.Proof memory proof = _loadSandboxProof("fixtures/payment_ok.json");
        settler.settle(proof);
        assertTrue(settler.settled(proof.data.responseBody.standardPaymentReference));
    }

    function _loadSandboxProof(string memory path) internal view returns (IPayment.Proof memory) {
        string memory raw = vm.readFile(path);
        bytes memory parsed = vm.parseJson(raw);
        return abi.decode(parsed, (IPayment.Proof));
    }
}

Store the sandbox response in test/fixtures/*.json and load it via vm.readFile + vm.parseJson. Foundry's JSON ABI decoder handles the whole IPayment.Proof tuple.

What the sandbox does and does not do

  • Does: returns a proof payload that decodes cleanly into the Solidity struct, has self-consistent Merkle proof bytes, and maps your input fields to the correct response body. Safe to feed straight into your contract.
  • Does not: sign anything that the real on-chain verifyPayment would accept. The Merkle root in a sandbox proof will not match the voting-round root on Flare, so the proof will be rejected by real FDC. This is the point. You mock the verification call in tests (vm.mockCall in Foundry) and trust the real FDC in production.
  • Does not: validate that the fields you supply make sense. If you claim receivedAmount doesn't match what the real chain would report, the sandbox doesn't care. That's fine for testing malformed-input handling.

CLI alternative

The same generator ships as a standalone CLI for scripted fixture refresh:

pipx install flareforge-cli
flareforge sandbox generate \
  --type Payment --source XRP \
  --field transactionId=0x49cf... --field inUtxo=0 \
  > test/fixtures/payment_ok.json

Wire it into your package.json or Makefile so fixture refresh is a single command. The CLI hits the same endpoint under the hood.

Related on the site

  • CrossChain Sandbox UI, point-and-click version for quick fixture generation.
  • FDC Payment guide, production verification flow the mock proof simulates.
  • GET /api/v1/sandbox/attestation-types, the full schema per type (which fields each response body contains).

← Previous guide

Verify a cross-chain payment in 20 lines of Solidity