Skip to main content

Reputation SDK Reference

Canonical function reference for:

  • @oma3/omatrust/reputation

This is the implementation contract for reputation workflows, including attestation submission, querying, verification, proof functions, and the controller witness API client.

For end-to-end workflow guides covering proof lifecycles, see the Attestations Guide and the OMATrust Specification.

High-Level vs Advanced APIs

Most developers should start with the high-level functions:

  • submitAttestation
  • prepareDelegatedAttestation
  • submitDelegatedAttestation
  • getAttestation
  • listAttestations
  • verifyAttestation
  • requestControllerWitness
  • getControllerAuthorization
  • verifySubjectOwnership

Advanced functions below are optional and intended for custom verifiers, specialized relays, and low-level integrations.

DID Canonicalization Note

For did:web identifiers, the SDK applies a canonical hostname normalization before building DIDs, hashing DIDs, deriving DID addresses, or verifying subject ownership.

Current did:web hostname normalization includes:

  • lowercasing the host
  • trimming surrounding whitespace
  • removing a trailing .
  • stripping a leading www.

As a result, did:web:www.example.com and did:web:example.com normalize to the same canonical DID in the SDK. This behavior affects helpers built on DID normalization, including normalizeDidWeb, buildDidWeb, computeDidHash, didToAddress, and verifyDidWebOwnership.

Core Types

type Hex = `0x${string}`;
type Did = string;

// All proof types defined in the OMATrust Proof Specification (§5.3)
type ProofType =
| "pop-jws" // JWS signature proof (§5.3.2)
| "pop-eip712" // EIP-712 typed data signature proof (§5.3.3)
| "x402-receipt" // x402 service receipt proof (§5.3.4)
| "evidence-pointer" // URL-based evidence pointer (§5.3.5)
| "tx-encoded-value" // Deterministic transfer amount proof (§5.3.6)
| "tx-interaction" // On-chain contract interaction proof (§5.3.7)
| "x402-offer"; // x402 signed offer proof (§5.3.8)

type ProofPurpose = "shared-control" | "commercial-tx";

type SchemaField = { name: string; type: string; value?: unknown };

type AttestationQueryResult = {
uid: Hex;
schema: Hex;
attester: Hex;
recipient: Hex;
revocable: boolean;
revocationTime: bigint;
expirationTime: bigint;
time: bigint;
refUID: Hex;
data: Record<string, unknown>;
raw?: string;
};

// OMATrust Proof wrapper (§5.3.1). All proofs share this envelope.
type ProofWrapper = {
proofType: ProofType;
proofObject: unknown; // Native proof object, shape depends on proofType
proofPurpose?: ProofPurpose;
version?: number; // Default 1
issuedAt?: number; // Unix timestamp
expiresAt?: number; // Unix timestamp
};

// Proof wrapper with typed proofObject for tx-encoded-value (§5.3.6)
type TxEncodedValueProof = ProofWrapper & {
proofType: "tx-encoded-value";
proofPurpose: ProofPurpose;
proofObject: {
chainId: string;
txHash: string;
};
};

// Proof wrapper with typed proofObject for tx-interaction (§5.3.7)
type TxInteractionProof = ProofWrapper & {
proofType: "tx-interaction";
proofPurpose: "commercial-tx";
proofObject: {
chainId: string;
txHash: string;
};
};

// Proof wrapper with typed proofObject for pop-eip712 (§5.3.3)
type PopEip712Proof = ProofWrapper & {
proofType: "pop-eip712";
proofObject: {
domain: { name: string; version: string; chainId: number };
message: {
signer: string;
authorizedEntity: string;
signingPurpose: string;
creationTimestamp: number;
expirationTimestamp: number;
randomValue: Hex;
statement: string;
};
signature: Hex;
};
};

// Proof wrapper with typed proofObject for pop-jws (§5.3.2)
type PopJwsProof = ProofWrapper & {
proofType: "pop-jws";
proofObject: string; // Compact JWS string
};

// Proof wrapper with typed proofObject for x402-receipt (§5.3.4)
type X402ReceiptProof = ProofWrapper & {
proofType: "x402-receipt";
proofPurpose: "commercial-tx";
proofObject: Record<string, unknown>; // x402 receipt per x402 spec
};

// Proof wrapper with typed proofObject for x402-offer (§5.3.8)
type X402OfferProof = ProofWrapper & {
proofType: "x402-offer";
proofPurpose: "commercial-tx";
proofObject: Record<string, unknown>; // x402 signed offer per x402 spec
};

// Proof wrapper with typed proofObject for evidence-pointer (§5.3.5)
type EvidencePointerProof = ProofWrapper & {
proofType: "evidence-pointer";
proofPurpose: "shared-control";
proofObject: {
url: string;
};
};

type ChainConstants = {
base: bigint;
range: bigint;
decimals: number;
nativeSymbol: string;
};

Error Handling

All functions throw OmaTrustError (extends Error) with a stable code property. Consumers can catch and switch on error.code.

class OmaTrustError extends Error {
code: string;
details?: unknown;
}
CodeThrown byDescription
INVALID_INPUTAll functions receiving malformed paramsMissing required fields, wrong types, empty strings
SCHEMA_NOT_FOUNDverifySchemaExists, getSchemaDetailsSchema UID does not exist on-chain
ATTESTATION_NOT_FOUNDgetAttestationAttestation UID does not exist or has been revoked
PROOF_VERIFICATION_FAILEDverifyAttestation, verifyProofOne or more proof checks failed
NETWORK_ERRORAny function making RPC or HTTP requestsProvider unreachable, timeout, HTTP error
UNSUPPORTED_CHAINcalculateTransferAmount, getChainConstants, hashSeedChain ID not in supported set
import { submitAttestation } from "@oma3/omatrust/reputation";

