Skip to content

LootSkins (ERC-721)

The token contract. Each tokenId is a redeemable claim on one specific CS2 item held in custody. Address and explorer link: Deployments.

  • Standards: ERC-721, ERC-2981 (royalty), ERC-165
  • name() LootFi Skins · symbol() LOOT
  • Upgradeable: UUPS proxy, owner-authorized

Token IDs

tokenId = (uint256(category) << 248) | sequence

The top byte is the item category; the remaining 248 bits are a per-category sequence. Full layout, category ordinals and decode snippets: Token IDs.

solidity
function categoryOf(uint256 tokenId) external pure returns (uint8);

A pure bit-shift — no storage read, no revert for a nonexistent token. Minting an id whose category byte is ≥ 8 reverts with InvalidCategory.

Functions anyone can call

deTokenize(uint256[] ids) payable

Burns your tokens and starts the return of the underlying skins.

solidity
function deTokenize(uint256[] calldata ids) external payable;
  • Direct owner only. An approved operator cannot burn on your behalf, by design — it guarantees the Detokenized event's owner is the true owner, which is who the physical item is returned to.
  • Maximum 50 ids per call (MAX_BATCH). More reverts with BatchTooLarge. An empty array reverts with EmptyBatch.
  • Payment must be exact. The call requires withdrawFee() * ids.length.

Excess payment is not refunded

deTokenize keeps whatever you send above the required fee. Always read withdrawFee() immediately before calling and send exactly fee × count. The fee is currently 0, so the correct value to send today is 0.

Burning the token is only half of a withdrawal — the physical return is a Steam trade offer you must accept. See Withdraw your skin and Withdrawal lifecycle.

Views

solidity
function withdrawFee() external view returns (uint256);   // wei, per token
function baseURI()    external view returns (string);      // always ends in "/"
function isMinter(address account) external view returns (bool);
function tokenURI(uint256 tokenId) external view returns (string);
function royaltyInfo(uint256 tokenId, uint256 salePrice)
    external view returns (address receiver, uint256 amount);

tokenURI is baseURI concatenated with the decimal tokenId, with no separator — which is why baseURI always ends in /. Read it on-chain rather than hardcoding a host; see Metadata format.

tokenURI reverts for a token that does not exist. That is also how you detect a burned token: ownerOf and tokenURI both revert, and the Detokenized event is the authoritative burn record.

Plus the full standard ERC-721 surface: ownerOf, balanceOf, approve, getApproved, setApprovalForAll, isApprovedForAll, transferFrom, safeTransferFrom, supportsInterface.

Not enumerable

LootSkins does not implement ERC-721Enumerable — there is no totalSupply() or tokenByIndex(). Enumerate the collection from Transfer logs starting at the deploy block instead. See Events & indexing.

Minter-gated functions

solidity
function mint(address to, uint256 tokenId) external;
function mintBatch(address to, uint256[] calldata tokenIds) external;

Callable only by an address for which isMinter is true. The minter role is separate from ownership and separately transferable, so mint authority can move to a different key or a multisig without moving contract ownership. mintBatch is capped at 50 ids.

Events

solidity
event Detokenized(address indexed owner, uint256[] tokenIds);
event MinterUpdated(address indexed minter, bool allowed);
event BaseURIUpdated(string baseURI);
event WithdrawFeeUpdated(uint256 fee);
event FeesWithdrawn(address indexed to, uint256 amount);

Plus standard Transfer, Approval and ApprovalForAll.

Detokenized.tokenIds is in the data field, not indexed

Indexing an array parameter stores only its hash, which would make the ids unrecoverable. They are deliberately non-indexed so any consumer can decode them. You cannot filter by a specific token id in that topic — decode the data.

Errors

ErrorWhen
ZeroAddress()An address argument was address(0)
NotMinter()Caller is not on the minter allow-list
InvalidCategory(uint256 category)tokenId's category byte is ≥ 8
BatchTooLarge(uint256 length)More than 50 ids in one call
EmptyBatch()Zero ids passed
NotOwner(uint256 tokenId)Caller is not the direct owner of a token being burned
InsufficientFee(uint256 required, uint256 provided)Sent less than withdrawFee × count
FeeTransferFailed()Fee sweep to the recipient failed

Decoding a raw revert selector: Errors.

Royalty

LootSkins implements ERC-2981. royaltyInfo currently reports 500 bps (5%).

ERC-2981 is advisory — the token contract does not enforce payment. LootFi's own marketplace reads it and pays the receiver on every fill; a third-party venue is free to ignore it. See Fees & royalties.

Owner functions

updateMinter, updateBaseURI, setWithdrawFee, setDefaultRoyalty, withdrawFees, and UUPS upgrade authorization. What each can and cannot do: Admin & upgrades.

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