Handle results
Narrow connect, transfer, sign, verify, and rejection results safely.
Handle results
GlyphCallbackResponse is a discriminated union. Narrow on status and then on type. Do not infer success from transport completion alone. Relay and direct callback paths deliver approvals and user rejections through the same result handling step. See Understand Glyph Connect and Relay v2 for the delivery model.
import type { GlyphCallbackResponse } from "@glyph-oss/connect";
function describeResult(result: GlyphCallbackResponse): string {
switch (result.status) {
case "connected":
return `Connected ${result.identity} with ${result.permissions.join(", ")} permission(s)`;
case "signed":
if (result.type === "sign_message") {
return `Message signed by ${result.identity}: ${result.signature}`;
}
return `${result.type} signed by ${result.identity} at tick ${result.target_tick}: ${result.tx_hash}`;
case "verified":
return result.valid
? `Signature is valid for ${result.identity}`
: `Signature is invalid for ${result.identity}`;
case "rejected":
return `User rejected ${result.type}`;
}
}Connect results
A successful connect result has:
{
status: "connected",
type: "connect",
nonce: string,
identity: string,
permissions: ("transfer" | "sc_call" | "sign_message")[],
}Use the returned permissions for the current connection result. A connect result does not authorize a request type that is absent from that array.
Transfer and contract-call results
Both transfer and smart-contract call approvals return:
{
status: "signed",
type: "transfer" | "sc_call",
nonce: string,
identity: string,
tx_hash: string,
target_tick: number,
}tx_hash identifies the signed transaction and target_tick is the wallet-provided target tick. The callback union does not include raw transaction bytes.
Sign-message results
A signed message result returns the identity, signature, and public key:
{
status: "signed",
type: "sign_message",
nonce: string,
identity: string,
signature: string,
public_key: string,
}Verify the callback envelope before using either signature or public_key. The sign_message request asks Glyph Wallet to sign after user approval; it does not make the dApp a signer. For dApp-side Qubic verification, use k12 and verify from @qubic.org/crypto@1.0.0. If the message itself must be checked against the signature, perform that application-level check with the same verified library API after decoding the message signature and public key according to the protocol.
Verify-message results
A verify-message result reports the wallet's boolean answer:
{
status: "verified",
type: "verify_message",
nonce: string,
valid: boolean,
identity: string,
}valid: false is a valid completed result. It is different from a malformed callback or a failed cryptographic envelope verification.
Rejections and failures
A user rejection is a parsed result, not a thrown SDK error:
{
status: "rejected",
type: GlyphRequestType,
nonce: string,
reason: "user_rejected",
}Handle it in normal application control flow. Treat parser errors, signature failures, nonce mismatches, type mismatches, expired requests, and relay transport errors as failures that must not be converted into success.
Server pattern
A callback endpoint should keep the original request record, verify the signed envelope, then switch on the returned result:
import { k12, verify } from "@qubic.org/crypto";
const result = await verifyCallbackEnvelope(body, {
expected: { nonce: record.nonce, type: record.type },
expectedRequestHash: record.requestHash,
expectedNetwork: record.network,
expectedDappOrigin: record.dappOrigin,
expectedExp: record.exp,
expectedCallbackUrl: record.callbackUrl,
requireSigned: true,
trustedPublicKeys,
verifySignature: ({ algorithm, payload, signature, publicKey }) => {
if (algorithm !== "qubic-schnorrq-sha256") return false;
return verify(k12(payload, 32), signature, publicKey);
},
});
if (result.status === "rejected") {
return respondAlreadyHandled(result.type);
}
return persistVerifiedResult(result);Persist only after verification and after checking that the nonce has not already been handled by your application.