try {
await submitAttestation(params);
} catch (err) {
if (err.code === "NETWORK_ERROR") {
// retry or switch RPC
}
}

Functions

submitAttestation(params)

type SubmitAttestationParams = {
signer: unknown; // ethers v6 Signer
chainId: number;
easContractAddress: Hex;
schemaUid: Hex;
schema: SchemaField[] | string;
data: Record<string, unknown>;
revocable?: boolean;
expirationTime?: bigint | number;
refUid?: Hex;
value?: bigint | number;
};
type SubmitAttestationResult = { uid: Hex; txHash: Hex; receipt?: unknown };
function submitAttestation(params: SubmitAttestationParams): Promise<SubmitAttestationResult>;
  • Purpose: Submit a direct attestation on-chain.
  • The schema field accepts either an array of SchemaField objects or an EAS schema string (e.g., "string subject, uint8 rating, string comment").
  • The data object keys must match the name fields in the schema.
  • If the schema includes a subjectDidHash field and data contains a subject DID string, the hash is auto-computed. You do not need to hash it yourself.
  • Throws: INVALID_INPUT, NETWORK_ERROR

prepareDelegatedAttestation(params)

type PrepareDelegatedAttestationParams = {
chainId: number;
easContractAddress: Hex;
schemaUid: Hex;
schema: SchemaField[] | string;
data: Record<string, unknown>;
attester: Hex;
nonce: bigint | number;
revocable?: boolean;
expirationTime?: bigint | number;
refUid?: Hex;
value?: bigint | number;
deadline?: bigint | number;
};
type PrepareDelegatedAttestationResult = {
delegatedRequest: Record<string, unknown>;
typedData: {
domain: Record<string, unknown>;
types: Record<string, unknown>;
message: Record<string, unknown>;
};
};
function prepareDelegatedAttestation(params: PrepareDelegatedAttestationParams): Promise<PrepareDelegatedAttestationResult>;
  • Purpose: Build delegated attestation payload + EIP-712 typed data for signing.
  • Same schema/data semantics as submitAttestation (including auto subjectDidHash).
  • The returned typedData is passed directly to signer.signTypedData(domain, types, message).
  • Throws: INVALID_INPUT

submitDelegatedAttestation(params)

type SubmitDelegatedAttestationParams = {
relayUrl: string;
prepared: PrepareDelegatedAttestationResult;
signature: Hex | string;
attester?: Hex;
};
type SubmitDelegatedAttestationResult = { uid: Hex; txHash?: Hex; status: "submitted" | "confirmed" };
function submitDelegatedAttestation(params: SubmitDelegatedAttestationParams): Promise<SubmitDelegatedAttestationResult>;
  • Purpose: Submit a signed delegated attestation via relay/gateway.
  • The relayUrl is the endpoint that accepts delegated attestation payloads (e.g., your API route or a shared gateway). See the Delegated Attestation API for the relay contract.
  • Throws: INVALID_INPUT, NETWORK_ERROR

getAttestation(params)

type GetAttestationParams = {
uid: Hex;
provider: unknown; // ethers v6 Provider
easContractAddress: Hex;
schema?: SchemaField[] | string;
};
function getAttestation(params: GetAttestationParams): Promise<AttestationQueryResult>;
  • Purpose: Read and decode one attestation by UID.
  • If schema is provided, the raw attestation data is decoded into the data field of the result. Otherwise data will be empty and raw will contain the encoded bytes.
  • Throws: ATTESTATION_NOT_FOUND, NETWORK_ERROR

listAttestations(params)

type ListAttestationsParams = {
subjectDid: Did;
provider: unknown; // ethers v6 Provider
easContractAddress: Hex;
schemas?: Hex[];
limit?: number;
fromBlock?: number;
toBlock?: number;
};
function listAttestations(params: ListAttestationsParams): Promise<AttestationQueryResult[]>;
  • Purpose: Query attestations by subject DID and optional filters.
  • Computes the DID hash internally to query on-chain.
  • Throws: INVALID_INPUT, NETWORK_ERROR

verifyAttestation(params)

type VerifyAttestationParams = {
attestation: AttestationQueryResult;
provider?: unknown; // ethers v6 Provider (required for on-chain checks)
checks?: ProofType[]; // Which proof types to verify; omit to verify all present
context?: Record<string, unknown>;
};
type VerifyAttestationResult = {
valid: boolean;
checks: Record<string, boolean>;
reasons: string[];
};
function verifyAttestation(params: VerifyAttestationParams): Promise<VerifyAttestationResult>;
  • Purpose: Verify attestation validity plus proof checks.
  • If checks is omitted, runs all applicable checks based on the proof types present in the attestation's proofs array.
  • context is an optional bag of values passed to individual proof verifiers. Recognized keys: subjectDid (the subject DID) and controllerDid (the controller DID).
  • reasons contains human-readable explanations for any failed checks.
  • Throws: PROOF_VERIFICATION_FAILED, NETWORK_ERROR

callControllerWitness(params)

type CallControllerWitnessParams = {
gatewayUrl: string;
attestationUid: Hex;
chainId: number;
easContract: Hex;
schemaUid: Hex;
subject: Did;
controller: Did;
timeoutMs?: number;
};
type CallControllerWitnessResult = {
ok: boolean;
method: "dns-txt" | "did-json";
details?: unknown;
};
function callControllerWitness(params: CallControllerWitnessParams): Promise<CallControllerWitnessResult>;
  • Purpose: Call controller witness endpoint with automatic fallback (tries dns-txt first, falls back to did-json).
  • The gatewayUrl is passed in by the consumer — not hardcoded. You decide whether to call your own API route or a shared gateway. See the Controller Witness API for the raw endpoint contract.
  • Typically called after submitAttestation or submitDelegatedAttestation completes.
  • Throws: NETWORK_ERROR

requestControllerWitness(params)

