Read the registry from your tool — ABI, types, viem snippets, indexing patterns.

View on GitHub

Integrate the registry

Build a directory, indexer, or wallet integration on top of ParticipantRegister.

Provider identity gate

Calling register(participantType, infoURI) with participantType == 0 (Provider) reverts with IdentityNotRegistered() unless msg.sender is a registered identity on the configured registry.

  • Coston2: registry = MockIdentityRegistry — admin-allowlisted for AP test fixtures.
  • Flare: registry = FlareIdentityAdapter, which resolves Flare's EntityManager via FlareContractRegistry and treats msg.sender as a registered FSP identity iff its EntityManager signing-policy address is distinct from the address itself and non-zero (defeats echo-on-miss) — matches the adapter source at src/FlareIdentityAdapter.sol. This is one-time identity registration (per flare-systems-deployment's REGISTRATION.md) — no vote power, no per-reward-epoch voter coupling. Connect from the same wallet you use as your Flare FSP identity — not your delegation, submit, or signing wallets.

All other participant types (DeFi, Wallet, Tool, FAssetsAgent, Exchange, App, AgenticAI) are open and can be registered from any wallet.

Contract

NetworkChain IDAddress
Flare140x29BA5B29C5451e7db5885A8CFE4c73Ae1A2eABe5
Songbird19n/a (out of v1 scope)
Coston2 (testnet)1140x09f15b14D16BA645661c576348E4d4C201242bF2

ABI: abi/ParticipantRegister.abi.json

Constructor: constructor(address _identityRegistry) — the registry address is immutable and set at deploy time.

TypeScript types for participant.json: types/participant.d.ts

Read the registry

import { createPublicClient, http, defineChain, parseAbi } from "viem";

const flare = defineChain({
  id: 14,
  name: "Flare",
  nativeCurrency: { name: "Flare", symbol: "FLR", decimals: 18 },
  rpcUrls: { default: { http: ["https://flare-api.flare.network/ext/C/rpc"] } },
});

const client = createPublicClient({ chain: flare, transport: http() });
const abi = parseAbi([
  "function getActiveParticipants() view returns (address[])",
  "function getParticipants(uint256,uint256) view returns ((address,uint8,string,bool,uint256,uint256,uint256)[])",
  "function getParticipantsByType(uint8) view returns (address[])",
]);

const REG = "0x29BA5B29C5451e7db5885A8CFE4c73Ae1A2eABe5";
const all = await client.readContract({ address: REG, abi, functionName: "getActiveParticipants" });

Index events

event ParticipantRegistered(address indexed owner, uint8 indexed participantType, uint256 index, string infoURI);
event ParticipantUnregistered(address indexed owner, uint256 index);

ParticipantRegistered fires on both first registration and updates. To distinguish:

// after fetching the receipt:
const p = await client.readContract({ address: REG, abi, functionName: "getParticipant", args: [event.args.owner] });
const isNew = p.registeredAt === BigInt(receipt.blockNumber);

Pagination

For on-chain consumers (Solidity), use getParticipants(offset, limit). Off-chain eth_call consumers can use the unbounded reads (getAllParticipants, getActiveParticipants, getParticipantsByType) but those become expensive past ~1000 participants.

Resolve metadata

The on-chain record stores only (participantType, infoURI). The participant.json at that URI should include flare:participant-type matching the on-chain value so consumers can verify the cross-reference without an extra RPC call. To get the full profile, fetch and parse the JSON at infoURI. Schema: assets/participant.schema.json.

const meta = await fetch(p.infoURI).then(r => r.json());
// meta.name, meta.url, meta.description, meta["flare:brand"]?.icon, etc.

CORS on the participant's infoURI is the most common integration failure. The contract doesn't validate it — your fetch will fail silently if the host hasn't set Access-Control-Allow-Origin. See Your participant.json § CORS.

Test-support contracts (Coston2 only)

MockIdentityRegistry (0xf77C24aFAC992CE17fFe2a01b642d1CE5d025D9e) is an admin-managed allowlist that mirrors the on-chain registered-FSP-identity check on testnet, where Flare's real EntityManager identity set isn't populated. The portal uses it on Coston2 to gate the Provider participant type. Do not rely on it from production code — Flare mainnet uses the real EntityManager via FlareContractRegistry.