ARC-56 program hash + ABI signature registry
Look up a deployed Algorand app's compiled TEAL by SHA-256 hash and get back a real ARC-56 spec you can use to decode its calls - or look up a raw 4-byte ARC-4 method selector and get back the human-readable method signature it came from.
Why this exists
A wallet about to send an application call usually only has the app ID. It can fetch the app's compiled approval and clear programs from the network, but without a spec it has no idea what methods exist, what arguments they take, or how to render the call to a user instead of a wall of opaque bytes.
This registry closes that gap: every ARC-56 spec indexed by scholtz/ARC56Registry has its compiled approval and clear programs hashed with SHA-256, and each hash maps to a durable URL for a matching spec.
Folder layout
| Registry | Path |
|---|---|
| Approval program hash - spec URL | approval-programs/<hash[:3]>/<hash>.txt |
| Approval program hash - full spec | approval-programs/<hash[:3]>/<hash>.arc56.json |
| Clear-state program hash - spec URL | clear-programs/<hash[:3]>/<hash>.txt |
| Clear-state program hash - full spec | clear-programs/<hash[:3]>/<hash>.arc56.json |
| ABI method selector - signature | abi-signatures/<selector[:2]>/<selector>.txt |
| ABI method selector - signature + apps | abi-signatures/<selector[:2]>/<selector>.json |
Splitting on the hash's first few hex characters keeps any one folder from holding
thousands of files. Each program-hash .txt file contains exactly one
line: a raw.githubusercontent.com URL pinned to the commit that last
touched the matching ARC-56 spec, so it keeps resolving to the exact spec that
produced this hash even after the source file is edited or replaced later. The
.arc56.json file right next to it is a byte-for-byte copy of that same
spec, so you can skip resolving the URL entirely and read the full spec straight
from this registry. Each abi-signatures/ .txt file
contains the plain-text ABI method signature itself, e.g.
add(uint64,uint64)uint128, and its .json file holds that
signature plus the sorted list of approval-program hashes of every indexed app
known to expose it.
How to look up a hash
-
Fetch the app's compiled programs, e.g. via algod's
GET /v2/applications/{app-id}, and base64-decodeparams.approval-programand/orparams.clear-state-program. - Compute
sha256(program_bytes), hex-encoded, lowercase. -
Fetch this hash's spec from this site -
/approval-programs/<hash[:3]>/<hash>.arc56.jsonor/clear-programs/<hash[:3]>/<hash>.arc56.json- using a URL relative to wherever this page is hosted. A 404 just means this registry hasn't indexed a spec producing that hash yet, not an error. -
If found, it's the full ARC-56 JSON spec - use it directly to decode the app's
methods, argument types, and state layout. (The
.txtfile at the same path instead holds a durable, commit-pinned URL to the spec's source location, if you want that instead of the copy.)
Example: program hash to spec
Python
import base64, hashlib, urllib.request, json
app_id = 123456789
info = json.load(urllib.request.urlopen(f"https://mainnet-api.4160.nodely.dev/v2/applications/{app_id}"))
approval = base64.b64decode(info["params"]["approval-program"])
digest = hashlib.sha256(approval).hexdigest()
lookup_url = f"https://scholtz.github.io/ARC56Registry/approval-programs/{digest[:3]}/{digest}.arc56.json"
try:
spec = json.load(urllib.request.urlopen(lookup_url))
print("Found spec for", spec["name"])
except urllib.error.HTTPError:
print("No ARC-56 spec indexed for this program yet")
TypeScript (Node.js 18+, built-in fetch)
import { createHash } from "node:crypto";
const appId = 123456789;
const info = await (await fetch(`https://mainnet-api.4160.nodely.dev/v2/applications/${appId}`)).json();
const approval = Buffer.from(info.params["approval-program"], "base64");
const digest = createHash("sha256").update(approval).digest("hex");
const lookupUrl = `https://scholtz.github.io/ARC56Registry/approval-programs/${digest.slice(0, 3)}/${digest}.arc56.json`;
const res = await fetch(lookupUrl);
if (res.ok) {
const spec = await res.json();
console.log("Found spec for", spec.name);
} else {
console.log("No ARC-56 spec indexed for this program yet");
}
C# (.NET 8+)
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text.Json;
using var http = new HttpClient();
var appId = 123456789;
var info = await http.GetFromJsonAsync<JsonElement>($"https://mainnet-api.4160.nodely.dev/v2/applications/{appId}");
var approval = Convert.FromBase64String(info.GetProperty("params").GetProperty("approval-program").GetString()!);
var digest = Convert.ToHexStringLower(SHA256.HashData(approval));
var lookupUrl = $"https://scholtz.github.io/ARC56Registry/approval-programs/{digest[..3]}/{digest}.arc56.json";
var response = await http.GetAsync(lookupUrl);
if (response.IsSuccessStatusCode)
{
var spec = await response.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine($"Found spec for {spec.GetProperty("name").GetString()}");
}
else
{
Console.WriteLine("No ARC-56 spec indexed for this program yet");
}
How to look up a method selector
Every ARC-4/ARC-56 application call's first argument is a 4-byte method
selector: the first 4 bytes of SHA-512/256 (not SHA-256) over
the method's ABI signature string, name(argtype,argtype,...)returntype.
If you've extracted that selector from a transaction but don't have the app's ARC-56
spec, this registry can still resolve it to a human-readable signature:
- Hex-encode the 4 selector bytes, lowercase, e.g.
8aa3b61f. -
Fetch
/abi-signatures/<selector[:2]>/<selector>.json- relative to wherever this page is hosted. A 404 just means this registry hasn't indexed a method with that selector yet, not an error. -
If found, it's a JSON object
{"abi": "<signature>", "apps": [...]}-abiis the plain-text ABI signature, e.g.add(uint64,uint64)uint128, andappsis the sorted list of approval-program hashes of every indexed app known to expose it.
Example: method selector to signature
Python
import urllib.request, json
selector = "8aa3b61f" # first 4 bytes of an app call's method-call arg, hex-encoded
lookup_url = f"https://scholtz.github.io/ARC56Registry/abi-signatures/{selector[:2]}/{selector}.json"
try:
entry = json.load(urllib.request.urlopen(lookup_url))
print("Selector resolves to", entry["abi"]) # add(uint64,uint64)uint128
print("Known apps:", entry["apps"])
except urllib.error.HTTPError:
print("No ABI method indexed for this selector yet")
TypeScript (Node.js 18+, built-in fetch)
const selector = "8aa3b61f"; // first 4 bytes of an app call's method-call arg, hex-encoded
const lookupUrl = `https://scholtz.github.io/ARC56Registry/abi-signatures/${selector.slice(0, 2)}/${selector}.json`;
const res = await fetch(lookupUrl);
if (res.ok) {
const entry = await res.json();
console.log("Selector resolves to", entry.abi); // add(uint64,uint64)uint128
console.log("Known apps:", entry.apps);
} else {
console.log("No ABI method indexed for this selector yet");
}
C# (.NET 8+)
using System.Linq;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient();
var selector = "8aa3b61f"; // first 4 bytes of an app call's method-call arg, hex-encoded
var lookupUrl = $"https://scholtz.github.io/ARC56Registry/abi-signatures/{selector[..2]}/{selector}.json";
var response = await http.GetAsync(lookupUrl);
if (response.IsSuccessStatusCode)
{
var entry = await response.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine($"Selector resolves to {entry.GetProperty("abi").GetString()}"); // add(uint64,uint64)uint128
var apps = entry.GetProperty("apps").EnumerateArray().Select(a => a.GetString());
Console.WriteLine($"Known apps: {string.Join(", ", apps)}");
}
else
{
Console.WriteLine("No ABI method indexed for this selector yet");
}
Self-hosted use: Docker image
Everything on this page is also published as a self-hosted webserver image,
scholtz2/arc56-registry
(an unprivileged, non-root nginx container listening on port 8080), tagged with
today's UTC date and latest. Run it to get your own HTTP endpoint
serving these tables, so lookups don't depend on this site being reachable at call
time:
docker run -d --name arc56-registry -p 8080:8080 scholtz2/arc56-registry:latest
curl http://localhost:8080/approval-programs/<hash[:3]>/<hash>.arc56.json
Prefer plain files instead of an HTTP endpoint? docker cp still works,
since it's a normal filesystem, not a data-only image - see the image's own
README
(also served at /README.md inside the running container) for that and
full usage details, and
docs/docker-hash-registry.md
for how it's built and published.
Freshness
All three tables are regenerated daily by generate-hash-registry.yml and this site is redeployed right after by deploy-hash-registry-pages.yml. If two indexed specs compile to the same program bytes, the hash points at whichever spec file is larger (generally the more complete one) - see docs/hash-registry.md for the exact tie-breaking rule and the ABI signature registry's own rules.