Skip to content

Solana ​

This page covers Solana: how a wallet proves control, how Bluprynt records the proof, and what @bluprynt/sdk-chain-solana re-checks. For the report format, see Reading the report.

How wallets prove control on Solana ​

A Solana wallet is an Ed25519 key pair. The address is the 32-byte public key, written in base58. So the address is all a verifier needs to check a signature. There's nothing to recover and no key to look up.

A wallet proves control by signing a message off-chain. There are two ways it gets signed:

  • Raw. The wallet signs the message's UTF-8 bytes as they are. Wallet apps do this when asked to sign a message.
  • Off-chain message format. The signed bytes start with a header that no transaction can start with. The Solana CLI signs this way, with solana sign-offchain-message. The header makes it impossible to trick someone into signing a transaction that looks like a message.

The two produce different signatures for the same text. A verifier has to know which one was used.

Tokens live in a program. SPL Token is the original token program. Token-2022 is a newer one with extensions. In both, each token has a mint account that holds two optional authorities:

  • the mint authority, which can create new tokens, and
  • the freeze authority, which can freeze holders' token accounts.

Either authority can be a plain wallet or an SPL multisig: an account owned by the token program that lists up to 11 signers and needs m of them to act.

How Bluprynt verifies it ​

The flows are described in Wallet verification. On Solana:

The SDK re-checks signature verifications:

methodUsed forStored evidence
signature / solana_rawWallet appsmessage, signature (base58)
signature / solana_offchain_v0The Solana CLImessage, signature (base58)
signature with no schemeOlder recordsmessage, signature. The SDK tries both formats
admin_manualSet by handNothing to re-check

Every verification also names the wallet_address and the authority it backs. On Solana, authorities are usually mint_authority or freeze_authority.

What the SDK checks ​

Offline, with no at, the SDK verifies the signature against the wallet address. It never calls an RPC.

With at, it reads the token's accounts. A Solana RPC can only answer for the current state, so only "now" reads anything. The SDK doesn't set a commitment, so reads use confirmed, the default of Kit's createSolanaRpc.

The package uses @solana/kit, and the @solana-program/token and @solana-program/token-2022 clients to decode accounts.

QuestionHow it's answered
Did this wallet sign the message?verifySignature from @solana/kit, with the public key taken from the address. Offline
Is the authority still the mint or freeze authority?getAccountInfo on the mint, decoded with the SPL Token or Token-2022 layout. At "now"
Does the multisig have the claimed signers and threshold?getAccountInfo on the multisig account. At "now"

The Solana checks reference lists every step and reason by name.

Quirks ​

Raw and off-chain signatures differ. The legacy off-chain format the CLI signs puts a 20-byte header before the message:

BytesFieldValue
16Signing domain\xff followed by solana offchain
1Version0
1Format0 (restricted ASCII)
2Message lengthlittle-endian u16

The wallet signs the whole buffer: the header followed by the message bytes (header || message). A signature over the raw message doesn't verify over these bytes, and the other way round. A record that names the wrong scheme is invalid.

Format 0 means restricted ASCII: printable characters only (0x20–0x7e), no newlines. Bluprynt's CLI challenge is one line of printable ASCII, so the CLI signs it as format 0. The SDK always uses format 0. A message the CLI signs as format 1 or 2 (for example one with a newline) won't verify. Bluprynt's multi-line wallet challenge is signed raw, so its format doesn't matter.

Two things called "version 0". The scheme name solana_offchain_v0 refers to the legacy header above, whose version byte is 0. @solana/kit has its own off-chain message codec, and it also calls its layout "version 0". That layout is newer: it adds an application domain and a list of signers to the header. Its bytes differ from what the CLI signs, so the SDK doesn't use it. It builds the legacy header with Kit's generic codecs instead. That header is the one piece the SDK and the example build by hand. Everything else comes from Kit.

Signatures are base58. A signature is 64 bytes, stored as a base58 string. The SDK decodes it before verifying.

SPL Token and Token-2022. Both programs use the same mint and multisig layouts for the fields the SDK reads. The SDK accepts accounts owned by either program. An account owned by any other program fails the … is owned by a token program step.

Token-2022 extensions aren't checked. The SDK reads only the base mint's mint and freeze authorities. A Token-2022 mint can carry extensions with their own authorities, such as a permanent delegate, a transfer hook, a mint close authority or a pausable authority. A permanent delegate can transfer or burn tokens from any holder's account. A transfer hook runs a program on every transfer. A pause authority can stop transfers. A wallet that shows a badge should know that a confirmed mint authority says nothing about these. Read the mint's extensions yourself if they matter to you.

