Decode Clarity print events
You don't decode them: every print is indexed already parsed from Clarity into JSON, with the raw hex kept beside it. One key, one call:
cURL TypeScript Copy
$ curl " $SECONDLAYER_API_URL /v1/index/events ?event_type=print&contract_id=SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry&order=desc&limit=1 " \
-H " Authorization: Bearer sk-sl_… "
import { Index } from " @secondlayer/sdk ";
const index = new Index ();
const page = await index . events . list ({
eventType : " print ",
contractId : " SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry ",
order : " desc ",
limit : 1 ,
});
200 response.json json Copy
{
" events ": [
{
" cursor ": " 9348740:2 ",
" block_height ": 9348740 ,
" block_time ": " 2026-10-28T17:42:12.000Z ",
" tx_id ": " 0xfff08aecc097d418f55b98b10abe2bc6b1785b89b0ed1b80506fc15a375a14f9 ",
" tx_index ": 0 ,
" event_index ": 2 ,
" event_type ": " print ",
" contract_id ": " SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry ",
" payload ": {
" topic ": " print ",
" value " : {
" amount ": " 40477 ",
" block-height ": " 973577 ",
" max-fee ": " 850 ",
" recipient ": { " hashbytes ": " 0x98cfcc0a22d16a9ce131dd774ee0245a3b10dcfb ", " version ": " 0x04 " },
" request-id ": " 4636 ",
" sender ": " SPEG24N6F52VPD8001H20TD317GHDD23GGVEZC56 ",
" topic ": " withdrawal-create "
},
" raw_value " : " 0x0c0000000706616d6f756e74… "
}
}
],
" next_cursor ": " desc:9348740:2 ",
" tip ": { " block_height ": 9348932 , " finalized_height ": 9348069 , " lag_seconds ": 18 },
" reorgs ": []
}
The decoded tuple is value: uints are decimal strings, buffers are hex, principals are strings. raw_value is the hex you started from, kept so you can check the decode.
A contract prints different shapes under different topics. The print-schema call lists a contract's topics and each field's Clarity and TypeScript type.
cURL TypeScript Copy
$ curl " $SECONDLAYER_API_URL /v1/index/contracts/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry/print-schema " \
-H " Authorization: Bearer sk-sl_… "
const schema = await index . printSchema (
" SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry ",
);
200 response.json json Copy
{
" contract_id ": " SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry ",
" topics ": [
▸ { "topic": "completed-deposit", "count": 1943, "fields": "…" }, … { "topic": "completed-deposit", "count": 1943, "fields": "…" }, 1 lines { " topic ": " completed-deposit ", " count ": 1943 , " fields ": " … " },
{
" topic ": " withdrawal-create ",
" count ": 29 ,
" fields ": [
{ " name ": " amount ", " camel_name ": " amount ", " clarity_type ": " uint ", " ts_type ": " bigint " },
▸ { "name": "block-height", "camel_name": "blockHeight", "clarity_type": "uint", "ts_type": "bigint" }, … { "name": "max-fee", "camel_name": "maxFee", "clarity_type": "uint", "ts_type": "bigint" }, 2 lines { " name ": " block-height ", " camel_name ": " blockHeight ", " clarity_type ": " uint ", " ts_type ": " bigint " },
{ " name ": " max-fee ", " camel_name ": " maxFee ", " clarity_type ": " uint ", " ts_type ": " bigint " },
{ " name ": " recipient " , " camel_name ": " recipient ", " clarity_type ": " (tuple (hashbytes (buff 32)) (version (buff 1))) ", " ts_type ": " { hashbytes: string; version: string } " },
▸ { "name": "request-id", "camel_name": "requestId", "clarity_type": "uint", "ts_type": "bigint" }, … { "name": "sender", "camel_name": "sender", "clarity_type": "principal", "ts_type": "string" } 2 lines { " name ": " request-id ", " camel_name ": " requestId ", " clarity_type ": " uint ", " ts_type ": " bigint " },
{ " name ": " sender ", " camel_name ": " sender ", " clarity_type ": " principal ", " ts_type ": " string " }
]
},
▸ { "topic": "withdrawal-accept", "count": 17, "fields": "…" }, … { "topic": "key-rotation", "count": 1, "fields": "…" } 3 lines { " topic ": " withdrawal-accept ", " count ": 17 , " fields ": " … " },
{ " topic ": " withdrawal-reject ", " count ": 10 , " fields ": " … " },
{ " topic ": " key-rotation ", " count ": 1 , " fields ": " … " }
],
" sampled ": true
}
Fields come back with their Clarity type, so you know recipient is a tuple before you parse it. The schema is sampled from indexed prints, so sampled: true means older topics may be missing.
A uint is a bigint in a subgraph handler (ts_type), but a decimal string in REST values.
The events feed filters by contract, not by topic. Keep the topic you want from the tuple's own topic key.
const withdrawals = page . events . filter (
( event ) => ( event . payload . value as { topic ?: string } | undefined )?. topic === " withdrawal-create ",
);
Copy
Prints that aren't tuples have no topic key; the schema lists them under *.
To be pushed one topic instead of reading it, a webhook trigger takes topic (Print fields ).
Page with next_cursor, as in any decoded feed .
Hosted reads are metered, with a free monthly allowance (What it costs ). Reading a contract's prints means reading every topic it emits, so a chatty contract costs more rows than the topic you keep.
Every filter and the response schema: Decoded events by event_type and Print payload schemas . The shapes by contract: Print schemas .
← Previous Calls to a contract function Next → NFT transfers for a collection Edit on GitHub