type RequestControllerWitnessParams = {
subjectDid: Did;
controllerDid: Did;
gatewayUrl?: string; // Defaults to OMATrust production endpoint
chainId?: number; // Defaults to the API's active chain
timeoutMs?: number; // Default: 15000
};
type RequestControllerWitnessResult = {
success: boolean;
uid: string | null;
txHash: string;
blockNumber: number;
observedAt: number;
method: string;
};
function requestControllerWitness(params: RequestControllerWitnessParams): Promise<RequestControllerWitnessResult>;
  • Purpose: Request a controller witness attestation from the OMATrust backend. This is the recommended replacement for callControllerWitness.
  • Makes a single API call. The backend handles evidence discovery (DNS TXT, did.json), attestation submission, and write quota deduction.
  • Requires an authenticated session (cookie-based for web clients).
  • Throws: NETWORK_ERROR, API_ERROR

getControllerAuthorization(params)

type W3CKeyPurpose =
| "authentication"
| "assertionMethod"
| "keyAgreement";

type GetControllerAuthorizationParams = {
controllerDid: string; // Full DID: did:pkh:eip155:*:0x... or did:jwk:<encoded>
subjectDid: Did;
provider: unknown; // ethers v6 Provider
chain?: string; // CAIP-2 identifier, defaults to "eip155:6623"
easContractAddress?: Hex; // If omitted, resolved from trust anchors
fromBlock?: number; // Defaults to 0 (full history)
resolveTxt?: (host: string) => Promise<string[][]>;
fetchDidDocument?: (domain: string) => Promise<Record<string, unknown>>;
purpose?: W3CKeyPurpose[]; // Defaults to ["authentication", "assertionMethod"]
};

type ControllerWitnessEvidence = {
uid: Hex;
issuedAt: bigint;
attester: string;
method?: "dns" | "did-document" | "manual" | "other";
};

type ControllerAuthorizationResult = {
authorized: boolean;
anchoredFrom: bigint | null;
until: bigint | null;
currentlyVerified: boolean;
liveMethod: "dns" | "did-document" | null;
controllerWitnesses: ControllerWitnessEvidence[];
keyBindingUid: Hex | null;
keyPurposeStatus: "matched" | "unknown" | "mismatch" | "not-required";
};
function getControllerAuthorization(params: GetControllerAuthorizationParams): Promise<ControllerAuthorizationResult>;
  • Purpose: Determine the authorization window for a controller-subject pair. Returns the time range during which the controller was authorized to file subject-scoped attestations.
  • Breaking change in 0.1.0-alpha.11: Renamed from getAttesterAuthorization. The attester parameter (raw EVM address) is replaced by controllerDid (a full DID string). Now supports did:jwk controllers in addition to EVM addresses.
  • Controller witness matching uses isSameControllerId internally — exact DID comparison + EVM address fallback (chain-agnostic) + JWK material match.
  • Key binding matching uses the keyId field (DID) and publicKeyJwk field (for did:jwk).
  • Checks on-chain controller-witness attestations, key-binding revocation and purpose status, and live DNS/did.json verification.
  • The consumer calls this once per controller-subject pair, then filters their attestation list in memory using the returned window.
  • anchoredFrom is the earliest timestamp of durable authorization evidence (first controller witness). Null when authorization is only currently verified by live DNS/did.json.
  • until is the end of the authorization window, set when a key binding revocation closes the window. Null if the window is still open.
  • controllerWitnesses returns structured evidence with metadata (UID, timestamp, attester, method) in oldest-first order.
  • keyPurposeStatus indicates whether the key binding's purposes satisfy the requested purpose filter. "mismatch" blocks authorization even if witnesses exist.
  • If purpose is omitted, defaults to ["authentication", "assertionMethod"] which covers the normal signing/control use case.
  • If easContractAddress is omitted, the function fetches trust anchors and resolves the EAS address for the given chain. Throws UNSUPPORTED_CHAIN if the chain is not in the trust anchors.
  • Throws: UNSUPPORTED_CHAIN, INVALID_INPUT, NETWORK_ERROR

Migration from getAttesterAuthorization

// Before (0.1.0-alpha.10 and earlier):
import { getAttesterAuthorization } from "@oma3/omatrust/reputation";
const auth = await getAttesterAuthorization({
attester: "0x1234...abcd", // raw EVM address
subjectDid: "did:web:example.com",
provider,
});

// After (0.1.0-alpha.11):
import { getControllerAuthorization } from "@oma3/omatrust/reputation";
const auth = await getControllerAuthorization({
controllerDid: "did:pkh:eip155:1:0x1234...abcd", // full DID, not raw address
subjectDid: "did:web:example.com",
provider,
});

Key differences:

  • attester: HexcontrollerDid: string — accepts a full DID (did:pkh:eip155:*:0x... or did:jwk:<encoded>)
  • Now supports did:jwk controllers (not just EVM addresses)
  • Type renames: GetAttesterAuthorizationParamsGetControllerAuthorizationParams, AttesterAuthorizationResultControllerAuthorizationResult
  • No deprecated aliases — old names are removed

Verifier usage pattern

import { getControllerAuthorization, listAttestations } from "@oma3/omatrust/reputation";

// 1. Get the authorization window (one call per controller-subject pair)
const auth = await getControllerAuthorization({
controllerDid: "did:pkh:eip155:1:0xABC...",
subjectDid: "did:web:example.com",
provider,
});

// 2. Filter attestations by the authorization window
const attestations = await listAttestations({ subjectDid: "did:web:example.com", provider, easContractAddress });
const authorized = attestations.filter(att => {
if (!auth.authorized) return false;
if (auth.until && att.time > auth.until) return false;
if (auth.anchoredFrom && att.time >= auth.anchoredFrom) return true;
// Fallback: trust current DNS/did.json for recent attestations (consumer policy)
if (auth.currentlyVerified) {
const sevenDaysAgo = BigInt(Math.floor(Date.now() / 1000) - 7 * 86400);
return att.time >= sevenDaysAgo;
}
return false;
});

