{"_id":"@0xtan0/chain-utils-erc721","_rev":"2-7b4c0c1318bb2d28e9b94d772bf5766c","name":"@0xtan0/chain-utils-erc721","dist-tags":{"latest":"0.1.1"},"versions":{"0.0.1":{"name":"@0xtan0/chain-utils-erc721","version":"0.0.1","author":{"name":"Wonderland"},"license":"MIT","_id":"@0xtan0/chain-utils-erc721@0.0.1","maintainers":[{"name":"0xtan0","email":"tano@wonderland.xyz"}],"dist":{"shasum":"55d347331175b28c7ef6743208b10fae631c0444","tarball":"https://registry.npmjs.org/@0xtan0/chain-utils-erc721/-/chain-utils-erc721-0.0.1.tgz","fileCount":110,"integrity":"sha512-AmbYARDjWyDRO5Lw4U5dmTgxFj8wnxAkX66EeGxmlh/lEMeBR7ACPqyxhT3zi1lKLXw0Sb7y4OOY2nsRDyAxyQ==","signatures":[{"sig":"MEQCIGzf1T9epy+PBGl8g5T5jIZl176208r4WMyoSfuFw4zEAiBZ6EeDqNBJGXlG20q/QofWtrHeH0jPJApS/+65/4T80Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":269891},"main":"./dist/src/index.js","type":"module","types":"./dist/src/index.d.ts","exports":{".":{"types":"./dist/src/index.d.ts","import":"./dist/src/index.js","default":"./dist/src/index.js"}},"gitHead":"bdb7dc8fb7be021b8f629b1247639bce0bc0ec56","private":false,"scripts":{"lint":"eslint \"{src,test}/**/*.{js,ts,json}\"","test":"vitest run --config vitest.config.ts --passWithNoTests","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist/","format":"prettier --check \"{src,test}/**/*.{js,ts,json}\"","lint:fix":"pnpm lint --fix","test:cov":"vitest run --config vitest.config.ts --coverage","format:fix":"prettier --write \"{src,test}/**/*.{js,ts,json}\"","check-types":"tsc --noEmit -p ./tsconfig.json"},"_npmUser":{"name":"0xtan0","email":"tano@wonderland.xyz"},"_npmVersion":"10.8.2","description":"Type-safe ERC-721 utilities for [viem](https://viem.sh/) for both developers and agents.","directories":{"src":"src"},"_nodeVersion":"20.18.1","dependencies":{"viem":"2.45.1","@0xtan0/chain-utils-core":"workspace:*"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/chain-utils-erc721_0.0.1_1771776126055_0.7780059428030093","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@0xtan0/chain-utils-erc721","version":"0.1.1","private":false,"description":"Type-safe ERC-721 utilities for [viem](https://viem.sh/) for both developers and agents.","repository":{"type":"git","url":"git+https://github.com/0xtan0/chain-utils.git","directory":"packages/erc721"},"license":"MIT","author":{"name":"Wonderland"},"type":"module","exports":{".":{"types":"./dist/src/index.d.ts","import":"./dist/src/index.js","default":"./dist/src/index.js"}},"main":"./dist/src/index.js","types":"./dist/src/index.d.ts","directories":{"src":"src"},"dependencies":{"viem":"2.45.1","@0xtan0/chain-utils-core":"0.1.1"},"scripts":{"build":"tsc -p tsconfig.build.json","check-types":"tsc --noEmit -p ./tsconfig.json","clean":"rm -rf dist/","format":"prettier --check \"{src,test}/**/*.{js,ts,json}\"","format:fix":"prettier --write \"{src,test}/**/*.{js,ts,json}\"","lint":"eslint \"{src,test}/**/*.{js,ts,json}\"","lint:fix":"pnpm lint --fix","test":"vitest run --config vitest.config.ts --passWithNoTests","test:cov":"vitest run --config vitest.config.ts --coverage"},"_id":"@0xtan0/chain-utils-erc721@0.1.1","bugs":{"url":"https://github.com/0xtan0/chain-utils/issues"},"homepage":"https://github.com/0xtan0/chain-utils#readme","_integrity":"sha512-E5ceJRz9HdJbmEfWKgJ7HvvbLN/VrVQId0tTP4lzrYedyamR7QIOH1uA5bRJFtXnU1CkpbNpG0iarHWF0ImlBg==","_resolved":"/tmp/68b4de1cdd642a923ef0b13b572f6853/0xtan0-chain-utils-erc721-0.1.1.tgz","_from":"file:0xtan0-chain-utils-erc721-0.1.1.tgz","_nodeVersion":"22.22.0","_npmVersion":"11.10.1","dist":{"integrity":"sha512-E5ceJRz9HdJbmEfWKgJ7HvvbLN/VrVQId0tTP4lzrYedyamR7QIOH1uA5bRJFtXnU1CkpbNpG0iarHWF0ImlBg==","shasum":"6bf97a6d3482bd6c8e06fedcf08c9fd820391cd1","tarball":"https://registry.npmjs.org/@0xtan0/chain-utils-erc721/-/chain-utils-erc721-0.1.1.tgz","fileCount":99,"unpackedSize":269404,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@0xtan0%2fchain-utils-erc721@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCkrh595pyaz/ZRYieMl3/KxkJi0s1Qt2aA6cVSKUkS0AIgBIfFE/yFweRrQMzJoPRKVGhgRgQZaEo3B7a2aGJWUmU="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:56b49513-8ad2-42e0-a537-d42044bd3df7"}},"maintainers":[{"name":"0xtan0","email":"tano@wonderland.xyz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/chain-utils-erc721_0.1.1_1771784860122_0.2806260126296636"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-22T16:02:05.954Z","modified":"2026-02-22T18:27:40.625Z","0.0.1":"2026-02-22T16:02:06.227Z","0.1.1":"2026-02-22T18:27:40.318Z"},"author":{"name":"Wonderland"},"license":"MIT","description":"Type-safe ERC-721 utilities for [viem](https://viem.sh/) for both developers and agents.","maintainers":[{"name":"0xtan0","email":"tano@wonderland.xyz"}],"readme":"# @0xtan0/chain-utils-erc721\n\nType-safe ERC-721 utilities for [viem](https://viem.sh/) for both developers and agents.\n\nThis package provides:\n\n-   read and write clients for single-chain ERC-721 interactions\n-   typed domain objects (collection metadata, owners, approvals, token URIs)\n-   batch reads with multicall-first behavior and per-item failure reporting\n-   transaction helpers (`prepare`, `sign`, `send`, `wait`) and one-shot write methods\n-   typed decoding for known ERC-721 custom errors\n\nBuilt on top of `@0xtan0/chain-utils-core`.\n\n## Install\n\n```bash\npnpm add @0xtan0/chain-utils-erc721 viem\n```\n\n## Highlights\n\n-   Typed read and write clients remove repetitive `readContract` and `writeContract` wiring.\n-   Batch reads are multicall-first by default (with safe fallback behavior), and keep per-item success/failure results.\n-   Collection-bound readers/writers eliminate repeated `collection` arguments when working on a single NFT contract.\n-   Standard transaction helpers (`prepare`, `sign`, `send`, `wait`) keep write flows consistent.\n-   Token ID and batch query shapes are checked by TypeScript.\n\n## TypeScript Safety\n\n```ts\nimport { createERC721Client } from \"@0xtan0/chain-utils-erc721\";\nimport { createPublicClient, http } from \"viem\";\nimport { mainnet } from \"viem/chains\";\n\nconst publicClient = createPublicClient({ chain: mainnet, transport: http() });\nconst reader = createERC721Client({ client: publicClient });\nconst collection = \"0x1234567890abcdef1234567890abcdef12345678\";\n\nawait reader.getOwnerOf(collection, 42n); // ok\n\n// @ts-expect-error tokenId must be bigint\nawait reader.getOwnerOf(collection, 42);\n\nawait reader.getOwners([{ collection, tokenId: 1n }]); // ok\n\n// @ts-expect-error batch tokenId must be bigint\nawait reader.getOwners([{ collection, tokenId: \"1\" }]);\n\nconst bound = reader.forCollection(collection);\nawait bound.getTokenURIs([1n, 2n, 3n]); // ok\n\n// @ts-expect-error token ID lists must be bigint[]\nawait bound.getTokenURIs([1, 2, 3]);\n```\n\n### Example\n\nManual batch ownership checks usually become repeated one-by-one calls:\n\n```ts\nconst tokenIds = [1n, 2n, 3n];\nconst owners = await Promise.all(\n    tokenIds.map((tokenId) =>\n        publicClient.readContract({\n            abi: erc721Abi,\n            address: collection,\n            functionName: \"ownerOf\",\n            args: [tokenId],\n        }),\n    ),\n);\n```\n\nWith `erc721`, the same query is a single typed batch call:\n\n```ts\nconst reader = createERC721Client({ client: publicClient });\nconst batch = await reader.getOwners(tokenIds.map((tokenId) => ({ collection, tokenId })));\n\nfor (const result of batch.results) {\n    if (result.status === \"success\") {\n        console.log(result.result);\n    }\n}\n```\n\n## Scope and Behavior\n\nThis package is focused on contract interaction primitives, not indexing or marketplace logic.\nIt is intended for service/backend flows that need deterministic, typed RPC behavior.\n\n-   Multicall is used when available on the chain; otherwise reads fall back to sequential calls.\n-   Batch methods preserve order and return partial failures in typed result structures.\n-   Enumerable reads are gated by ERC165 support checks.\n-   Write helpers enforce wallet-based signing flow through `WalletClient`.\n\n## Example: Ownership + Metadata + Transfer\n\nThis is a common backend flow for marketplaces, custodial dashboards, and ops tooling.\n\n```ts\nimport {\n    createERC721Client,\n    createERC721WriteClient,\n    NonexistentToken,\n} from \"@0xtan0/chain-utils-erc721\";\nimport { createPublicClient, createWalletClient, http } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { mainnet } from \"viem/chains\";\n\nconst chain = mainnet;\nconst collection = \"0x1234567890abcdef1234567890abcdef12345678\" as const;\nconst tokenId = 42n;\nconst from = \"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" as const;\nconst to = \"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\" as const;\n\nconst publicClient = createPublicClient({ chain, transport: http(process.env.RPC_URL) });\n\nconst account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);\n\nconst walletClient = createWalletClient({\n    chain,\n    transport: http(process.env.RPC_URL),\n    account,\n});\n\nconst reader = createERC721Client({ client: publicClient });\nconst writer = createERC721WriteClient({ client: publicClient, walletClient });\n\n// 1) Read collection and token state\nconst [metadata, owner, tokenURI] = await Promise.all([\n    reader.getCollectionMetadata(collection),\n    reader.getOwnerOf(collection, tokenId),\n    reader.getTokenURI(collection, tokenId),\n]);\n\nconsole.log(`${metadata.name} (${metadata.symbol})`);\nconsole.log(`Owner: ${owner.owner}`);\nconsole.log(`Token URI: ${tokenURI.tokenURI}`);\n\n// 2) Fast batch check for many token owners in one RPC round\nconst ownerBatch = await reader.getOwners([\n    { collection, tokenId: 1n },\n    { collection, tokenId: 2n },\n    { collection, tokenId: 3n },\n]);\n\nfor (const [i, result] of ownerBatch.results.entries()) {\n    if (result.status === \"success\") {\n        console.log(`Token ${ownerBatch.queries[i]!.tokenId} owner: ${result.result}`);\n    } else {\n        console.warn(`Query failed:`, ownerBatch.failures);\n    }\n}\n\n// 3) Execute transfer with receipt waiting (simulate + sign + send + wait)\ntry {\n    const receipt = await writer.transferFrom(collection, from, to, tokenId, {\n        waitForReceipt: true,\n    });\n\n    console.log(\"Transfer mined:\", receipt.transactionHash);\n} catch (error) {\n    if (error instanceof NonexistentToken) {\n        console.error(\"Cannot transfer: token does not exist\", error.tokenId);\n    }\n    throw error;\n}\n```\n\n## Collection-Bound Reader/Writer\n\nIf you work with one collection repeatedly, bind it once and remove the `collection` arg from every call.\n\n```ts\nimport {\n    createERC721Client,\n    createERC721CollectionReader,\n    createERC721CollectionWriter,\n    createERC721WriteClient,\n} from \"@0xtan0/chain-utils-erc721\";\n\nconst reader = createERC721Client({ client: publicClient });\nconst writer = createERC721WriteClient({ client: publicClient, walletClient });\n\n// Bind through existing clients\nconst coolCatsRead = reader.forCollection(collection);\nconst coolCatsWrite = writer.forCollection(collection);\n\nawait coolCatsRead.getOwnerOf(42n);\nawait coolCatsRead.getTokenURIs([1n, 2n, 3n]);\nawait coolCatsWrite.transferFrom(from, to, 42n, { waitForReceipt: true });\n\n// Or construct bound objects directly\nconst directRead = createERC721CollectionReader({ collection, client: publicClient });\nconst directWrite = createERC721CollectionWriter({\n    collection,\n    client: publicClient,\n    walletClient,\n});\n```\n\n## API Summary\n\n| Export                         | Description                                                       |\n| ------------------------------ | ----------------------------------------------------------------- |\n| `createERC721Client`           | Single-chain read client (owner, balances, approvals, metadata)   |\n| `createERC721WriteClient`      | Single-chain write client (approve, setApprovalForAll, transfers) |\n| `ERC721CollectionReader`       | Bound single-chain collection reader                              |\n| `createERC721CollectionReader` | Factory for bound collection reader                               |\n| `ERC721CollectionWriter`       | Bound single-chain collection writer (extends reader)             |\n| `createERC721CollectionWriter` | Factory for bound collection writer                               |\n| `ERC721ReadClient`             | Class implementation behind the read factory                      |\n| `ERC721WriteClient`            | Class implementation behind the write factory                     |\n| `ERC721ErrorDecoder`           | Decodes ERC-721 custom errors and legacy string reverts           |\n\n## Typed Errors\n\nCommon errors are exposed as classes you can catch directly:\n\n-   `InvalidAddress`\n-   `NotERC721Contract`\n-   `NotERC721Enumerable`\n-   `NonexistentToken`\n-   `IncorrectOwner`\n-   `InsufficientApproval`\n-   `InvalidSender` / `InvalidReceiver`\n-   `InvalidApprover` / `InvalidOperator`\n\n## License\n\nMIT\n","readmeFilename":"README.md","homepage":"https://github.com/0xtan0/chain-utils#readme","repository":{"type":"git","url":"git+https://github.com/0xtan0/chain-utils.git","directory":"packages/erc721"},"bugs":{"url":"https://github.com/0xtan0/chain-utils/issues"}}