Skip to content

Read token state ​

Everything you can learn about a LootFi token without an API key. Addresses: Deployments.

Core reads ​

solidity
function ownerOf(uint256 tokenId) external view returns (address);
function balanceOf(address owner) external view returns (uint256);
function tokenURI(uint256 tokenId) external view returns (string);
function categoryOf(uint256 tokenId) external pure returns (uint8);
function royaltyInfo(uint256 tokenId, uint256 salePrice)
    external view returns (address receiver, uint256 amount);
function withdrawFee() external view returns (uint256);
function baseURI() external view returns (string);
function supportsInterface(bytes4 interfaceId) external view returns (bool);
ts
import { publicClient } from "./chain";

const [owner, uri, category] = await Promise.all([
  publicClient.readContract({ address: LOOT_SKINS, abi, functionName: "ownerOf",    args: [tokenId] }),
  publicClient.readContract({ address: LOOT_SKINS, abi, functionName: "tokenURI",   args: [tokenId] }),
  publicClient.readContract({ address: LOOT_SKINS, abi, functionName: "categoryOf", args: [tokenId] }),
]);

categoryOf is pure — a bit-shift with no storage access. It returns a value for any id, including one that was never minted, so it is not an existence check.

Decoding a token id ​

tokenId = (category << 248) | sequence
ts
export function decodeTokenId(tokenId: bigint) {
  return {
    category: Number(tokenId >> 248n),
    sequence: tokenId & ((1n << 248n) - 1n),
  };
}
solidity
uint8   category = uint8(tokenId >> 248);
uint256 sequence = tokenId & ((uint256(1) << 248) - 1);
python
category = token_id >> 248
sequence = token_id & ((1 << 248) - 1)
OrdinalCategory
0Gun
1Knife
2Glove
3Agent
4Sticker
5Graffiti
6Container
7Music Kit

Gun is ordinal 0

if (category) is false for every gun — the most common item type. Test category != null / !== undefined, never truthiness. The same trap applies to paintSeed: 0, which is a real and sometimes valuable pattern.

Full layout and rationale: Token IDs.

Detecting a burned token ​

A burned token has no owner:

ts
try {
  await publicClient.readContract({ ...skins, functionName: "ownerOf", args: [tokenId] });
  // exists
} catch {
  // burned, or never minted — these are indistinguishable by this call alone
}

tokenURI reverts the same way. To distinguish burned from never minted, look for the Detokenized event, which is the authoritative burn record — see Events & indexing.

Enumerating the collection ​

LootSkins is not ERC-721Enumerable

There is no totalSupply(), no tokenByIndex(), and no tokenOfOwnerByIndex(). Do not build against them.

Replay Transfer logs from the deploy block (19419826) and fold them into an ownership map. A mint is a Transfer from the zero address; a burn is a Transfer to it. That is the same approach LootFi uses internally, and it is exact rather than approximate.

Can this token be listed or sold? ​

fulfillOrder accepts either approval form, so check both:

ts
const [approvedAll, approvedOne] = await Promise.all([
  publicClient.readContract({ ...skins, functionName: "isApprovedForAll", args: [owner, LOOT_MARKET] }),
  publicClient.readContract({ ...skins, functionName: "getApproved",      args: [tokenId] }),
]);

const marketCanMove =
  approvedAll || approvedOne.toLowerCase() === LOOT_MARKET.toLowerCase();

Checking only isApprovedForAll will report perfectly fillable orders as stale.

Is a specific order still fillable? ​

Mirror the contract's own checks rather than guessing:

ts
const [owner, used, minNonce] = await Promise.all([
  publicClient.readContract({ ...skins,  functionName: "ownerOf",         args: [order.tokenId] }),
  publicClient.readContract({ ...market, functionName: "usedOrCancelled", args: [order.maker, order.nonce] }),
  publicClient.readContract({ ...market, functionName: "minimumNonce",    args: [order.maker] }),
]);

const fillable =
  BigInt(order.expiry) > BigInt(Math.floor(Date.now() / 1000)) &&
  owner.toLowerCase() === order.maker.toLowerCase() &&
  !used &&
  BigInt(order.nonce) >= minNonce &&
  marketCanMove;

Better still, simulate the actual call — see Fill an order.

Interface support ​

ts
await publicClient.readContract({
  ...skins, functionName: "supportsInterface", args: ["0x80ac58cd"],  // ERC-721
});
await publicClient.readContract({
  ...skins, functionName: "supportsInterface", args: ["0x2a55205a"],  // ERC-2981
});

LootFi is not affiliated with, endorsed by, or sponsored by Valve Corporation. Counter-Strike and Steam are trademarks of Valve Corporation.