SPL multisig signers are compared as a set. An SPL multisig account has 11 signer slots, of which the first n are in use. The SDK reads those n signers and compares them with the claimed signers as a set: order doesn't matter. It compares the threshold m only when the claim has one.

Squads vaults and other PDA authorities aren't supported. A Squads vault is a program-derived address (PDA) controlled by the Squads program. It isn't an SPL Token multisig. Listed as a multisig, it fails multisig is owned by a token program, or multisig account exists if the vault account was never funded. Listed as a regular authority, it can't sign a message, because a PDA has no private key.

Only "now". A Solana RPC serves current account state. It can't tell you who the mint authority was last month. The SDK's Solana checker doesn't try to read historical state, so every moment except "now" is unverifiable with Solana RPC cannot read historical account state. That includes signature verifications.

Addresses are exact. Base58 is case-sensitive. The SDK compares Solana addresses byte for byte. Changing the case of one letter makes a different address.

Check it yourself ​

To re-check a verification by hand, verify its signature as shown below. Then confirm that the wallet_address is the authority's address or one of its signers, and that the authority's type and address appear in the token's authorities.

Synthetic data (fixed public test keys) ​

This script uses a test key pair built from fixed bytes. It's public, holds nothing, and must never be used on-chain. The script signs Bluprynt's multi-line wallet challenge raw, as a wallet app would, and the one-line CLI challenge in the legacy off-chain format, as the CLI would. It builds a lookup in the shape the API returns for a Solana token. It then checks the signatures twice: with Kit alone, and through the SDK. Neither needs a network.

ts
import { fileURLToPath } from "node:url";
import {
  checkKyiVerification,
  verifyKyiResponse,
} from "@bluprynt/sdk-chain-solana";
import {
  type Address,
  createKeyPairFromPrivateKeyBytes,
  fixCodecSize,
  getAddressFromPublicKey,
  getBase16Decoder,
  getBase58Decoder,
  getBase58Encoder,
  getBytesCodec,
  getPublicKeyFromAddress,
  getTupleCodec,
  getU8Codec,
  getU16Codec,
  getUtf8Codec,
  type ReadonlyUint8Array,
  type SignatureBytes,
  signBytes,
  verifySignature,
} from "@solana/kit";

type KyiTokenLookup = Parameters<typeof verifyKyiResponse>[0];
type KyiVerification = Parameters<typeof checkKyiVerification>[0];
type KyiSignatureScheme = "solana_raw" | "solana_offchain_v0" | null;

// Public test keys: anyone can rebuild them from these bytes, so they must never hold funds or authority on-chain.
const WALLET_SECRET = new Uint8Array(32).fill(1);
const MINT_SECRET = new Uint8Array(32).fill(2);

const CAIP2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
const VERIFIED_AT = "2026-10-01T12:00:00.000Z";

const utf8 = getUtf8Codec();
const legacyOffchain = getTupleCodec([
  fixCodecSize(getBytesCodec(), 16),
  getU8Codec(),
  getU8Codec(),
  getU16Codec(),
  getBytesCodec(),
]);
const signingDomain = Uint8Array.from([
  0xff,
  ...utf8.encode("solana offchain"),
]);

function legacyOffchainMessage(message: string): ReadonlyUint8Array {
  const body = utf8.encode(message);
  return legacyOffchain.encode([signingDomain, 0, 0, body.length, body]);
}

async function verifiesFor(
  wallet: Address,
  signature: string,
  bytes: ReadonlyUint8Array,
) {
  return verifySignature(
    await getPublicKeyFromAddress(wallet),
    getBase58Encoder().encode(signature) as SignatureBytes,
    bytes,
  );
}

