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;status | type | Result fields |
|---|---|---|
signed | transfer or sc_call | nonce, identity, tx_hash, target_tick |
signed | sign_message | nonce, identity, signature, public_key |
connected | connect | nonce, identity, permissions |
verified | verify_message | nonce, valid, identity |
rejected | any request type | nonce, 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():
- Parses
resultwith the expected nonce and request type. - Checks the callback envelope and payload version markers.
- Validates
request_hash, network, nonce, request type, dApp origin, expiry, issued-at time, and relay binding shapes. - Compares any
expectedRequestHash,expectedNetwork,expectedDappOrigin,expectedExp, andexpectedCallbackUrlvalues. - Requires
proof.signed_payloadto equal canonical JSON ofpayload. - Recomputes
payload.result_hashfrom canonical JSON ofresult. - Restricts
proof.public_keytotrustedPublicKeyswhen that option is supplied. - Decodes the proof and calls the required
verifySignaturecallback.
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.
