cw721 is the NFT standard for CosmWasm chains — Terra2, Osmosis, Neutron, Injective and the rest. If you have worked with ERC-721 on Ethereum the concepts transfer directly, but the mechanics differ in ways that matter the moment you start building against them or debugging why a transfer failed.
What a cw721 contract actually is
A cw721 is a CosmWasm smart contract that maintains one central mapping: token ID to owner address. Everything else — metadata, approvals, collection info — hangs off that.
Its stored state, in practice:
tokens— token ID →{ owner, approvals[], token_uri, extension }contract_info— the collection'snameandsymbolnum_tokens— the count of minted tokensoperators— addresses granted blanket permission over an owner's entire holdingsminter— who is allowed to mint
You query it like any CosmWasm contract, with a JSON message. Owner of a token:
{"owner_of": {"token_id": "42"}}
Collection name and symbol:
{"contract_info": {}}
Everything a marketplace, an indexer or a wallet knows about a collection comes from queries like these against the contract. There is no privileged API — the chain is the source.
The four messages that matter
Execution messages are where the behaviour lives. Four cover almost everything:
transfer_nft — move a token to a new owner. Callable by the owner or an approved address.
send_nft — move a token to a contract along with a message payload the receiving contract executes. This is the CosmWasm pattern for "deposit this NFT into a protocol and do something with it" in one step, and it has no direct ERC-721 equivalent in everyday use.
approve — grant one address permission to transfer one specific token. This is what listing on a marketplace does. Optionally carries an expiry.
approve_all — grant one address permission to transfer every token you own in that collection, present and future. Powerful, convenient, and the single most over-granted permission in NFT trading.
The approval distinction is the practical security lesson. A per-token approval bounds your exposure to one token. An operator approval bounds it to everything you hold in that collection, until you revoke it. Prefer the narrow one whenever the interface lets you choose.
Where the metadata lives
A cw721 token carries a token_uri and/or an on-chain extension. This is where implementations diverge, and it is why one collection renders instantly in every wallet while another shows a broken image.
token_uri pointing at IPFS. The common pattern. The contract stores something like ipfs://Qm.../42.json, and a client fetches the JSON to get the name, image and traits. Content-addressed, so the data cannot silently change — but it only stays available while somebody pins it.
token_uri pointing at HTTP. Cheap and fast, and entirely dependent on somebody continuing to pay for a domain and a server. When the project stops, the art stops.
On-chain extension. Traits stored directly in contract state. Costs more to mint, survives as long as the chain does. The cw721-metadata-onchain variant formalises this.
The token is the ownership record either way. Metadata permanence is a separate engineering decision, and it is worth checking which one a collection made before assuming an NFT is durable.
cw721 versus ERC-721
| ERC-721 (Ethereum) | cw721 (CosmWasm) | |
|---|---|---|
| Language | Solidity | Rust, compiled to Wasm |
| Interaction | ABI-encoded calls | JSON messages |
| Deployment | One contract per collection | Code ID uploaded once, instantiated per collection |
| Send-with-payload | Rare in practice | send_nft is idiomatic |
| Royalty standard | EIP-2981 | cw2981, closely modelled on it |
| Typical gas | Significant | Fractions of a cent |
The code-ID model is the structural difference worth knowing. On CosmWasm you upload contract code once, receive a code ID, and then instantiate as many collections from it as you like. Ten collections sharing a code ID share audited, identical logic. On Ethereum, ten collections generally means ten separately deployed contracts, each of which could differ in ways you have to check individually.
That property is why a Cosmos marketplace can reason about a whole class of collections at once, and why "which code ID is this instantiated from" is a genuinely useful question to ask about an unfamiliar collection.
cw2981 and royalties
cw2981 extends cw721 with a royalty query. A marketplace asks the collection contract what royalty is owed on a given sale price:
{"extension": {"msg": {"royalty_info": {"token_id": "42", "sale_price": "100000000"}}}}
The contract answers with a receiver address and an amount, and the marketplace pays it as part of settlement. There is also a check_royalties query so a marketplace can tell whether a collection supports the extension at all before trying.
The crucial detail, which applies equally to EIP-2981 on Ethereum: this is a query, not an enforcement mechanism. The standard describes what should be paid. Whether it is paid depends on the marketplace choosing to honour it. A raw transfer_nft between two wallets pays no royalty because no marketplace is involved. The royalties article covers what follows from that.
Reading a collection yourself
You do not need a marketplace to inspect a cw721. Any public LCD endpoint will do — base64-encode the query and hit the smart-query route:
LCD=https://terra-rest.publicnode.com
CONTRACT=terra1...
Q=$(echo -n '{"contract_info":{}}' | base64)
curl -s "$LCD/cosmwasm/wasm/v1/contract/$CONTRACT/smart/$Q"
Swap the query for {"num_tokens":{}} to get supply, or {"owner_of":{"token_id":"1"}} to check an owner. This is the same path every marketplace and wallet uses. There is nothing behind a curtain.
FAQ
What is cw721?
cw721 is the non-fungible token standard for CosmWasm-based chains such as Terra2, Osmosis and Neutron. It defines the contract interface for ownership, transfers, approvals and metadata, filling the same role ERC-721 does on Ethereum.
What is the difference between cw721 and ERC-721?
They model the same thing with different mechanics. cw721 contracts are Rust compiled to Wasm and take JSON messages; ERC-721 is Solidity with ABI-encoded calls. CosmWasm also uploads code once as a code ID and instantiates many collections from it, and adds send_nft for transferring a token to a contract with an executable payload.
What does approve_all do?
It grants an address operator permission over every token you own in that collection, including ones you acquire later, until you revoke it. A plain approve grants permission over a single token only, which is the safer default for marketplace listings.
Where is cw721 metadata stored?
Either off-chain behind a token_uri — usually IPFS, sometimes plain HTTP — or on-chain in the token's extension field. On-chain storage costs more to mint but survives as long as the chain does; IPFS survives as long as somebody pins the content.
Is cw2981 royalty payment enforced by the chain?
No. cw2981 is a query interface that lets a contract state what royalty is owed. Paying it is the marketplace's choice at settlement. A direct wallet-to-wallet transfer_nft involves no marketplace and pays no royalty.