Advanced Attestation Functions

encodeAttestationData(schema, data)

function encodeAttestationData(
schema: SchemaField[] | string,
data: Record<string, unknown>
): Hex;
  • Purpose: ABI-encode attestation data using the EAS SchemaEncoder. This is what gets stored on-chain as the attestation's data field.
  • The schema parameter accepts either a SchemaField[] array or an EAS schema string (e.g., "string subject, uint8 rating").
  • The data object keys must match the schema field names exactly.
  • Returns: ABI-encoded bytes as a hex string.
  • Used internally by submitAttestation and prepareDelegatedAttestation. Call this directly only if you're building custom submission logic.
  • Throws: INVALID_INPUT

decodeAttestationData(schema, encodedData)

function decodeAttestationData(
schema: SchemaField[] | string,
encodedData: Hex
): Record<string, unknown>;
  • Purpose: Decode ABI-encoded attestation bytes back into a typed object.
  • Returns: Object keyed by schema field names with decoded values (e.g., { subject: "did:web:example.com", rating: 5n }).
  • Used internally by getAttestation when schema is provided. Call this directly if you have raw attestation bytes and need to decode them separately.
  • Throws: INVALID_INPUT

extractExpirationTime(data)

function extractExpirationTime(
data: Record<string, unknown>
): bigint | number | undefined;
  • Purpose: Extract the expiresAt timestamp from decoded attestation data.
  • Returns: Timestamp as bigint or number, or undefined if expiresAt is not present or is null.
  • String values containing only digits are converted to bigint.

validateAttestationData(schema, data)

type AttestationValidationError = {
schemaFieldName: string; // Name of the schema field that failed validation
expectedType: string; // ABI type declared in the schema (e.g., "uint256", "address")
providedType: string; // JS type of the value that was provided (e.g., "null", "string")
providedValue: unknown; // The actual value that was provided
};

function validateAttestationData(
schema: SchemaField[] | string,
data: Record<string, unknown>
): AttestationValidationError[];
  • Purpose: Validate attestation data against a schema before submitting on-chain. Returns an array of field-level errors for any values that don't match their declared ABI types.
  • Returns an empty array if all fields are valid.
  • Validates: uint*/int* (numeric values), string, string[], bool, address (via ethers isAddress), bytes, and fixed-size bytes1bytes32.
  • Used internally by encodeAttestationData. Call this directly if you want to surface validation errors to users before attempting a transaction.
  • This is a client-side check to prevent on-chain reverts from malformed data.

buildDelegatedAttestationTypedData(params)

function buildDelegatedAttestationTypedData(
params: PrepareDelegatedAttestationParams
): { domain: Record<string, unknown>; types: Record<string, unknown>; message: Record<string, unknown> };
  • Purpose: Build EIP-712 typed data for delegated attestation signing. This is the lower-level building block used by prepareDelegatedAttestation.
  • Call this directly only if you need the typed data without the full delegatedRequest wrapper.
  • Throws: INVALID_INPUT

buildDelegatedTypedDataFromEncoded(params)

type BuildDelegatedTypedDataFromEncodedParams = {
chainId: number;
easContractAddress: Hex;
schemaUid: Hex;
encodedData: Hex;
recipient: Hex;
attester: Hex;
nonce: bigint | number;
revocable?: boolean;
expirationTime?: bigint | number;
refUid?: Hex;
value?: bigint | number;
deadline?: bigint | number;
};
function buildDelegatedTypedDataFromEncoded(
params: BuildDelegatedTypedDataFromEncodedParams
): { domain: Record<string, unknown>; types: Record<string, unknown>; message: Record<string, unknown> };
  • Purpose: Build delegated attestation EIP-712 typed data from pre-encoded bytes.
  • Use this when your relay/server receives already-encoded attestation bytes and must independently rebuild typed data for verification or signing checks.
  • Unlike buildDelegatedAttestationTypedData, this function does not encode schema/data and does not resolve recipient from DID input. It uses encodedData and recipient exactly as provided.

splitSignature(signature)

function splitSignature(
signature: Hex | string
): { v: number; r: Hex; s: Hex };
  • Purpose: Split a 65-byte signature into {v, r, s} components for EAS contract calls.
  • Throws: INVALID_INPUT

getAttestationsForDid(params)

function getAttestationsForDid(
params: ListAttestationsParams
): Promise<AttestationQueryResult[]>;
  • Purpose: Low-level DID subject query helper. Same as listAttestations but without deduplication or sorting. Use listAttestations unless you need raw results.
  • Throws: NETWORK_ERROR

getLatestAttestations(params)

type GetLatestAttestationsParams = {
provider: unknown;
easContractAddress: Hex;
schemas?: Hex[];
limit?: number;
fromBlock?: number;
};
function getLatestAttestations(
params: GetLatestAttestationsParams
): Promise<AttestationQueryResult[]>;
  • Purpose: Fetch the most recent attestations across schemas, not filtered by subject. Useful for dashboards and monitoring.
  • Throws: NETWORK_ERROR

deduplicateReviews(attestations)

function deduplicateReviews(
attestations: AttestationQueryResult[]
): AttestationQueryResult[];
  • Purpose: Deduplicate User Review attestations by attester + subject + major version. When the same attester reviews the same subject multiple times, only the latest attestation (by timestamp) is kept. This implements the OMATrust supersession logic defined in the Reputation Specification §7.1.4.

calculateAverageUserReviewRating(attestations)

