Skip to main content

Prerequisites

  • Funded issuer wallet authorized on the target schema (authority or delegate)
  • Recipient meta-address (66-byte hex) for the full announce path
  • ethereumWalletClient or solanaWallet configured

Issue to a meta-address

When the recipient is a meta-address, issueAttestation resolves stealth material, submits attest, and publishes an announcement (default announce: true):
const { txHash, uid, stealthAddressHash } = await client.issueAttestation("ethereum", {
  schemaId,
  recipient: recipientMetaAddressHex,   // 66-byte hex
  fieldValues: {
    passed: "true",
    jurisdiction: "US",
  },
  expiration: { slotOrBlock: 9_999_999 },  // optional
  refUid: undefined,                        // optional chained credential
  announce: true,                           // default when recipient is meta-address
});

console.log({ txHash, uid, stealthAddressHash });
The announcement uses the V2 0xB2 metadata layout, so recipients discover it with discoverTraitsV2(rows, { chain }).

Issue to self (E2E test pattern)

const result = await client.issueAttestation("ethereum", {
  schemaId,
  recipient: client.getMetaAddressHex(),
  fieldValues: { passed: "true", note: "e2e" },
});

const issued = await client.getMyIssuedAttestations("ethereum");
const found = issued.find((a) => a.uid === result.uid);

Recipient formats

// Meta-address: full announce path
recipient: "0x" + "04".repeat(66)  // 132 hex chars

// Raw EVM stealth address: resolves to stealth_address_hash
recipient: "0x" + "ab".repeat(20)

// Precomputed hash: no announce (no ephemeral key available)
recipient: "0x" + "cd".repeat(32)

Legacy V1 issuer announce helpers

These helpers encode the older V1 0xA7 attestation id marker. Keep them only for legacy integrations. For schema-bound V2 attestations, prefer issueAttestation, which submits the attestation and publishes the matching V2 announcement.
const prep = client.prepareReputationAssignment(recipientMeta, attestationId);
const announce = client.buildAssignReputationTransaction(recipientMeta, attestationId);
await walletClient.sendTransaction({ to: announce.to, data: announce.data });
Metadata encoding:
const metadata = client.encodeReputationMetadata(viewTag, attestationId);

Verify issuance

import { decodeAttestationData, parseFieldDefs } from "@opaquecash/opaque";

const attestations = await client.getMyIssuedAttestations("ethereum");

for (const a of attestations) {
  // AttestationV2 carries the raw encoded payload; decode it with the schema's fields.
  const fields = decodeAttestationData(a.dataHex, parseFieldDefs(schema.fieldDefinitions));
  console.log(a.uid, a.schemaId, a.stealthAddressHash, fields);
}

Solana

Same API; pass chain: "solana":
await client.issueAttestation("solana", {
  schemaId,
  recipient: client.getMetaAddressHex(),
  fieldValues: { passed: "true", note: "devnet" },
});

Error cases

ErrorCause
Schema not foundInvalid schemaId or wrong chain
Not authorized issuerWallet is not authority/delegate
Recipient not registeredEOA lookup failed (send flows)
Expiry in the pastInvalid expiration / schemaExpiry