FlareForge

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

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