function calculateAverageUserReviewRating(
attestations: AttestationQueryResult[]
): number;
  • Purpose: Calculate the average ratingValue field across an array of User Review attestations. This function is specific to the User Review schema (Reputation Specification §7.1) and expects each attestation's data to contain a numeric ratingValue field (1–5).
  • Returns: Average as a floating-point number.

getMajorVersion(version)

function getMajorVersion(version: string): number;
  • Purpose: Extract the major version number from a semver string (e.g., "2.1.0"2).

verifySchemaExists(schemaRegistry, schemaUid)

function verifySchemaExists(
schemaRegistry: unknown,
schemaUid: Hex
): Promise<boolean>;
  • Purpose: Check if a schema UID exists in the EAS SchemaRegistry contract.
  • The schemaRegistry parameter is an EAS SDK SchemaRegistry instance, created via new SchemaRegistry(address) from @ethereum-attestation-service/eas-sdk. The SchemaRegistry is the on-chain contract that stores all registered attestation schemas — each schema gets a unique UID when registered, and this function checks whether a given UID exists.
  • Throws: NETWORK_ERROR

getSchemaDetails(schemaRegistry, schemaUid)

function getSchemaDetails(
schemaRegistry: unknown,
schemaUid: Hex
): Promise<{ uid: Hex; schema: string; resolver: Hex; revocable: boolean }>;
  • Purpose: Fetch the full schema record from the EAS SchemaRegistry contract.
  • Returns: Schema record with the schema string (e.g., "string subject, uint8 rating"), resolver address, and revocability flag.
  • Throws: SCHEMA_NOT_FOUND, NETWORK_ERROR

formatSchemaUid(schemaUid)

function formatSchemaUid(schemaUid: string): Hex;
  • Purpose: Ensure a schema UID has the 0x prefix and is lowercase. Normalizes inconsistent formatting.

Advanced Proof Functions

The OMATrust Proof Specification defines seven proof types. This section provides SDK functions for creating and verifying each type. For the full proof specification, see the OMATrust Proof Specification.

Proof Creation

createTxEncodedValueProof(chainId, txHash, purpose)

function createTxEncodedValueProof(
chainId: number,
txHash: Hex,
purpose: ProofPurpose
): TxEncodedValueProof;
  • Purpose: Build a tx-encoded-value proof wrapper from a completed native-value transaction (§5.3.6).

createTxInteractionProof(chainId, txHash)

function createTxInteractionProof(
chainId: number,
txHash: Hex
): TxInteractionProof;
  • Purpose: Build a tx-interaction proof wrapper from a completed smart contract transaction (§5.3.7). Used when a reviewer interacted with a contract-based service.

createPopEip712Proof(params)

type CreatePopEip712ProofParams = {
signer: string; // Subject address (the signer)
authorizedEntity: string; // Controller DID
signingPurpose: ProofPurpose;
chainId: number;
creationTimestamp?: number;
expirationTimestamp?: number;
randomValue?: Hex;
statement?: string; // Defaults to "This is not a transaction or asset approval."
};
function createPopEip712Proof(
params: CreatePopEip712ProofParams,
signFn: (typedData: Record<string, unknown>) => Promise<Hex>
): Promise<PopEip712Proof>;
  • Purpose: Build a pop-eip712 proof by constructing the canonical OMATrust EIP-712 typed data (§5.3.3) and signing it with the provided signing function.
  • The signFn callback receives the full EIP-712 typed data and should return the signature. This keeps the SDK signer-agnostic — you can use ethers, viem, or any wallet.
  • Throws: INVALID_INPUT

createPopJwsProof(params)

type CreatePopJwsProofParams = {
issuer: Did; // Subject DID
audience: Did; // Controller DID
purpose: ProofPurpose;
issuedAt?: number;
expiresAt?: number;
nonce?: string;
};
function createPopJwsProof(
params: CreatePopJwsProofParams,
signFn: (payload: Record<string, unknown>, header: Record<string, unknown>) => Promise<string>
): Promise<PopJwsProof>;
  • Purpose: Build a pop-jws proof by constructing the JWS payload and header per §5.3.2 and signing with the provided callback.
  • The signFn receives the JWS payload and header objects and should return the compact JWS string.
  • Throws: INVALID_INPUT

createEvidencePointerProof(url)

function createEvidencePointerProof(
url: string
): EvidencePointerProof;
  • Purpose: Build an evidence-pointer proof wrapper pointing to a publicly accessible URL containing evidence (§5.3.5). The URL should resolve to either an embedded cryptographic proof or a handle-link statement in v=1;controller=<DID> format.

createX402ReceiptProof(receipt)

function createX402ReceiptProof(
receipt: Record<string, unknown>
): X402ReceiptProof;
  • Purpose: Build an x402-receipt proof wrapper from an x402 service receipt object (§5.3.4). The receipt is the object from extensions["offer-receipt"].info.receipt in an x402 Settlement Response.

createX402OfferProof(offer)

function createX402OfferProof(
offer: Record<string, unknown>
): X402OfferProof;
  • Purpose: Build an x402-offer proof wrapper from an x402 signed offer object (§5.3.8). The offer is from extensions["offer-receipt"].info.offers[*] in an x402 Payment Requirements response.

Proof Verification

verifyProof(proof, context)

