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
verifyPaymentwould 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.mockCallin Foundry) and trust the real FDC in production. - Does not: validate that the fields you supply make sense. If you claim
receivedAmountdoesn'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.jsonWire 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).