Querying EAS Data
How to read attestations from Maroo's EAS — via Solidity contract calls (the canonical surface), via the EAS Indexer for reverse lookups, and via the JS/TS SDKs.
EAS on Maroo is just a Solidity contract preinstall — you read attestations the same way you'd read any contract, over JSON-RPC. The Indexer is a separate preinstall that exposes reverse lookups ("all attestations issued to address X under schema Y"). Pick the path that fits your stack: contract call for one-shot reads, Indexer for reverse lookups, eas-sdk if you want a JS-first API.
Prerequisites
- Maroo testnet RPC URL
- EAS / EAS Indexer addresses (preinstalls or resolved via the EAS precompile's
getParams())
Resolve EAS / Indexer addresses
Two options. Hard-code the canonical preinstall addresses (
EAS = 0x1000…0007, Indexer = 0x1000…0008) — fine for known networks. Or call the EAS precompile's getParams() so the same code works against any Maroo network without modification.import { createPublicClient, http } from "viem";
import { iEasAbi as easPrecompileAbi } from "@maroo-chain/contracts/abi/precompiles/eas/IEas";
const publicClient = createPublicClient({ transport: http(process.env.MAROO_RPC_URL) });
const EAS_PRECOMPILE = "0x1000000000000000000000000000000000000009";
const { eas: EAS, indexer: INDEXER } = await publicClient.readContract({
address: EAS_PRECOMPILE,
abi: easPrecompileAbi,
functionName: "getParams",
}); Method 1 — Direct contract call (viem / ethers)
The default path for frontend dApps and most backend code. Use
getAttestation(uid) against the EAS contract; it returns the full Attestation struct (schema, recipient, attester, time, expirationTime, revocationTime, refUID, data).import { parseAbi } from "viem";
const easAbi = parseAbi([
"struct Attestation { bytes32 uid; bytes32 schema; uint64 time; uint64 expirationTime; uint64 revocationTime; bytes32 refUID; address recipient; address attester; bool revocable; bytes data; }",
"function getAttestation(bytes32 uid) view returns (Attestation)",
]);
const attestation = await publicClient.readContract({
address: EAS,
abi: easAbi,
functionName: "getAttestation",
args: [uid],
});
// attestation.recipient, attestation.revocationTime, attestation.expirationTime, ... Method 2 — Reverse lookups via the Indexer
"Has user X been attested under schema Y?" — that's the Indexer. Two methods you'll use:
Then feed each UID into Method 1 to fetch the actual attestation.
getReceivedAttestationUIDCount(recipient, schemaUid)→ cheap existence checkgetReceivedAttestationUIDs(recipient, schemaUid, start, length, reverseOrder)→ paginated list
Then feed each UID into Method 1 to fetch the actual attestation.
const indexerAbi = parseAbi([
"function getReceivedAttestationUIDCount(address recipient, bytes32 schemaUID) view returns (uint256)",
"function getReceivedAttestationUIDs(address recipient, bytes32 schemaUID, uint256 start, uint256 length, bool reverseOrder) view returns (bytes32[])",
]);
// 1) count check
const count = await publicClient.readContract({
address: INDEXER,
abi: indexerAbi,
functionName: "getReceivedAttestationUIDCount",
args: [recipient, schemaUid],
});
// 2) most recent 5
const uids = await publicClient.readContract({
address: INDEXER,
abi: indexerAbi,
functionName: "getReceivedAttestationUIDs",
args: [recipient, schemaUid, 0n, 5n, true /* reverseOrder */],
}); Note: The Indexer lists only attestations someone has indexed with
indexAttestation(uid) (any account may call it; a schema's resolver may do it on attest). A plain attest does not appear here until then, and PCL's EAS_POLICY, which finds attestations only through the Indexer, rejects that sender with EasNoAttestationReceived. The Indexer tracks indexing, not validity: filter out revoked/expired attestations after fetching. EAS_POLICY runs the same revocation/expiration checks against getAttestation, on the most recently indexed one. Method 3 — eas-sdk (JS)
If you already use the official
@ethereum-attestation-service/eas-sdk in your stack, point it at the Maroo EAS address and use it normally. The SDK wraps getAttestation and the schema registry calls.const { EAS: EasSdk } = require("@ethereum-attestation-service/eas-sdk");
async function main() {
const eas = new EasSdk(EAS); // EAS: the address the precompile's getParams returned above
await eas.connect(provider);
const attestation = await eas.getAttestation(uid);
console.log(attestation);
}
main().catch((err) => {
console.error(err);
process.exit(1);
}); Note: The eas-sdk doesn't ship Indexer bindings — for reverse lookups you'll still call the Indexer directly via viem/ethers as in Method 2.
Method 4 — cast for ad-hoc terminal queries
When you just want to confirm a value from the shell.
# fetch one attestation
cast call $EAS "getAttestation(bytes32)" $ATTESTATION_UID --rpc-url $MAROO_RPC_URL
# count attestations a user has under a schema
cast call $INDEXER \
"getReceivedAttestationUIDCount(address,bytes32)(uint256)" \
$RECIPIENT $SCHEMA_UID --rpc-url $MAROO_RPC_URL Conclusion
All four paths read the same on-chain state. Use viem/ethers for app code, eas-sdk if you already depend on it, the Indexer for reverse lookups, and
cast for one-off shell checks.