type VerifyProofParams = {
proof: ProofWrapper;
provider?: unknown; // ethers v6 Provider (required for on-chain proofs)
expectedSubjectDid?: Did;
expectedControllerDid?: Did;
};
type VerifyProofResult = {
valid: boolean;
proofType: ProofType;
reason?: string;
};
function verifyProof(
params: VerifyProofParams
): Promise<VerifyProofResult>;
  • Purpose: Verify a single proof wrapper of any type. Routes to the appropriate type-specific verification logic based on proof.proofType.
  • For tx-encoded-value: fetches the on-chain transaction and checks the transfer amount matches the deterministic value.
  • For tx-interaction: fetches the transaction and confirms sender/recipient match the expected attester/subject.
  • For pop-eip712: recovers the signer from the EIP-712 signature and validates against the canonical OMATrust schema.
  • For pop-jws: validates the JWS signature against the embedded JWK and checks claims.
  • For x402-receipt with format: "jws": performs cryptographic JWS verification, returns did:jwk as the durable controller DID.
  • For x402-receipt with format: "eip712": performs cryptographic EIP-712 verification, recovers the signer address.
  • For x402-offer with format: "jws": performs cryptographic JWS verification.
  • For x402-offer with format: "eip712": performs cryptographic EIP-712 verification.
  • For x402-receipt/x402-offer with other formats: backward-compatible shape-only validation.
  • For evidence-pointer: fetches the URL and validates the evidence artifact (embedded crypto proof or handle-link statement).
  • Throws: PROOF_VERIFICATION_FAILED, NETWORK_ERROR

x402 JWS Verification

These functions verify x402 signed offers and receipts that use JWS Compact Serialization. They support two key resolution paths: embedded jwk (self-contained, offline-verifiable) and kid resolution (resolves a DID URL to find the public key).

All JWS verification functions return a did:jwk as the durable controller DID. This is the identity you pass to getControllerAuthorization — not the kid DID URL, which is a mutable reference.

For the full verification → authorization flow, see Client Verification.

verifyX402JwsArtifact(artifact, options?)

type X402JwsArtifact = {
format: "jws";
signature: string; // JWS Compact Serialization
};

type JwsVerificationResult = {
valid: true;
header: Record<string, unknown>;
payload: Record<string, unknown>;
kid: string;
publicKeyJwk: Record<string, unknown>;
publicKeySource: "embedded-jwk" | "kid-resolution";
publicKeyDid: string; // did:jwk — durable controller DID
};

type JwsVerificationFailure = {
valid: false;
error: { code: string; message: string };
};

type JwsVerifyOptions = {
fetchDidDocument?: (domain: string) => Promise<Record<string, unknown>>;
};

function verifyX402JwsArtifact(
artifact: X402JwsArtifact,
options?: JwsVerifyOptions
): Promise<JwsVerificationResult | JwsVerificationFailure>;
  • Purpose: Core JWS verification for any x402 artifact. Parses the compact JWS, obtains the public key (from embedded jwk or by resolving kid), verifies the signature, and derives the did:jwk.
  • Does not validate payload shape — use verifyX402JwsOffer or verifyX402JwsReceipt for payload validation.
  • When both kid and jwk are present, uses jwk for verification and rejects if kid resolves to a conflicting key.
  • Throws: PROOF_VERIFICATION_FAILED

verifyX402JwsOffer(artifact, options?)

function verifyX402JwsOffer(
artifact: X402JwsArtifact,
options?: JwsVerifyOptions
): Promise<JwsVerificationResult | JwsVerificationFailure>;
  • Purpose: Verify a JWS-signed x402 offer. Performs signature verification and validates the offer payload contains required fields: version, resourceUrl, scheme, network, asset, payTo, amount.
  • Throws: PROOF_VERIFICATION_FAILED

verifyX402JwsReceipt(artifact, options?)

function verifyX402JwsReceipt(
artifact: X402JwsArtifact,
options?: JwsVerifyOptions
): Promise<JwsVerificationResult | JwsVerificationFailure>;
  • Purpose: Verify a JWS-signed x402 receipt. Performs signature verification and validates the receipt payload contains required fields: version, network, resourceUrl, payer, issuedAt.
  • Throws: PROOF_VERIFICATION_FAILED

x402 EIP-712 Verification

These functions verify x402 signed offers and receipts that use EIP-712 typed-data signatures. The signer address is recovered directly from the signature — no DID resolution needed.

All EIP-712 artifacts use a fixed domain (name: "x402 offer"/"x402 receipt", version: "1", chainId: 1). The chainId: 1 is intentional — EIP-712 is used as an off-chain signing format; the actual payment network is in the payload.

verifyX402Eip712Artifact(artifact, artifactType)

type X402Eip712Artifact = {
format: "eip712";
payload: Record<string, unknown>;
signature: string; // hex-encoded, 0x-prefixed, 65 bytes
};

type Eip712VerificationResult = {
valid: true;
payload: Record<string, unknown>;
signer: string; // recovered EVM address (checksummed)
artifactType: "offer" | "receipt";
};

type Eip712VerificationFailure = {
valid: false;
error: { code: string; message: string };
payload?: Record<string, unknown>;
signer?: string;
};

function verifyX402Eip712Artifact(
artifact: X402Eip712Artifact,
artifactType: "offer" | "receipt"
): Eip712VerificationResult | Eip712VerificationFailure;
  • Purpose: Core EIP-712 verification for any x402 artifact. Constructs the canonical EIP-712 typed data, recovers the signer address, and validates the payload shape.
  • Throws: PROOF_VERIFICATION_FAILED

verifyX402Eip712Offer(artifact)

function verifyX402Eip712Offer(
artifact: X402Eip712Artifact
): Eip712VerificationResult | Eip712VerificationFailure;
  • Purpose: Verify an EIP-712-signed x402 offer. Recovers the signer and validates offer payload fields.

verifyX402Eip712Receipt(artifact)

function verifyX402Eip712Receipt(
artifact: X402Eip712Artifact
): Eip712VerificationResult | Eip712VerificationFailure;
  • Purpose: Verify an EIP-712-signed x402 receipt. Recovers the signer and validates receipt payload fields.

tx-encoded-value Helpers

calculateTransferAmount(subject, counterparty, chainId, purpose)

