Skip to content

Off-chain attestations

The same schemas, the same fields, but the attestation is signed and stored locally instead of broadcast to chain. Zero gas. Verifiable cryptographically by anyone with the envelope and the signer’s address.

  • Free-tier flows where users don’t have crypto and you don’t want to bear gas.
  • High-volume pipelines where on-chain cost is prohibitive.
  • Pre-publication staging: sign now, decide later whether to bring it on-chain.
  • Privacy-sensitive content where even publishing the hash is undesirable until needed.

Complete, copy-pasteable runnable. Off-chain signing needs a private key but no RPC connectivity. See Configuration for every constructor option.

import { Fidemark, getNetwork } from "@fidemark/sdk";
const fidemark = new Fidemark({
network: getNetwork("base-sepolia"),
privateKey: process.env.PRIVATE_KEY,
});
const envelope = await fidemark.attestHumanOffchain({
content: myArticle,
contentType: "text/article",
});
const aiEnvelope = await fidemark.attestAIOffchain({
content: aiOutput,
modelId: "claude-sonnet-4-6",
provider: "anthropic",
prompt: userPrompt,
});

All three return an OffchainEnvelope with: type, uid (EAS-derived, deterministic from the signed payload), attester, network, signed (the full EAS V2 payload + signature), and decoded convenience fields. The envelope is JSON-serializable; share it over any transport (HTTP, IPFS, Slack, email).

const valid = fidemark.verifyOffchain(envelope);
// true iff signature recovers to envelope.attester AND payload + network match

Pure cryptography, no RPC. The check fails when:

  • The signature doesn’t recover to envelope.attester (tampered signed payload, wrong attester).
  • The envelope was signed for a different chain or EAS contract.
  • The schema in the signed payload doesn’t match this network’s expected schema for envelope.type.

A signed envelope can be promoted to an on-chain attestation at any time, and someone other than the original attester can pay gas to do it. The mechanism is EAS’s delegated-attestation flow, available in all three SDKs.

Alice (the original attester) signs an off-chain envelope and asks the SDK to also produce the matching delegated EIP-712 signature.

import { Fidemark, getNetwork } from "@fidemark/sdk";
const alice = new Fidemark({
network: getNetwork("base-sepolia"),
privateKey: process.env.ALICE_KEY,
});
const envelope = await alice.attestHumanOffchain(
{ content: myArticle, contentType: "text/article" },
{ signWithDelegated: true },
);
// envelope.delegated is now populated: a second EIP-712 signature, plus
// the attester's nonce and an optional deadline.

Bob (any wallet with funds) takes Alice’s signed envelope and publishes it on-chain. The on-chain record names Alice as the attester; Bob just paid gas.

import { Fidemark, getNetwork } from "@fidemark/sdk";
const bob = new Fidemark({
network: getNetwork("base-sepolia"),
privateKey: process.env.BOB_KEY,
});
const result = await bob.publishOffchain(envelope);
// result.uid : NEW on-chain UID (different from envelope.uid)
// result.txHash : bob's transaction hash
// result.verifyUrl: public verify URL

On-chain, attestation.attester is recorded as alice, not bob. Bob just paid gas.

  • The on-chain UID differs from the off-chain UID. EAS stamps time = block.timestamp when the attestation lands, and the UID is hashed from the attestation data including time. The off-chain envelope remains independently verifiable forever.
  • Each delegated signature is single-use. EAS’s nonce increments after the publish; the same envelope can’t be replayed.
  • Mismatched network = rejection. publishOffchain throws INVALID_INPUT before any RPC call.
  • No delegated signature, no promotion. Envelopes signed without signWithDelegated: true can still be verified off-chain forever, but a third party can’t bring them on-chain.
PropertyOn-chainOff-chain
Gas cost (signer)~$0.001-0.09 on Base$0
StorageEAS contractwherever you put the envelope
VerificationRPC callpure crypto
Revocationon-chain revokenot yet, bring on-chain first via publishOffchain
Bring on-chain latern/ayes, sign with signWithDelegated: true
Cross-protocol indexingEAS GraphQL, Etherscannone until promoted on-chain