glyphdocs
API reference

Results and callback envelopes

Parse result unions and verify signed v2 callback envelopes.

Results and callback envelopes

Glyph returns a GlyphCallbackResponse. A result is not trusted merely because it has the expected JSON shape. For a signed callback, parse the result and verify every signed binding before using it.

Result union

type GlyphCallbackResponse =
  | GlyphSignedTransferCallback
  | GlyphSignedMessageCallback
  | GlyphConnectedCallback
  | GlyphVerifiedCallback
  | GlyphRejectedCallback;
statustypeResult fields
signedtransfer or sc_callnonce, identity, tx_hash, target_tick
signedsign_messagenonce, identity, signature, public_key
connectedconnectnonce, identity, permissions
verifiedverify_messagenonce, valid, identity
rejectedany request typenonce, reason: "user_rejected"

Transaction results contain the transaction hash and target tick. They do not contain a serialized transaction in the callback result type.

Parse an unsigned result shape

parseCallbackResponse() validates the JSON object and can enforce the request nonce and type:

import { parseCallbackResponse } from "@glyph-oss/connect";

const result = parseCallbackResponse(await request.json(), {
  nonce: requestRecord.nonce,
  type: requestRecord.type,
});

It throws for a non-object, an unknown request type, a nonce or type mismatch, malformed fields, an unknown rejection reason, or an unsupported status/type pair. Parsing is shape validation. It is not cryptographic verification.

Signed envelope helpers

A signed v2 body has this shape:

interface GlyphSignedCallbackEnvelope {
  version: "glyph-connect-callback-envelope/2";
  result: GlyphCallbackResponse;
  payload: GlyphCallbackSignaturePayload;
  proof: {
    algorithm: "qubic-schnorrq-sha256";
    identity: string;
    public_key: string;
    signature: string;
    signed_payload: string;
  };
}

Use isSignedCallbackEnvelope() for type narrowing. Use verifyCallbackEnvelope() to perform strict validation and the final dApp-side SchnorrQ check with @qubic.org/crypto@1.0.0. parseOrVerifyCallback() selects the signed verifier when it sees a signed envelope or when any signed-verification option requires one.

import { k12, verify } from "@qubic.org/crypto";

const result = await verifyCallbackEnvelope(body, {
  expected: { nonce: request.nonce, type: request.type },
  expectedRequestHash: envelope.request_hash,
  expectedNetwork: envelope.network,
  expectedDappOrigin: request.dapp.origin,
  expectedExp: request.exp ?? null,
  expectedCallbackUrl: envelope.callback,
  requireSigned: true,
  trustedPublicKeys: [trustedWalletPublicKey],
  verifySignature: ({ algorithm, payload, signature, publicKey }) => {
    if (algorithm !== "qubic-schnorrq-sha256") return false;
    return verify(k12(payload, 32), signature, publicKey);
  },
});

The SDK's verifySignature callback receives:

({
  algorithm: "qubic-schnorrq-sha256",
  payload: Uint8Array,    // UTF-8 canonical signed_payload
  signature: Uint8Array,  // decoded proof.signature
  publicKey: Uint8Array,  // decoded proof.public_key
  envelope,
}) => boolean | Promise<boolean>

@qubic.org/crypto exports verify(message, signature, publicKey), where message is the digest rather than the raw payload. For this callback format, call verify(k12(payload, 32), signature, publicKey) after checking algorithm === "qubic-schnorrq-sha256". This is dApp-side verification; Glyph Wallet is the signer and retains its key material. Do not add a browser private-key signer to this flow.

Verification performed by the SDK

For a signed envelope, verifyCallbackEnvelope():

  1. Parses result with the expected nonce and request type.
  2. Checks the callback envelope and payload version markers.
  3. Validates request_hash, network, nonce, request type, dApp origin, expiry, issued-at time, and relay binding shapes.
  4. Compares any expectedRequestHash, expectedNetwork, expectedDappOrigin, expectedExp, and expectedCallbackUrl values.
  5. Requires proof.signed_payload to equal canonical JSON of payload.
  6. Recomputes payload.result_hash from canonical JSON of result.
  7. Restricts proof.public_key to trustedPublicKeys when that option is supplied.
  8. Decodes the proof and calls the required verifySignature callback.

If the callback body is unsigned and requireSigned or any binding option is set, verification throws. If a signed body is supplied without verifySignature, verification also throws.

On this page