function calculateTransferAmount(
subject: Did,
counterparty: Did,
chainId: number,
purpose: ProofPurpose
): bigint;
  • Purpose: Compute the deterministic transfer amount for a tx-encoded-value proof (§5.3.6).
  • The amount is derived from the DID hashes of both parties, the chain ID, and the proof purpose. It is deterministic — the same inputs always produce the same amount.
  • Returns: Amount in the chain's smallest unit (e.g., wei for EVM chains).
  • Throws: INVALID_INPUT, UNSUPPORTED_CHAIN

calculateTransferAmountFromAddresses(subjectAddress, counterpartyAddress, chainId, purpose)

function calculateTransferAmountFromAddresses(
subjectAddress: string,
counterpartyAddress: string,
chainId: number,
purpose: ProofPurpose
): bigint;
  • Purpose: Convenience wrapper that accepts raw addresses instead of DIDs. Internally builds did:pkh DIDs from the addresses and delegates to calculateTransferAmount.
  • Throws: INVALID_INPUT, UNSUPPORTED_CHAIN

constructSeed(subjectDidHash, counterpartyDidHash, purpose)

function constructSeed(
subjectDidHash: Hex,
counterpartyDidHash: Hex,
purpose: ProofPurpose
): Uint8Array;
  • Purpose: Build the canonical JCS (RFC 8785) seed object per §5.3.6.3, canonicalize it, and return the UTF-8 bytes. This seed is the input to hashSeed.

hashSeed(seedBytes, chainId)

function hashSeed(
seedBytes: Uint8Array,
chainId: number
): Hex;
  • Purpose: Hash the seed bytes using the chain-appropriate algorithm (keccak256 for EVM chains, SHA-256 for others).
  • Throws: UNSUPPORTED_CHAIN

getChainConstants(chainId, purpose)

function getChainConstants(
chainId: number,
purpose: ProofPurpose
): ChainConstants;
  • Purpose: Return the BASE and RANGE constants for a given chain and proof purpose. These are spec-defined values (Appendix A) used in the transfer amount calculation.
  • Returns: ChainConstants with base (minimum amount), range (amount range), decimals (native token decimals), and nativeSymbol (e.g., "ETH", "MATIC").
  • Throws: UNSUPPORTED_CHAIN

formatTransferAmount(amount, chainId)

function formatTransferAmount(
amount: bigint | number,
chainId: number
): string;
  • Purpose: Format a transfer amount for human-readable display (e.g., "0.000000000000001234 ETH").
  • Throws: UNSUPPORTED_CHAIN

getSupportedChainIds()

function getSupportedChainIds(): number[];
  • Purpose: List all chain IDs that support tx-encoded-value proofs.

isChainSupported(chainId)

function isChainSupported(chainId: number): boolean;
  • Purpose: Check if a specific chain ID supports tx-encoded-value proofs.

evidence-pointer Helpers

verifyDnsTxtControllerDid(domain, expectedControllerDid)

function verifyDnsTxtControllerDid(
domain: string,
expectedControllerDid: Did,
options?: {
resolveTxt?: (host: string) => Promise<string[][]>;
recordPrefix?: string;
}
): Promise<{ valid: boolean; record?: string; reason?: string }>;
  • Purpose: Verify that the _controllers.<domain> DNS TXT record contains the expected controller DID.
  • Uses isSameControllerId internally for matching — handles did:jwk and chain-agnostic EVM address comparison.
  • recordPrefix defaults to _controllers.
  • In Node.js/server runtimes, the default export resolves DNS TXT directly.
  • In browser bundles, DNS TXT verification requires an injected resolveTxt function. Without one, the browser export throws NETWORK_ERROR.
  • Throws: NETWORK_ERROR

parseDnsTxtRecord(record)

function parseDnsTxtRecord(
record: string
): { version?: string; controllers: Did[]; controller?: Did };
  • Purpose: Parse a DNS TXT record or evidence string in v=1;controller=did:pkh:... format. A single record may contain multiple controller= entries (e.g., v=1;controller=did:pkh:eip155:66238:0x...;controller=did:pkh:eip155:1:0x...).
  • Returns controllers — an array of all controller DIDs found in the record. Use this field to iterate over all declared controllers.
  • Throws: INVALID_INPUT

buildDnsTxtRecord(controllerDid)

function buildDnsTxtRecord(controllerDid: Did): string;
  • Purpose: Build the expected v=1;controller=<DID> evidence string for a given controller DID.

fetchDidDocument(domain)

function fetchDidDocument(
domain: string
): Promise<Record<string, unknown>>;
  • Purpose: Fetch https://<domain>/.well-known/did.json and return the parsed DID document.
  • Throws: NETWORK_ERROR

verifyDidJsonControllerDid(domain, expectedControllerDid, options?)

function verifyDidJsonControllerDid(
domain: string,
expectedControllerDid: Did,
options?: {
fetchDidDocument?: (domain: string) => Promise<Record<string, unknown>>;
}
): Promise<{ valid: boolean; reason?: string }>;
  • Purpose: Verify that https://<domain>/.well-known/did.json contains the expected controller DID.
  • This is the .well-known counterpart to verifyDnsTxtControllerDid.
  • Internally, it fetches the DID document and then applies the same controller-address matching logic as verifyDidDocumentControllerDid.
  • Throws: INVALID_INPUT, NETWORK_ERROR

verifyDidDocumentControllerDid(didDocument, expectedControllerDid)

function verifyDidDocumentControllerDid(
didDocument: Record<string, unknown>,
expectedControllerDid: Did
): { valid: boolean; reason?: string };
  • Purpose: Verify that a DID document's controller or verificationMethod addresses match the expected controller DID.
  • For did:jwk controllers, compares against publicKeyJwk in verification methods.
  • For did:pkh, compares EVM addresses (chain-agnostic).

extractEvmAddressesFromDidDocument(didDocument)

