ProofHandoff

The panel that hands a verified FDC proof to your own contract — the ABI-ready struct as pasteable TypeScript, the Solidity type to write against, and the verification call that already ran.

import { ProofHandoff } from '@flarekit-dev/react-ui'

For EVMTransaction and Web2Json the consumer is your contract, by design — a proof of an arbitrary EVM transaction, or of an arbitrary JSON endpoint filtered through your own jq and encoded to your own ABI signature, has no meaning until your logic gives it one. This project ships no demo consumer, and the reason is recorded in .thoughts/decisions/2026-08-04-no-first-party-proof-consumer.md.

But a screen that says "nothing takes this" and stops is a cul-de-sac. ProofHandoff is the difference between nothing takes this and this is the last step we own: the proof struct as calldata you can paste, the Solidity type to write against, and the verification that already happened.

Live#

The gallery mounts no standalone ProofHandoff case — the panel is verified inside ProofDetail, which renders it whenever abiStruct is passed. So the preview below shows the whole proof screen, with the handoff panel at the bottom of it. That is where the panel actually appears, and re-mounting it alone would mean showing a state the surface was never verified against.

mock kit

EVMTransaction

Attested in voting round 1415859
Verified on chain
SourcetestFLR 0x74657374464c5200000000000000000000000000000000000000000000000000
Voting roundRound1415859
Lowest used timestamp1785823530 Full precision. This field carries uint64 sentinels that a naive JSON parse corrupts.
Proof ownerNot bound This family binds no owner; anyone may present the proof.
Merkle proof2 nodes

Attested response

Every value as attested, at full precision.
blockNumber22447891
timestamp1785823530
sourceAddress0xa4b05cdb545fa7ca12be9f866d64e8a843a31bd9
isDeploymentfalse
receivingAddress0x48ac463d7975828989331f4de43341627b9c5f1d
value1000
input0x6c6d7bdd
status1
events1 entry
Verification only
Your own contract. flare-kit verifies the proof on chain through FdcVerification and hands you the ABI-ready struct; what it means is yours to decide, and this project deploys nothing that would presume. The proof above is real and verified on chain. Nothing deployed takes it because nothing deployed could know what it means — this is a declared end state, not a step that failed.

Take this to your contract

The proof is verified. What it means is yours to decide.
Solidity typeIEVMTransaction.Proof From flare-foundry-periphery-package. Import IEVMTransaction and take the struct as calldata.
Already verified byFdcVerification.verifyEVMTransaction Your contract should verify it again itself — this kit's check is a pre-flight, not a substitute.
consume-proof.ts

Paste-ready. Nothing here is a placeholder except your contract, its ABI and its function.

import { eVMTransactionFamily } from '@flarekit-dev/core'

// The proof, ABI-ready. Integers are bigint: a uint64 through Number is the
// corruption FDC proofs fail silently on.
const proof = {
  merkleProof: [
    "0xe111111111111111111111111111111111111111111111111111111111111111",
    "0xe222222222222222222222222222222222222222222222222222222222222222"
  ],
  data: {
    attestationType: "0x45564d5472616e73616374696f6e000000000000000000000000000000000000",
    sourceId: "0x74657374464c5200000000000000000000000000000000000000000000000000",
    votingRound: 1415859n,
    lowestUsedTimestamp: 1785823530n,
    requestBody: {
      transactionHash: "0xb5bf29512bae84f3837303721dad7241a6dae64dcf39c1568123ef4fc5715cd0",
      requiredConfirmations: 1,
      provideInput: true,
      listEvents: true,
      logIndices: []
    },
    responseBody: {
      blockNumber: 22447891n,
      timestamp: 1785823530n,
      sourceAddress: "0xa4b05cdb545fa7ca12be9f866d64e8a843a31bd9",
      isDeployment: false,
      receivingAddress: "0x48ac463d7975828989331f4de43341627b9c5f1d",
      value: 1000n,
      input: "0x6c6d7bdd",
      status: 1,
      events: [
        {
          logIndex: 0,
          emitterAddress: "0x48ac463d7975828989331f4de43341627b9c5f1d",
          topics: [
            "0xabababababababababababababababababababababababababababababababab"
          ],
          data: "0x00",
          removed: false
        }
      ]
    }
  }
}

// Your contract takes it as IEVMTransaction.Proof calldata.
await wallet.writeContract({
  address: yourContract,
  abi: yourAbi,
  functionName: 'yourFunction',
  args: [proof],
})

Usage#

ProofHandoff formats a struct you already hold. It fetches nothing, verifies nothing and signs nothing. Build the struct with the family module's toProofStruct — the family owns the ABI shape, and this surface never rebuilds it.

import { evmTransactionFamily } from '@flarekit-dev/core'
import { ProofHandoff } from '@flarekit-dev/react-ui'
import '@flarekit-dev/react-ui/styles.css'

export function Handoff({ row, proof }) {
  return <ProofHandoff row={row} abiStruct={evmTransactionFamily.toProofStruct(proof)} />
}

Passing abiStruct to ProofDetail renders the same panel in place, which is how the proof screen reaches it.

Props#

PropTypeDefaultDescription
rowrequiredFamilyRowThe catalogue row for this proof’s family. Its name is what the Solidity type, the verification call and the import line in the snippet are all built from.
abiStructrequiredunknownThe ABI-ready struct, from the family module’s `toProofStruct`. Typed `unknown` because the panel renders whatever shape the family produced without knowing its fields.
classNamestringExtra class on the outer element, so a host layout can place the panel.

What it renders#

A panel with two rows and a code window. The rows name the Solidity type — IEVMTransaction.Proof, from flare-foundry-periphery-package — and the verification that already ran, FdcVerification.verifyEVMTransaction, with the note that your contract should verify it again itself. This kit's check is a pre-flight, not a substitute.

The code window holds the struct as a TypeScript literal you can paste, wrapped in the writeContract call that takes it. Nothing in it is a placeholder except your contract, its ABI and its function.

States#

Both cases in the switcher above come from the gallery's fdc-04 section, one source of truth for the gallery and these docs. They are the two cases that pass abiStruct, which is the only thing that decides whether the panel renders:

  • verifier-only, with the handoffEVMTransaction, where nothing deployed consumes the proof and the handoff is the whole end of the road.
  • uint64-max sentinelXRPPaymentNonexistence, a family FAssets does consume, so the handoff sits alongside a real consumer rather than replacing one. Its struct carries 18446744073709551615 in two fields, and the snippet renders it as 18446744073709551615n, whole and pasteable. This is the case worth looking at hardest.

The panel itself has no other states: it renders the struct it is given, or it is not rendered at all.

Mock to live#

There is nothing to swap. ProofHandoff reads no network and holds no addresses; it formats a struct. The same panel renders a mock proof and a Coston2 proof identically, because in both cases the struct came from the same toProofStruct on the same family module.

What it will not do#

It will not emit JSON. The snippet is TypeScript with bigint literals because that is what can actually be pasted — JSON renders every uint64 as a string or, far worse, as a number, which is the precise corruption this kit exists to prevent, reintroduced at the last possible moment.

It will not claim your contract is done being careful. FdcVerification having returned true here is a pre-flight; the panel says so on the row, because a contract that trusts a caller's assertion of verification is a contract with no verification.