Reference

Verify an inscription

Rebuild a token's media from chain and check its fingerprint.

Each token stores a Keccak-256 digest for every file. You can rebuild the file from the chain and check that digest yourself. You don't need the Scrible app.

What you need#

  • The token address.
  • The public RPC: https://rpc.mainnet.chain.robinhood.com
  • Foundry's cast, or Node with ethers v6.

How the check works#

  1. Read the media info

    Call mediaInfo(i) on the token. You get the MIME type, the digest, the size, and the chunk addresses.

  2. Read each chunk's code

    Each chunk is a contract. Its code is a 0x00 byte followed by the data.

  3. Strip and join

    Drop the first byte of each chunk. Join the chunks in order.

  4. Hash and compare

    Take the Keccak-256 of the result. It must equal the digest. The length must equal the size.

With cast#

RPC=https://rpc.mainnet.chain.robinhood.com
TOKEN=0x39Fc9c8D24e38586f325fB8cFf7F9F1ae4635e59

# 1. File count and info for file 0
cast call $TOKEN "mediaCount()(uint256)" --rpc-url $RPC
cast call $TOKEN "mediaInfo(uint256)(string,bytes32,uint256,address[])" 0 --rpc-url $RPC

# 2. Join the chunks (drop the leading 00 of each)
DATA=0x
for CHUNK in 0xCHUNK_1 0xCHUNK_2; do
  CODE=$(cast code $CHUNK --rpc-url $RPC)
  DATA="$DATA${CODE:4}"
done

# 3. Compare with the digest from step 1
cast keccak $DATA

Replace 0xCHUNK_1 0xCHUNK_2 with the addresses from step 1, in order. Run the script in bash. For FIST, the result is 0xb877f648beceb5a0a95187602b8fcbb2e9215a6269f71537db7beaf664a44538.

With ethers#

import { Contract, JsonRpcProvider, concat, dataSlice, keccak256, getBytes } from "ethers";
import { writeFileSync } from "node:fs";

const provider = new JsonRpcProvider("https://rpc.mainnet.chain.robinhood.com", 4663);
const token = new Contract("0x39Fc9c8D24e38586f325fB8cFf7F9F1ae4635e59", [
  "function mediaCount() view returns (uint256)",
  "function mediaInfo(uint256) view returns (string mime, bytes32 digest, uint256 size, address[] chunks)",
], provider);

const count = Number(await token.mediaCount());
for (let i = 0; i < count; i++) {
  const { mime, digest, size, chunks } = await token.mediaInfo(i);
  const parts = [];
  for (const chunk of chunks) parts.push(dataSlice(await provider.getCode(chunk), 1));
  const data = concat(parts);
  const ok = keccak256(data) === digest && getBytes(data).length === Number(size);
  console.log(i, mime, ok ? "verified" : "MISMATCH");
  writeFileSync(`media-${i}`, getBytes(data));
}

The script writes each file to disk. Open it with the matching file type.

The quick way#

readMedia(i) on the token returns the joined bytes in one call. It trusts the token's own logic, so use the chunk method above for an independent check.

Example#

FIST (0x39Fc…5e59) is the first Scrible inscription. It holds one JPEG of 45,325 bytes in 2 chunks.

esc