export async function run() {
  const keys = await createKeyPairFromPrivateKeyBytes(WALLET_SECRET);
  const wallet = await getAddressFromPublicKey(keys.publicKey);
  const mint = await getAddressFromPublicKey(
    (await createKeyPairFromPrivateKeyBytes(MINT_SECRET)).publicKey,
  );

  const walletMessage = [
    "Please, sign this message to verify your Bluprynt KYI credentials",
    "",
    "Purpose: Verify wallet ownership",
    "Issued to: example",
    `Address: ${wallet}::1790000000000::example-nonce`,
  ].join("\n");
  const cliMessage = `Bluprynt wallet verification || Issued to: example || Address: ${wallet}::1790000000000::example-nonce`;
  const offchainBytes = legacyOffchainMessage(cliMessage);
  const base58 = getBase58Decoder();
  const rawSignature = base58.decode(
    await signBytes(keys.privateKey, utf8.encode(walletMessage)),
  );
  const offchainSignature = base58.decode(
    await signBytes(keys.privateKey, offchainBytes),
  );
  const formats = async (signature: string, message: string) => ({
    overRawMessage: await verifiesFor(wallet, signature, utf8.encode(message)),
    overOffchainMessage: await verifiesFor(
      wallet,
      signature,
      legacyOffchainMessage(message),
    ),
  });

  const token = {
    caip19: `${CAIP2}/token:${mint}`,
    caip2: CAIP2,
    chain_slug: "solana",
    address: mint,
    standard: null,
    decimals: null,
    verified_at: VERIFIED_AT,
  };
  const authority = {
    type: "mint_authority",
    address: wallet,
    account_type: "regular" as const,
    signers: [],
    threshold: null,
    observed_at: VERIFIED_AT,
  };
  const verification = (
    scheme: KyiSignatureScheme,
    message: string,
    signature: string,
  ): KyiVerification => ({
    verified_at: VERIFIED_AT,
    expires_at: null,
    wallet_address: wallet,
    authority,
    method: {
      type: "signature",
      scheme,
      message,
      signature,
      signer_public_key: null,
    },
  });
  const lookup: KyiTokenLookup = {
    kyi_verified: true,
    data: [
      {
        kyi_verified: true,
        token,
        asset: {
          slug: null,
          name: "Example Token",
          symbol: "EXMPL",
          description: null,
          website_url: null,
          whitepaper_url: null,
        },
        issuer: null,
        authorities: [authority],
        verifications: [
          verification("solana_raw", walletMessage, rawSignature),
          verification("solana_offchain_v0", cliMessage, offchainSignature),
        ],
      },
    ],
  };
  const proof = async (
    scheme: KyiSignatureScheme,
    message: string,
    signature: string,
  ) =>
    (
      await checkKyiVerification(
        verification(scheme, message, signature),
        token,
      )
    ).proof;

  return {
    wallet,
    mint,
    messages: { wallet: walletMessage, cli: cliMessage },
    legacyOffchainHeader: getBase16Decoder().decode(offchainBytes.slice(0, 20)),
    signatures: { raw: rawSignature, offchain: offchainSignature },
    independent: {
      rawSignature: await formats(rawSignature, walletMessage),
      offchainSignature: await formats(offchainSignature, cliMessage),
    },
    sdk: {
      report: await verifyKyiResponse(lookup),
      rawSignatureRecordedAsOffchain: await proof(
        "solana_offchain_v0",
        walletMessage,
        rawSignature,
      ),
      offchainSignatureRecordedAsRaw: await proof(
        "solana_raw",
        cliMessage,
        offchainSignature,
      ),
      offchainSignatureWithoutScheme: await proof(
        null,
        cliMessage,
        offchainSignature,
      ),
    },
  };
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
  console.log(JSON.stringify(await run(), null, 2));
}

Its test checks that this output stays the same:

