Return an entry from a map

Query contract map data using the Stacks API map_entry endpoint


import { Cl, cvToHex } from "@stacks/transactions";
// Query a map entry from a contract
const contractAddress = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM";
const contractName = "my-contract";
const mapName = "user-balances";
// Create the map key (e.g., a principal)
const mapKey = Cl.standardPrincipal("ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5");
const response = await fetch(
`https://api.hiro.so/v2/map_entry/${contractAddress}/${contractName}/${mapName}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cvToHex(mapKey)),
}
);
const result = await response.json();
const data = result.data ? Cl.deserialize(result.data) : null;
console.log("Map value:", data);

Use cases

  • Reading user balances from token contracts
  • Checking NFT ownership records
  • Retrieving configuration values from contracts
  • Monitoring contract state without transactions

Key concepts

The map_entry API:

  • POST request: Send the serialized map key
  • Hex encoding: Keys must be hex-encoded Clarity values
  • Response format: Returns hex-encoded Clarity value or null

Query different map key types

// String key
const stringKey = Cl.stringAscii("config-key");
const response1 = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(cvToHex(stringKey)),
});
// Tuple key
const tupleKey = Cl.tuple({
user: Cl.standardPrincipal("ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"),
token: Cl.uint(1),
});
const response2 = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(cvToHex(tupleKey)),
});
// Uint key
const uintKey = Cl.uint(12345);
const response3 = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(cvToHex(uintKey)),
});

Batch map queries

// Query multiple map entries
async function batchQueryMap(
contractId: string,
mapName: string,
keys: any[]
) {
const [contractAddress, contractName] = contractId.split(".");
const promises = keys.map(async (key) => {
const response = await fetch(
`https://api.hiro.so/v2/map_entry/${contractAddress}/${contractName}/${mapName}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(cvToHex(key)),
}
);
const result = await response.json();
return {
key,
value: result.data ? Cl.deserialize(result.data) : null,
};
});
return Promise.all(promises);
}
// Example: Query multiple user balances
const users = [
"ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5",
"ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG",
"ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC",
];
const balances = await batchQueryMap(
"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-contract",
"balances",
users.map(u => Cl.standardPrincipal(u))
);

Type-safe map queries

import { cvToValue } from "@stacks/transactions";
// Define expected map value type
interface TokenBalance {
balance: bigint;
locked: bigint;
}
// Type-safe query function
async function getTokenBalance(
contractId: string,
user: string
): Promise<TokenBalance | null> {
const [address, name] = contractId.split(".");
const response = await fetch(
`https://api.hiro.so/v2/map_entry/${address}/${name}/balances`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(cvToHex(Cl.standardPrincipal(user))),
}
);
const result = await response.json();
if (!result.data) return null;
const value = cvToValue(Cl.deserialize(result.data));
return {
balance: BigInt(value.balance),
locked: BigInt(value.locked),
};
}
API tip

The map_entry endpoint is read-only and doesn't require authentication. For mainnet, use https://api.mainnet.hiro.so/v2/map_entry/...

Error handling

async function safeQueryMap(contractId: string, mapName: string, key: any) {
try {
const [address, name] = contractId.split(".");
const response = await fetch(
`https://api.hiro.so/v2/map_entry/${address}/${name}/${mapName}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(cvToHex(key)),
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
return result.data ? Cl.deserialize(result.data) : null;
} catch (error) {
console.error("Map query failed:", error);
return null;
}
}