function extractEvmAddressesFromDidDocument(
didDocument: Record<string, unknown>
): string[];
  • Purpose: Extract all checksummed EVM addresses from a DID document's verificationMethod array (from blockchainAccountId and publicKeyHex fields).
  • Renamed in 0.1.0-alpha.11 from extractAddressesFromDidDocument to clarify it is EVM-specific.

extractJwksFromDidDocument(didDocument)

function extractJwksFromDidDocument(
didDocument: Record<string, unknown>
): Array<Record<string, unknown>>;
  • Purpose: Extract publicKeyJwk objects from verification methods in a DID document.
  • Returns an array of JWK objects found in the document's verification methods.

Subject Ownership Helpers

These functions are the SDK-level verification primitives for subject ownership. They are the closest reusable equivalent to the verification portion of app-registry-frontend's verify-and-attest flow, but they do not write attestations or perform app-specific orchestration.

verifyDidWebOwnership(params)

type VerifyDidWebOwnershipParams = {
subjectDid: Did;
connectedWalletDid: Did; // must be did:pkh
resolveTxt?: (host: string) => Promise<string[][]>;
recordPrefix?: string; // defaults to "_controllers"
fetchDidDocument?: (domain: string) => Promise<Record<string, unknown>>;
};

type SubjectOwnershipVerificationResult = {
valid: boolean;
method?: "dns" | "did-document" | "wallet" | "contract" | "minting-wallet" | "transfer";
reason?: string;
details?: string;
subjectDid: Did;
connectedWalletDid: Did;
controllingWalletDid?: Did;
};

function verifyDidWebOwnership(
params: VerifyDidWebOwnershipParams
): Promise<SubjectOwnershipVerificationResult>;
  • Purpose: Verify ownership of a did:web subject against a connected wallet DID.
  • The subjectDid is canonicalized before verification. For did:web, that includes lowercasing the host, removing a trailing ., and stripping a leading www..
  • Verification order:
    • DNS TXT check at _controllers.<domain>
    • DID document check at https://<domain>/.well-known/did.json
  • Returns method: "dns" or method: "did-document" on success.
  • Best used in backend/server runtimes. In browser runtimes, provide resolveTxt if you want DNS verification there.
  • Throws: INVALID_INPUT, NETWORK_ERROR

verifyDidPkhOwnership(params)

type EvmOwnershipProvider = {
call(transaction: { to: string; data: string }): Promise<string>;
getCode(address: string): Promise<string>;
getStorage(address: string, slot: string): Promise<string>;
getTransaction(hash: string): Promise<{
from?: string | null;
to?: string | null;
value?: bigint | string | number | null;
blockNumber?: number | null;
} | null>;
getTransactionReceipt(hash: string): Promise<{ blockNumber: number } | null>;
getBlockNumber(): Promise<number>;
getBlock(blockNumber: number): Promise<{ timestamp: number } | null>;
};

type VerifyDidPkhOwnershipParams = {
subjectDid: Did; // must be an EVM did:pkh
connectedWalletDid: Did; // must be did:pkh
provider: EvmOwnershipProvider;
txHash?: Hex | string;
};

function verifyDidPkhOwnership(
params: VerifyDidPkhOwnershipParams
): Promise<SubjectOwnershipVerificationResult>;
  • Purpose: Verify ownership of an EVM did:pkh subject.
  • Supported cases:
    • direct wallet DID match
    • contract ownership via owner()
    • contract ownership via admin()
    • contract ownership via getOwner()
    • EIP-1967 admin slot lookup
    • transfer-proof verification when txHash is provided
  • Returns one of:
    • method: "wallet"
    • method: "contract"
    • method: "minting-wallet"
    • method: "transfer"
  • Throws: INVALID_INPUT, NETWORK_ERROR

verifySubjectOwnership(params)

type VerifySubjectOwnershipParams =
| VerifyDidWebOwnershipParams
| VerifyDidPkhOwnershipParams;

function verifySubjectOwnership(
params: VerifySubjectOwnershipParams
): Promise<SubjectOwnershipVerificationResult>;
  • Purpose: Dispatch subject ownership verification based on the DID method.
  • did:web subjects route to verifyDidWebOwnership.
  • did:pkh subjects route to verifyDidPkhOwnership.
  • Throws INVALID_INPUT for unsupported DID methods or missing required inputs such as the EVM provider for did:pkh.

EIP-712 Helpers

verifyEip712Signature(typedData, signature)

function verifyEip712Signature(
typedData: { domain: Record<string, unknown>; types: Record<string, unknown>; message: Record<string, unknown> },
signature: Hex | string
): { valid: boolean; signer?: string };
  • Purpose: Recover the signer address from an EIP-712 typed data signature and verify it.
  • Throws: INVALID_INPUT

buildEip712Domain(name, version, chainId, verifyingContract)

function buildEip712Domain(
name: string,
version: string,
chainId: number,
verifyingContract: Hex
): { name: string; version: string; chainId: number; verifyingContract: Hex };
  • Purpose: Build a standard EIP-712 domain separator object.

getOmaTrustProofEip712Types()

function getOmaTrustProofEip712Types(): {
primaryType: string;
types: Record<string, Array<{ name: string; type: string }>>;
};
  • Purpose: Return the canonical OMATrust EIP-712 types and primaryType for pop-eip712 proofs as defined in the Proof Specification §5.3.3. Useful for building signing requests or verifying proofs without hardcoding the schema.

Explorer Helpers

getExplorerTxUrl(chainId, txHash)

function getExplorerTxUrl(chainId: number, txHash: Hex): string;
  • Purpose: Build a block explorer URL for a transaction (e.g., Etherscan, Basescan).
  • Throws: UNSUPPORTED_CHAIN

getExplorerAddressUrl(chainId, address)

function getExplorerAddressUrl(chainId: number, address: string): string;
  • Purpose: Build a block explorer URL for an address.
  • Throws: UNSUPPORTED_CHAIN