Output
json
{
  "wallet": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
  "mint": "9hSR6S7WPtxmTojgo6GG3k4yDPecgJY292j7xrsUGWBu",
  "messages": {
    "wallet": "Please, sign this message to verify your Bluprynt KYI credentials\n\nPurpose: Verify wallet ownership\nIssued to: example\nAddress: AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9::1790000000000::example-nonce",
    "cli": "Bluprynt wallet verification || Issued to: example || Address: AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9::1790000000000::example-nonce"
  },
  "legacyOffchainHeader": "ff736f6c616e61206f6666636861696e00008900",
  "signatures": {
    "raw": "3J7c4noSLt9FQuFz4cARW2fWYvJKmxd91xjgM3rfN6F8SHZ28CMWumDqzYuHGvByf4UJpgHixakxrsY6pQL3pmn8",
    "offchain": "4DGEkBZHtRdLRU7qU7ZA5qK6pSKAJgSqELbyEYjL3hm7S5VtsutXHoeSysWmKArkGZd3QwPK7TWtTD1GohdXQoCY"
  },
  "independent": {
    "rawSignature": {
      "overRawMessage": true,
      "overOffchainMessage": false
    },
    "offchainSignature": {
      "overRawMessage": false,
      "overOffchainMessage": true
    }
  },
  "sdk": {
    "report": {
      "claimed": true,
      "evidence": "confirmed",
      "verified": true,
      "matchesClaim": true,
      "tokens": [
        {
          "caip19": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:9hSR6S7WPtxmTojgo6GG3k4yDPecgJY292j7xrsUGWBu",
          "claimed": true,
          "evidence": "confirmed",
          "verified": true,
          "matchesClaim": true,
          "checks": [
            {
              "kind": "verification",
              "subject": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
              "method": "signature:solana_raw",
              "proof": {
                "status": "valid",
                "steps": [
                  {
                    "check": "wallet is the authority or one of its signers",
                    "via": "offline",
                    "ok": true,
                    "expected": [
                      "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                    ],
                    "actual": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                  },
                  {
                    "check": "solana_raw signature verifies for wallet",
                    "via": "offline",
                    "ok": true
                  },
                  {
                    "check": "verification's authority is listed for the token",
                    "via": "offline",
                    "ok": true,
                    "expected": [
                      {
                        "type": "mint_authority",
                        "address": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                      }
                    ],
                    "actual": {
                      "type": "mint_authority",
                      "address": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                    }
                  }
                ]
              },
              "at": {}
            },
            {
              "kind": "verification",
              "subject": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
              "method": "signature:solana_offchain_v0",
              "proof": {
                "status": "valid",
                "steps": [
                  {
                    "check": "wallet is the authority or one of its signers",
                    "via": "offline",
                    "ok": true,
                    "expected": [
                      "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                    ],
                    "actual": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                  },
                  {
                    "check": "solana_offchain_v0 signature verifies for wallet",
                    "via": "offline",
                    "ok": true
                  },
                  {
                    "check": "verification's authority is listed for the token",
                    "via": "offline",
                    "ok": true,
                    "expected": [
                      {
                        "type": "mint_authority",
                        "address": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                      }
                    ],
                    "actual": {
                      "type": "mint_authority",
                      "address": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                    }
                  }
                ]
              },
              "at": {}
            },
            {
              "kind": "authority",
              "subject": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
              "method": "mint_authority",
              "at": {}
            }
          ],
          "at": {}
        }
      ],
      "at": {}
    },
    "rawSignatureRecordedAsOffchain": {
      "status": "invalid",
      "reason": "solana_offchain_v0 signature verifies for wallet failed",
      "steps": [
        {
          "check": "wallet is the authority or one of its signers",
          "via": "offline",
          "ok": true,
          "expected": [
            "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
          ],
          "actual": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
        },
        {
          "check": "solana_offchain_v0 signature verifies for wallet",
          "via": "offline",
          "ok": false
        }
      ]
    },
    "offchainSignatureRecordedAsRaw": {
      "status": "invalid",
      "reason": "solana_raw signature verifies for wallet failed",
      "steps": [
        {
          "check": "wallet is the authority or one of its signers",
          "via": "offline",
          "ok": true,
          "expected": [
            "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
          ],
          "actual": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
        },
        {
          "check": "solana_raw signature verifies for wallet",
          "via": "offline",
          "ok": false
        }
      ]
    },
    "offchainSignatureWithoutScheme": {
      "status": "valid",
      "steps": [
        {
          "check": "wallet is the authority or one of its signers",
          "via": "offline",
          "ok": true,
          "expected": [
            "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
          ],
          "actual": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
        },
        {
          "check": "signature verifies for wallet (scheme not recorded, matched solana_offchain_v0)",
          "via": "offline",
          "ok": true
        }
      ]
    }
  }
}

What it shows:

  • messages: the wallet challenge has newlines; the CLI challenge is one line of printable ASCII.
  • legacyOffchainHeader is the 20-byte header of the CLI challenge in hex: the signing domain, version 00, format 00, and the length.
  • independent: for each message, its signature verifies only over the bytes it was made from, raw or with the header.
  • sdk.report: both verifications are valid when recorded with their real scheme.
  • sdk.rawSignatureRecordedAsOffchain and sdk.offchainSignatureRecordedAsRaw: swapping the scheme makes the proof invalid.
  • sdk.offchainSignatureWithoutScheme: with no scheme, the SDK finds the format that matches and names it.

Kit and the SDK agree: each signature verifies only in the format it was made in, and a proof recorded with the other format is invalid.

Source: examples/chains/src/solana.ts · output: examples/chains/output/solana.json

With the Solana CLI (not run by our tests) ​

You can sign and verify the same way with the Solana CLI. See Off-chain message signing with the Solana CLI for the exact options of your CLI version. message.txt holds the one-line CLI challenge, with no trailing newline.

sh
solana sign-offchain-message --keypair wallet.json "$(cat message.txt)"
solana verify-offchain-signature --signer <WALLET_ADDRESS> "$(cat message.txt)" <SIGNATURE>

At a moment: synthetic data (recorded RPC responses) ​

This script builds a lookup with two made-up tokens. In both, the test wallet is the listed mint_authority, and it signed Bluprynt's challenge raw. It asks for at: ["verified", "now"]. In the recorded chain, the first mint is a Token-2022 mint whose mint authority is still the wallet. The second is an SPL Token mint whose mint authority was handed to another key. The RPC responses are recorded in src/fixtures/solana-now.json, so the example runs offline. pnpm -C examples/chains fixtures regenerates them.

ts
import { fileURLToPath } from "node:url";
import { verifyKyiResponse } from "@bluprynt/sdk-chain-solana";
import {
  type Address,
  createKeyPairFromPrivateKeyBytes,
  getAddressFromPublicKey,
  getBase58Decoder,
  getUtf8Encoder,
  signBytes,
} from "@solana/kit";
import fixture from "./fixtures/solana-now.json" with { type: "json" };
import { withRecordedFetch } from "./replay.ts";

type KyiTokenLookup = Parameters<typeof verifyKyiResponse>[0];
type KyiTokenLookupItem = KyiTokenLookup["data"][number];

// Public test keys: anyone can rebuild them from these bytes, and the mint accounts recorded in fixtures/solana-now.json are synthetic.
const WALLET_SECRET = new Uint8Array(32).fill(1);
const KEPT_MINT_SECRET = new Uint8Array(32).fill(2);
const HANDED_OVER_MINT_SECRET = new Uint8Array(32).fill(3);

const CAIP2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
const RPC_URL = "https://solana.rpc.example.invalid";
const VERIFIED_AT = "2026-10-01T12:00:00.000Z";

const addressOf = async (secret: Uint8Array) =>
  getAddressFromPublicKey(
    (await createKeyPairFromPrivateKeyBytes(secret)).publicKey,
  );

export async function run() {
  const keys = await createKeyPairFromPrivateKeyBytes(WALLET_SECRET);
  const wallet = await getAddressFromPublicKey(keys.publicKey);
  const keptMint = await addressOf(KEPT_MINT_SECRET);
  const handedOverMint = await addressOf(HANDED_OVER_MINT_SECRET);

  const message = [
    "Please, sign this message to verify your Bluprynt KYI credentials",
    "",
    "Purpose: Verify wallet ownership",
    "Issued to: example",
    `Address: ${wallet}::1790856000000::example-nonce`,
  ].join("\n");
  const signature = getBase58Decoder().decode(
    await signBytes(keys.privateKey, getUtf8Encoder().encode(message)),
  );

  const mintToken = (mint: Address, symbol: string): KyiTokenLookupItem => {
    const authority = {
      type: "mint_authority",
      address: wallet,
      account_type: "regular" as const,
      signers: [],
      threshold: null,
      observed_at: VERIFIED_AT,
    };
    return {
      kyi_verified: true,
      token: {
        caip19: `${CAIP2}/token:${mint}`,
        caip2: CAIP2,
        chain_slug: "solana",
        address: mint,
        standard: null,
        decimals: 6,
        verified_at: VERIFIED_AT,
      },
      asset: {
        slug: null,
        name: `Example ${symbol}`,
        symbol,
        description: null,
        website_url: null,
        whitepaper_url: null,
      },
      issuer: null,
      authorities: [authority],
      verifications: [
        {
          verified_at: VERIFIED_AT,
          expires_at: null,
          wallet_address: wallet,
          authority,
          method: {
            type: "signature",
            scheme: "solana_raw",
            message,
            signature,
            signer_public_key: null,
          },
        },
      ],
    };
  };
  const lookup: KyiTokenLookup = {
    kyi_verified: true,
    data: [mintToken(keptMint, "KEPT"), mintToken(handedOverMint, "MOVED")],
  };

  return {
    wallet,
    mints: { kept: keptMint, handedOver: handedOverMint },
    report: await withRecordedFetch(fixture.records, () =>
      verifyKyiResponse(lookup, {
        rpcUrls: { [CAIP2]: RPC_URL },
        at: ["verified", "now"],
      }),
    ),
  };
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
  console.log(JSON.stringify(await run(), null, 2));
}

Its test checks that this output stays the same:

Output
json
{
  "wallet": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
  "mints": {
    "kept": "9hSR6S7WPtxmTojgo6GG3k4yDPecgJY292j7xrsUGWBu",
    "handedOver": "GyGKxMyg1p9SsHfm15MkNUu1u9TN2JtTspcdmrtGUdse"
  },
  "report": {
    "claimed": true,
    "evidence": "confirmed",
    "verified": true,
    "matchesClaim": true,
    "tokens": [
      {
        "caip19": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:9hSR6S7WPtxmTojgo6GG3k4yDPecgJY292j7xrsUGWBu",
        "claimed": true,
        "evidence": "confirmed",
        "verified": true,
        "matchesClaim": true,
        "checks": [
          {
            "kind": "verification",
            "subject": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
            "method": "signature:solana_raw",
            "proof": {
              "status": "valid",
              "steps": [
                {
                  "check": "wallet is the authority or one of its signers",
                  "via": "offline",
                  "ok": true,
                  "expected": [
                    "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                  ],
                  "actual": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                },
                {
                  "check": "solana_raw signature verifies for wallet",
                  "via": "offline",
                  "ok": true
                },
                {
                  "check": "verification's authority is listed for the token",
                  "via": "offline",
                  "ok": true,
                  "expected": [
                    {
                      "type": "mint_authority",
                      "address": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                    }
                  ],
                  "actual": {
                    "type": "mint_authority",
                    "address": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                  }
                }
              ]
            },
            "at": {
              "verified": {
                "status": "unverifiable",
                "reason": "Solana RPC cannot read historical account state",
                "steps": []
              },
              "now": {
                "status": "valid",
                "steps": [
                  {
                    "check": "solana_raw signature verifies for wallet",
                    "via": "offline",
                    "ok": true
                  },
                  {
                    "check": "mint account exists",
                    "via": "rpc",
                    "ok": true
                  },
                  {
                    "check": "mint is owned by a token program",
                    "via": "rpc",
                    "ok": true,
                    "expected": [
                      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                      "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
                    ],
                    "actual": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
                  },
                  {
                    "check": "account is a token mint",
                    "via": "rpc",
                    "ok": true
                  },
                  {
                    "check": "mint authority",
                    "via": "rpc",
                    "ok": true,
                    "expected": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
                    "actual": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                  }
                ],
                "position": {
                  "slot": "412345678"
                }
              }
            }
          },
          {
            "kind": "authority",
            "subject": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
            "method": "mint_authority",
            "at": {
              "verified": {
                "status": "unverifiable",
                "reason": "Solana RPC cannot read historical account state",
                "steps": []
              },
              "now": {
                "status": "valid",
                "steps": [
                  {
                    "check": "mint account exists",
                    "via": "rpc",
                    "ok": true
                  },
                  {
                    "check": "mint is owned by a token program",
                    "via": "rpc",
                    "ok": true,
                    "expected": [
                      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                      "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
                    ],
                    "actual": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
                  },
                  {
                    "check": "account is a token mint",
                    "via": "rpc",
                    "ok": true
                  },
                  {
                    "check": "mint authority",
                    "via": "rpc",
                    "ok": true,
                    "expected": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
                    "actual": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                  }
                ],
                "position": {
                  "slot": "412345678"
                }
              }
            }
          }
        ],
        "at": {
          "verified": {
            "evidence": "inconclusive",
            "verified": false,
            "matchesClaim": null
          },
          "now": {
            "evidence": "confirmed",
            "verified": true,
            "matchesClaim": true
          }
        }
      },
      {
        "caip19": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:GyGKxMyg1p9SsHfm15MkNUu1u9TN2JtTspcdmrtGUdse",
        "claimed": true,
        "evidence": "confirmed",
        "verified": true,
        "matchesClaim": true,
        "checks": [
          {
            "kind": "verification",
            "subject": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
            "method": "signature:solana_raw",
            "proof": {
              "status": "valid",
              "steps": [
                {
                  "check": "wallet is the authority or one of its signers",
                  "via": "offline",
                  "ok": true,
                  "expected": [
                    "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                  ],
                  "actual": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                },
                {
                  "check": "solana_raw signature verifies for wallet",
                  "via": "offline",
                  "ok": true
                },
                {
                  "check": "verification's authority is listed for the token",
                  "via": "offline",
                  "ok": true,
                  "expected": [
                    {
                      "type": "mint_authority",
                      "address": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                    }
                  ],
                  "actual": {
                    "type": "mint_authority",
                    "address": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9"
                  }
                }
              ]
            },
            "at": {
              "verified": {
                "status": "unverifiable",
                "reason": "Solana RPC cannot read historical account state",
                "steps": []
              },
              "now": {
                "status": "invalid",
                "reason": "mint authority failed",
                "steps": [
                  {
                    "check": "solana_raw signature verifies for wallet",
                    "via": "offline",
                    "ok": true
                  },
                  {
                    "check": "mint account exists",
                    "via": "rpc",
                    "ok": true
                  },
                  {
                    "check": "mint is owned by a token program",
                    "via": "rpc",
                    "ok": true,
                    "expected": [
                      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                      "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
                    ],
                    "actual": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
                  },
                  {
                    "check": "account is a token mint",
                    "via": "rpc",
                    "ok": true
                  },
                  {
                    "check": "mint authority",
                    "via": "rpc",
                    "ok": false,
                    "expected": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
                    "actual": "EdmxWPmx2WH6WgFfTdu9xfkYf3k1g5wD1zccTVySEEh1"
                  }
                ],
                "position": {
                  "slot": "412345679"
                }
              }
            }
          },
          {
            "kind": "authority",
            "subject": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
            "method": "mint_authority",
            "at": {
              "verified": {
                "status": "unverifiable",
                "reason": "Solana RPC cannot read historical account state",
                "steps": []
              },
              "now": {
                "status": "invalid",
                "reason": "mint authority failed",
                "steps": [
                  {
                    "check": "mint account exists",
                    "via": "rpc",
                    "ok": true
                  },
                  {
                    "check": "mint is owned by a token program",
                    "via": "rpc",
                    "ok": true,
                    "expected": [
                      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                      "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
                    ],
                    "actual": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
                  },
                  {
                    "check": "account is a token mint",
                    "via": "rpc",
                    "ok": true
                  },
                  {
                    "check": "mint authority",
                    "via": "rpc",
                    "ok": false,
                    "expected": "AKnL4NNf3DGWZJS6cPknBuEGnVsV4A4m5tgebLHaRSZ9",
                    "actual": "EdmxWPmx2WH6WgFfTdu9xfkYf3k1g5wD1zccTVySEEh1"
                  }
                ],
                "position": {
                  "slot": "412345679"
                }
              }
            }
          }
        ],
        "at": {
          "verified": {
            "evidence": "inconclusive",
            "verified": false,
            "matchesClaim": null
          },
          "now": {
            "evidence": "contradicted",
            "verified": false,
            "matchesClaim": false
          }
        }
      }
    ],
    "at": {
      "verified": {
        "evidence": "inconclusive",
        "verified": false,
        "matchesClaim": null
      },
      "now": {
        "evidence": "confirmed",
        "verified": true,
        "matchesClaim": true
      }
    }
  }
}
  • at.verified: unverifiable with Solana RPC cannot read historical account state, for both tokens. So each token's at.verified.evidence is inconclusive.
  • at.now for the first token, read at slot 412345678: the mint authority is the wallet. valid, and the token's at.now.evidence is confirmed.
  • at.now for the second token, read at slot 412345679: the mint authority is another key, so the result is invalid with mint authority failed. The token's at.now.evidence is contradicted.

The response-level at.now.evidence is confirmed, because one token is confirmed. For a badge, read each token's own report. See Showing a badge.

Source: examples/chains/src/solana-now.ts · output: examples/chains/output/solana-now.json

Limits ​

These come back unverifiable on Solana:

  • Every moment except "now". The RPC can't read past account state. The SDK can't tell either way.
  • Transfers and contract signatures. The SDK doesn't re-check them on Solana.
  • admin_manual verifications. There is nothing to re-check.
  • Authority types other than mint_authority and freeze_authority. A multisig of another type is partly checked: see Solana checks.
  • Expired verifications, at moments at or after expires_at.