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 contractconst 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 keyconst stringKey = Cl.stringAscii("config-key");const response1 = await fetch(url, {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify(cvToHex(stringKey)),});// Tuple keyconst 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 keyconst 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 entriesasync 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 balancesconst 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 typeinterface TokenBalance {balance: bigint;locked: bigint;}// Type-safe query functionasync 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;}}