{"_id":"@d3or/slotseek","_rev":"7-43771857ad3b1c3e81e241859b911ce5","name":"@d3or/slotseek","dist-tags":{"latest":"1.3.1"},"versions":{"1.0.0":{"name":"@d3or/slotseek","version":"1.0.0","keywords":["ethereum","erc20"],"author":{"name":"d3or"},"license":"MIT","_id":"@d3or/slotseek@1.0.0","maintainers":[{"name":"d3or","email":"deorrunner@gmail.com"}],"homepage":"https://github.com/d3or/slotseek#readme","bugs":{"url":"https://github.com/d3or/slotseek/issues"},"dist":{"shasum":"fb967aea201739ef50ae26ef35c4e18f1088f718","tarball":"https://registry.npmjs.org/@d3or/slotseek/-/slotseek-1.0.0.tgz","fileCount":14,"integrity":"sha512-6L3A6OIIsYV2BJ7VIlY4RE8+nW8LxuZqp9I0j738GEMhLkiYDjoPX3qyPBba1WahdPwRW85BovBSVf4vAJtTxQ==","signatures":[{"sig":"MEUCICUhZLDckJI1Ionngdv+7Bo0/ZUTeh3hNurtVo2DSB9FAiEAnxbbR/cLZRzri6gn6f1O5KRx/iYua0Us3ctoyEh3EEc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":34683},"jest":{"preset":"ts-jest","testMatch":["<rootDir>/tests/**/*.test.ts"],"setupFiles":["<rootDir>/jest.setup.ts"],"testEnvironment":"node"},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"b42bfe009c2906dbb518bad47f771075737c72c2","scripts":{"test":"jest","build":"tsc","test:watch":"jest --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"d3or","email":"deorrunner@gmail.com"},"repository":{"url":"git+https://github.com/d3or/slotseek.git","type":"git"},"_npmVersion":"10.7.0","description":"A library for finding the storage slots on an ERC20 token for balances and approvals, which can be used to mock the balances and approvals of an address when estimating gas costs of transactions that would fail if the address did not have the required bal","directories":{},"_nodeVersion":"18.20.3","dependencies":{"ethers":"^5.7.2"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","dotenv":"^16.4.5","ts-jest":"^29.1.0","typescript":"^4.9.5","@types/jest":"^29.5.12"},"peerDependencies":{"ethers":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/slotseek_1.0.0_1720310087302_0.806765083428238","host":"s3://npm-registry-packages"}},"1.0.1":{"name":"@d3or/slotseek","version":"1.0.1","keywords":["ethereum","erc20"],"author":{"name":"d3or"},"license":"MIT","_id":"@d3or/slotseek@1.0.1","maintainers":[{"name":"d3or","email":"deorrunner@gmail.com"}],"homepage":"https://github.com/d3or/slotseek#readme","bugs":{"url":"https://github.com/d3or/slotseek/issues"},"dist":{"shasum":"50bc3f58ff353532e1807217498421305eeab2ef","tarball":"https://registry.npmjs.org/@d3or/slotseek/-/slotseek-1.0.1.tgz","fileCount":14,"integrity":"sha512-FNqVjcxMi9/XJn/q2KvINUFCZHgL4wdygJrWvGSH0iYNdJuG9hb9je50tWYOGAmfZfBDtbydrgxJPoTd7d3nkA==","signatures":[{"sig":"MEQCIAipI6oYTQ7Df8Oev6/Iu+owJCcsYAgQsyk/jY+u1Z73AiBIfOjabbtKUdUtVrZHkueGIbA4kOS35PWo6YJSO6VT2A==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":39193},"jest":{"preset":"ts-jest","testMatch":["<rootDir>/tests/**/*.test.ts"],"setupFiles":["<rootDir>/jest.setup.ts"],"testEnvironment":"node"},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"b93282deb3810d423ee3ee2c26cece70f07d26cb","scripts":{"test":"jest","build":"tsc","test:watch":"jest --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"d3or","email":"deorrunner@gmail.com"},"repository":{"url":"git+https://github.com/d3or/slotseek.git","type":"git"},"_npmVersion":"10.7.0","description":"A library for finding the storage slots on an ERC20 token for balances and approvals, which can be used to mock the balances and approvals of an address when estimating gas costs of transactions that would fail if the address did not have the required bal","directories":{},"_nodeVersion":"18.20.3","dependencies":{"ethers":"^5.7.2"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","dotenv":"^16.4.5","ts-jest":"^29.1.0","typescript":"^4.9.5","@types/jest":"^29.5.12"},"peerDependencies":{"ethers":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/slotseek_1.0.1_1720716811304_0.003117555935351568","host":"s3://npm-registry-packages"}},"1.0.2":{"name":"@d3or/slotseek","version":"1.0.2","keywords":["ethereum","erc20"],"author":{"name":"d3or"},"license":"MIT","_id":"@d3or/slotseek@1.0.2","maintainers":[{"name":"d3or","email":"deorrunner@gmail.com"}],"homepage":"https://github.com/d3or/slotseek#readme","bugs":{"url":"https://github.com/d3or/slotseek/issues"},"dist":{"shasum":"b88eeeb2047d648de3965e6714de9cd1d6d596a1","tarball":"https://registry.npmjs.org/@d3or/slotseek/-/slotseek-1.0.2.tgz","fileCount":22,"integrity":"sha512-k7OJ12h/43hqzlHpc7RWkQU7WE4o4SVOdmpoUFj2vDngAJ2lv5mUfkbKEXE67WTZ9hBUjP/vUa53HWBLjgUZOw==","signatures":[{"sig":"MEUCIAsZpgPN0RfsA2dmzTZ8DrWvQ5sKK3myodyc6Pv8VSZVAiEA0qhq8mhEcDHzPUbiO1YhY9lnxsDQ5AwtKhUJpnvy/gc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":50698},"jest":{"preset":"ts-jest","testMatch":["<rootDir>/tests/**/*.test.ts"],"setupFiles":["<rootDir>/jest.setup.ts"],"testEnvironment":"node"},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"fd383678c3b37ac6afcb5ea452f1e5130a9241b6","scripts":{"test":"jest","build":"tsc","test:watch":"jest --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"d3or","email":"deorrunner@gmail.com"},"repository":{"url":"git+https://github.com/d3or/slotseek.git","type":"git"},"_npmVersion":"10.7.0","description":"A library for finding the storage slots on an ERC20 token for balances and approvals, which can be used to mock the balances and approvals of an address when estimating gas costs of transactions that would fail if the address did not have the required bal","directories":{},"_nodeVersion":"18.20.3","dependencies":{"ethers":"^5.7.2"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","dotenv":"^16.4.5","ts-jest":"^29.1.0","typescript":"^4.9.5","@types/jest":"^29.5.12"},"peerDependencies":{"ethers":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/slotseek_1.0.2_1720800052878_0.4519690023926275","host":"s3://npm-registry-packages"}},"1.1.2":{"name":"@d3or/slotseek","version":"1.1.2","keywords":["ethereum","erc20"],"author":{"name":"d3or"},"license":"MIT","_id":"@d3or/slotseek@1.1.2","maintainers":[{"name":"d3or","email":"deorrunner@gmail.com"}],"homepage":"https://github.com/d3or/slotseek#readme","bugs":{"url":"https://github.com/d3or/slotseek/issues"},"dist":{"shasum":"bbd0430637cb2ebfcd1980585dc1740a1d93cd3b","tarball":"https://registry.npmjs.org/@d3or/slotseek/-/slotseek-1.1.2.tgz","fileCount":26,"integrity":"sha512-oDRm9nmcwRKHQStBtOnkI6X0tLzVbkdK1dQrXtz+jzoYUI0cYcwPJ/nIX+HfxaY+fEHZ2fqGP0dl/NY6vd2HoQ==","signatures":[{"sig":"MEUCIDThBWfTSlpmf1vFElxF7NYtj0OxzU1Vw+G3UQvrX7BwAiEArxNyryUrgisACzK6es+FgXi7a7557MXcjxkAsfjBN8I=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":58997},"jest":{"preset":"ts-jest","testMatch":["<rootDir>/tests/**/*.test.ts"],"setupFiles":["<rootDir>/jest.setup.ts"],"testEnvironment":"node"},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"de40af71d88a68bfcab7fd5e06f10fee85ff55d0","scripts":{"test":"jest","build":"tsc","test:watch":"jest --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"d3or","email":"deorrunner@gmail.com"},"repository":{"url":"git+https://github.com/d3or/slotseek.git","type":"git"},"_npmVersion":"10.8.2","description":"A library for finding the storage slots on an ERC20 token for balances and approvals, which can be used to mock the balances and approvals of an address when estimating gas costs of transactions that would fail if the address did not have the required bal","directories":{},"_nodeVersion":"22.5.1","dependencies":{"ethers":"^5.7.2"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","dotenv":"^16.4.5","ts-jest":"^29.1.0","typescript":"^4.9.5","@types/jest":"^29.5.12"},"peerDependencies":{"ethers":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/slotseek_1.1.2_1731744330116_0.46572835631870113","host":"s3://npm-registry-packages"}},"1.2.0":{"name":"@d3or/slotseek","version":"1.2.0","keywords":["ethereum","erc20"],"author":{"name":"d3or"},"license":"MIT","_id":"@d3or/slotseek@1.2.0","maintainers":[{"name":"d3or","email":"deorrunner@gmail.com"}],"homepage":"https://github.com/d3or/slotseek#readme","bugs":{"url":"https://github.com/d3or/slotseek/issues"},"dist":{"shasum":"36e2ddfdaebd1cc6f3d35ce555b386602d4d8887","tarball":"https://registry.npmjs.org/@d3or/slotseek/-/slotseek-1.2.0.tgz","fileCount":26,"integrity":"sha512-6sJCbDpE9QA47WZS0xKVHut+cDx8ycwrB7cduIYm/7VXC0B5W0rx74cmjM+fjTXMAtwnUfbMSANqY5Pdp/nJYg==","signatures":[{"sig":"MEUCIQDG7dz4JkxdjwVlo3N72vm0hQQcTQg60PMmOeV45omj+AIgNtVwp58XkUtVmdTbPnxzTosU/Uy/6JVSpxRcYT0JmEI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":77906},"jest":{"preset":"ts-jest","testMatch":["<rootDir>/tests/**/*.test.ts"],"setupFiles":["<rootDir>/jest.setup.ts"],"testEnvironment":"node"},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=14.0.0"},"scripts":{"test":"jest","build":"tsc","prepack":"npm run build","test:watch":"jest --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"d3or","email":"deorrunner@gmail.com"},"repository":{"url":"git+https://github.com/d3or/slotseek.git","type":"git"},"_npmVersion":"10.8.2","description":"A library for finding the storage slots on an ERC20 token for balances and approvals, which can be used to mock the balances and approvals of an address when estimating gas costs of transactions that would fail if the address did not have the required bal","directories":{},"_nodeVersion":"20.18.0","dependencies":{"ethers":"^5.7.2"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","dotenv":"^16.4.5","ts-jest":"^29.1.0","typescript":"^4.9.5","@types/jest":"^29.5.12"},"peerDependencies":{"ethers":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/slotseek_1.2.0_1785340756231_0.4713263030708057","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@d3or/slotseek","version":"1.3.0","keywords":["ethereum","erc20"],"author":{"name":"d3or"},"license":"MIT","_id":"@d3or/slotseek@1.3.0","maintainers":[{"name":"d3or","email":"deorrunner@gmail.com"}],"homepage":"https://github.com/d3or/slotseek#readme","bugs":{"url":"https://github.com/d3or/slotseek/issues"},"dist":{"shasum":"0f932721c63c007ea21786bf1728ff1998b82393","tarball":"https://registry.npmjs.org/@d3or/slotseek/-/slotseek-1.3.0.tgz","fileCount":26,"integrity":"sha512-7y1B899Ah6+Ms9Rk/DuY4oHklqW18tYfie/cBzzJvmGCX/KmOs4Ld1ww3BsmHvJt0UFhpENdYKQrdtwCVp6bYg==","signatures":[{"sig":"MEYCIQCg6e17gjv5WDakMy4BUOWPlWTKdPTeRgCO4hoKl3AHuAIhANbIdkN3ObtCDvhneqFziYsyAApoMnKH6cjPz6bl9mHm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":102906},"jest":{"preset":"ts-jest","testMatch":["<rootDir>/tests/**/*.test.ts"],"setupFiles":["<rootDir>/jest.setup.ts"],"testEnvironment":"node"},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"912a021a6127e9d4d40dce21a787112632ea1ee7","scripts":{"test":"jest","build":"tsc","prepack":"npm run build","test:watch":"jest --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"d3or","email":"deorrunner@gmail.com"},"repository":{"url":"git+https://github.com/d3or/slotseek.git","type":"git"},"_npmVersion":"10.8.2","description":"A library for finding the storage slots on an ERC20 token for balances and approvals, which can be used to mock the balances and approvals of an address when estimating gas costs of transactions that would fail if the address did not have the required bal","directories":{},"_nodeVersion":"20.18.0","dependencies":{"ethers":"^5.7.2"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","dotenv":"^16.4.5","ts-jest":"^29.1.0","typescript":"^4.9.5","@types/jest":"^29.5.12"},"peerDependencies":{"ethers":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/slotseek_1.3.0_1785800445290_0.5512914769013952","host":"s3://npm-registry-packages-npm-production"}},"1.3.1":{"name":"@d3or/slotseek","version":"1.3.1","description":"A library for finding the storage slots on an ERC20 token for balances and approvals, which can be used to mock the balances and approvals of an address when estimating gas costs of transactions that would fail if the address did not have the required bal","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage","prepack":"npm run build","prepublishOnly":"npm run build && npm test"},"jest":{"preset":"ts-jest","testEnvironment":"node","setupFiles":["<rootDir>/jest.setup.ts"],"testMatch":["<rootDir>/tests/**/*.test.ts"]},"keywords":["ethereum","erc20"],"author":{"name":"d3or"},"license":"MIT","dependencies":{"ethers":"^5.7.2"},"devDependencies":{"@types/jest":"^29.5.12","dotenv":"^16.4.5","jest":"^29.7.0","ts-jest":"^29.1.0","typescript":"^4.9.5"},"peerDependencies":{"ethers":"^5.7.2"},"engines":{"node":">=14.0.0"},"repository":{"type":"git","url":"git+https://github.com/d3or/slotseek.git"},"bugs":{"url":"https://github.com/d3or/slotseek/issues"},"homepage":"https://github.com/d3or/slotseek#readme","_id":"@d3or/slotseek@1.3.1","_nodeVersion":"20.18.0","_npmVersion":"10.8.2","dist":{"integrity":"sha512-mj/6jRLKG2iYWjh/JpxFJf9Pfn5Ex+Qu1Eay8LEkVniQgKCg/NyQxpEAvrX1nYDWzlsqtihn7wSsW6+w1qIB0w==","shasum":"f833a0c90188adc33195eb079ede3454594708fa","tarball":"https://registry.npmjs.org/@d3or/slotseek/-/slotseek-1.3.1.tgz","fileCount":26,"unpackedSize":103229,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC2uPofnIcCtulHtRhnEEXp4VrSebq4l3L/8iChQJNoCwIhALh9QFp7Wb2l/RnN82xE/WXeMu5VNamIBtx9fuQ0QjBm"}]},"_npmUser":{"name":"d3or","email":"deorrunner@gmail.com"},"directories":{},"maintainers":[{"name":"d3or","email":"deorrunner@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/slotseek_1.3.1_1786399689453_0.4430520332972909"},"_hasShrinkwrap":false}},"time":{"created":"2024-07-06T23:54:47.194Z","modified":"2026-08-10T22:08:09.773Z","1.0.0":"2024-07-06T23:54:47.446Z","1.0.1":"2024-07-11T16:53:31.428Z","1.0.2":"2024-07-12T16:00:53.042Z","1.1.2":"2024-11-16T08:05:30.475Z","1.2.0":"2026-07-29T15:59:16.549Z","1.3.0":"2026-08-03T23:40:45.467Z","1.3.1":"2026-08-10T22:08:09.604Z"},"bugs":{"url":"https://github.com/d3or/slotseek/issues"},"author":{"name":"d3or"},"license":"MIT","homepage":"https://github.com/d3or/slotseek#readme","keywords":["ethereum","erc20"],"repository":{"type":"git","url":"git+https://github.com/d3or/slotseek.git"},"description":"A library for finding the storage slots on an ERC20 token for balances and approvals, which can be used to mock the balances and approvals of an address when estimating gas costs of transactions that would fail if the address did not have the required bal","maintainers":[{"name":"d3or","email":"deorrunner@gmail.com"}],"readme":"# slotseek\n\n<a href=\"https://www.npmjs.com/package/@d3or/slotseek/\"><img src=\"https://img.shields.io/npm/v/@d3or/slotseek.svg\" alt=\"NPM version\"></a>\n<a href=\"https://twitter.com/intent/follow?screen_name=deor\"><img src=\"https://img.shields.io/twitter/follow/deor.svg?style=social&label=Follow%20@deor\" alt=\"Follow on Twitter\" /></a>\n<a href=\"https://github.com/d3or/slotseek/actions/workflows/test.yml\"><img src=\"https://github.com/d3or/slotseek/actions/workflows/test.yml/badge.svg\" alt=\"Build Status\" /></a>\n\nslotseek is a javascript library that assists with finding the storage slots for the `balanceOf` and `allowance` mappings in an ERC20 token contract, and the permit2 allowance mapping. It also provides a way to generate mock data that can be used to override the state of a contract in an `eth_call` or `eth_estimateGas` call.\n\nThe main use case for this library is to estimate gas costs of transactions that would fail if the address did not have the required balance or approval.\n\nFor example, estimating the gas a transaction will consume when swapping, before the user has approved the contract to spend their tokens.\n\n## Features\n\n- Find storage slots for `balanceOf` and `allowance` mappings in an ERC20 token contract, and permit2 allowance mapping\n- Generates mock data that can be used to override the state of a contract in an `eth_call`/`eth_estimateGas` call\n- Supports [vyper storage layouts](https://docs.vyperlang.org/en/stable/scoping-and-declarations.html#storage-layout)\n\n## How it works\n\nThe library uses a brute force approach to find the storage slot of the `balanceOf` and `allowance` mappings in an ERC20 token contract. It does this by using a user-provided address that we know has a balance or approval, and then iterates through the storage slots of the contract via the `eth_getStorageAt` JSON-RPC method until it finds the slot where the storage value matches the user's balance or approval.\n\nThis is not a perfect method, and there are more efficient ways to find the storage slot outside of just interacting directly with the contract over RPC. But it's difficult to do so without needing to setup more tools/infra, especially for multi-chain support and gas estimation at runtime. Also, there are not many tools to help with this in javascript.\n\n## Installation\n\n```bash\nnpm install @d3or/slotseek\n# or\nyarn add @d3or/slotseek\n```\n\n## Optional verified-layout cache\n\nApplications can supply any async cache implementation. slotseek does not create a\nnetwork connection or depend on a particular cache client.\n\n```typescript\nimport { generateMockBalanceData, StorageLayoutCacheAdapter } from \"@d3or/slotseek\";\n\nconst cache: StorageLayoutCacheAdapter = {\n  get: async (key) => JSON.parse((await redis.get(key)) ?? \"null\"),\n  set: async (key, layout, ttlSeconds) => {\n    await redis.set(key, JSON.stringify(layout), \"EX\", ttlSeconds);\n  },\n};\n\nawait generateMockBalanceData(provider, {\n  tokenAddress,\n  holderAddress,\n  mockAddress,\n  cache,\n  chainId: 8453, // optional; otherwise resolved from provider.getNetwork()\n  cacheTtlSeconds: 7 * 24 * 60 * 60,\n  cacheTimeoutMs: 200,\n});\n```\n\nKeys are versioned and scoped by layout kind, chain ID, and lowercased token.\nLayouts verified against a positive on-chain balance or allowance are cached with\nthe long `cacheTtlSeconds` TTL (default 7 days). Malformed, expired, failed, or\nslow cache reads are treated as misses, and cache write failures never fail slot\ndiscovery.\n\n### Negative caching\n\nFailed discoveries are also cached, with a short TTL\n(`negativeCacheTtlSeconds`, default 15 minutes), so repeated quotes for\nunsupported tokens do not re-run the full storage probe on every call:\n\n- Approval discovery that finds a zero allowance (reason `zero-allowance`) or\n  exhausts all probes (reason `not-found`) writes a negative marker. Markers\n  are keyed by `(kind, chainId, token)`, but allowance is owner/spender\n  specific, so a fresh `zero-allowance` hit re-checks the current pair with a\n  single `allowance()` call: if it is still zero the caller receives the\n  fallback approval slot (10) with no storage probes; if it is positive the\n  marker is ignored and full discovery runs for that pair.\n- Balance discovery that exhausts all probes despite a positive on-chain\n  balance writes a `not-found` marker, and fresh hits fail fast without RPC.\n  Zero-balance outcomes are holder-specific and are never negative-cached.\n- `not-found` markers record the probe budget (`maxSlots`) used; a caller\n  searching more slots than the marker covered ignores it and retries.\n- A search in which any storage probe rejected (rate limit, provider error) is\n  treated as transient and never negative-cached.\n- Negative markers carry an absolute `expiresAt` set by the writer; readers\n  honor the minimum of that and their own `negativeCacheTtlSeconds`, so a\n  marker never outlives its writer's TTL. After expiry, discovery runs again.\n- Concurrent callers of the same discovery (same token, and same\n  holder/owner/spender and probe budget) share the in-flight result, including\n  failures and rejections, so a burst of identical quotes performs at most one\n  probe sequence.\n\n### Observability\n\nPass `onCacheEvent` to receive structured cache telemetry for wiring into\nmetrics or logs. Events carry `type`, `kind` (`balance` / `approval`),\n`chainId`, `tokenAddress`, and (for negative outcomes) `reason`. Event types:\n\n- `local_hit` - served from the in-process cache\n- `external_hit` - served from the application-provided cache adapter\n- `negative_hit` - a fresh negative marker was consumed\n- `verified` - discovery succeeded and the layout was cached\n- `discovery_failed` - discovery failed and a negative outcome was recorded\n- `cache_error` - the external adapter threw or timed out (fail-open)\n\nThe callback may be sync or async; exceptions and rejections are swallowed.\n\n```typescript\nawait generateMockApprovalData(provider, {\n  ...args,\n  cache,\n  onCacheEvent: (event) => metrics.increment(`slotseek.cache.${event.type}`),\n});\n```\n\n## TODO\n\n- [X] Add caching options to reduce the number of RPC calls and reduce the time it takes to find the same slot again\n\n## Example of overriding a users balance via eth_call\n\n```javascript\nimport { ethers } from \"ethers\";\nimport { generateMockBalanceData } from \"@d3or/slotseek\";\n\nasync function fakeUserBalance() {\n  // Setup - Base RPC\n  const provider = new ethers.providers.JsonRpcProvider(\"YOUR_RPC_URL\");\n\n  // Constants\n  const tokenAddress = \"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\"; // USDC on Base\n  const holderAddress = \"0x0000c3Caa36E2d9A8CD5269C976eDe05018f0000\"; // USDC holder\n  const mockAddress = ethers.Wallet.createRandom().address; // Address to fake balance for\n  const mockBalanceAmount = \"1000000000000\"; // 1 million USDC (6 decimal places), optional. If not provided, defaults to the balance of the holder\n\n  // Generate mock balance data\n  const data = await generateMockBalanceData(provider, {\n    tokenAddress,\n    holderAddress,\n    mockAddress,\n    mockBalanceAmount,\n  });\n\n  // Prepare state diff object\n  const stateDiff = {\n    [tokenAddress]: {\n      stateDiff: {\n        [data.slot]: data.balance,\n      },\n    },\n  };\n\n  // Prepare balanceOf call\n  const balanceOfSelector = \"0x70a08231\";\n  const encodedAddress = ethers.utils.defaultAbiCoder\n    .encode([\"address\"], [mockAddress])\n    .slice(2);\n  const getBalanceCalldata = balanceOfSelector + encodedAddress;\n\n  // Make the eth_call with state overrides, or eth_estimateGas\n  const balanceOfResponse = await provider.send(\"eth_call\", [\n    {\n      from: mockAddress,\n      to: tokenAddress,\n      data: getBalanceCalldata,\n    },\n    \"latest\",\n    stateDiff,\n  ]);\n\n  // Decode and log the result\n  const balance = ethers.BigNumber.from(\n    ethers.utils.defaultAbiCoder.decode([\"uint256\"], balanceOfResponse)[0]\n  );\n\n  console.log(\n    `Mocked balance for ${mockAddress}: ${ethers.utils.formatUnits(\n      balance,\n      6\n    )} USDC`\n  );\n}\n\nfakeUserBalance().catch(console.error);\n```\n\nThis can also be used to fake approvals, by using the `generateMockApprovalData` function instead of `generateMockBalanceData`.\n\n```javascript\nimport { ethers } from \"ethers\";\nimport { generateMockApprovalData } from \"@d3or/slotseek\";\n\nasync function fakeUserApproval() {\n  // Setup\n  const provider = new ethers.providers.JsonRpcProvider(\"YOUR_RPC_URL\");\n\n  // Constants\n  const tokenAddress = \"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\"; // USDC on Base\n  const ownerAddress = \"0x0000c3Caa36E2d9A8CD5269C976eDe05018f0000\"; // USDC holder\n  const spenderAddress = \"0x000000000022D473030F116dDEE9F6B43aC78BA3\"; // Spender address\n  const mockAddress = ethers.Wallet.createRandom().address; // Address to fake balance for\n  const mockApprovalAmount = \"1000000000000\"; // 1 million USDC (6 decimal places)\n\n  // Generate mock approval data\n  const mockApprovalData = await generateMockApprovalData(provider, {\n    tokenAddress,\n    ownerAddress,\n    spenderAddress,\n    mockAddress,\n    mockApprovalAmount,\n  });\n\n  // Prepare state diff object\n  const stateDiff = {\n    [tokenAddress]: {\n      stateDiff: {\n        [mockApprovalData.slot]: mockApprovalData.approval,\n      },\n    },\n  };\n\n  // Function selector for allowance(address,address)\n  const allowanceSelector = \"0xdd62ed3e\";\n  // Encode the owner and spender addresses\n  const encodedAddresses = ethers.utils.defaultAbiCoder\n    .encode([\"address\", \"address\"], [mockAddress, spenderAddress])\n    .slice(2);\n  const getAllowanceCalldata = allowanceSelector + encodedAddresses;\n\n  // Make the eth_call with state overrides, or eth_estimateGas\n  const allowanceResponse = await provider.send(\"eth_call\", [\n    {\n      from: mockAddress,\n      to: tokenAddress,\n      data: getAllowanceCalldata,\n    },\n    \"latest\",\n    stateDiff,\n  ]);\n\n  // Decode and log the result\n  const allowance = ethers.BigNumber.from(\n    ethers.utils.defaultAbiCoder.decode([\"uint256\"], allowanceResponse)[0]\n  );\n\n  console.log(\n    `Mocked allowance for ${mockAddress}: ${ethers.utils.formatUnits(\n      allowance,\n      6\n    )} USDC`\n  );\n}\n\nfakeUserApproval().catch(console.error);\n```\n\nYou can also override both the balance and the allowance at the same time by providing both the `balance` and `approval` fields in the state diff object.\n\n## Example of just finding the storage slot in a contract\n\n```javascript\nimport { ethers } from \"ethers\";\nimport { getErc20BalanceStorageSlot } from \"@d3or/slotseek\";\n\nasync function findStorageSlot() {\n  // Setup - Base RPC\n  const provider = new ethers.providers.JsonRpcProvider(\n    \"https://mainnet.base.org\"\n  );\n\n  // Constants\n  const tokenAddress = \"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\"; // USDC on Base\n  const holderAddress = \"0x0000c3Caa36E2d9A8CD5269C976eDe05018f0000\"; // USDC holder\n  const maxSlots = 100; // Max slots to search\n\n  // Find the storage slot for the balance of the holde\n  // or for approvals, use getErc20AllowanceStorageSlot\n  const { slot, balance, isVyper } = await getErc20BalanceStorageSlot(\n    provider,\n    tokenAddress,\n    holderAddress,\n    maxSlots\n  );\n\n  console.log(\n    `User has balance of ${ethers.utils.formatUnits(\n      balance,\n      6\n    )} USDC stored at slot #${Number(slot)}`\n  );\n}\n\nfindStorageSlot().catch(console.error);\n```\n\n## Example of mocking the permit2 allowance mapping \n\n```javascript\nimport { ethers } from \"ethers\";\nimport { computePermit2AllowanceStorageSlot } from \"@d3or/slotseek\";\n\nasync function findStorageSlot() {\n  // Setup - Base RPC\n  const provider = new ethers.providers.JsonRpcProvider(\n    \"https://mainnet.base.org\"\n  );\n\n  // Constants\n  const tokenAddress = \"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\"; // USDC on Base\n  const mockAddress = \"0x0000c3Caa36E2d9A8CD5269C976eDe05018f0000\"; // USDC holder to mock approval for\n  const spenderAddress = \"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\"\n\n  // Compute storage slot of where the allowance would be held\n  const { slot } = computePermit2AllowanceStorageSlot(mockAddress, tokenAddress, spenderAddress)\n\n  const permit2Contract = '0x000000000022d473030f116ddee9f6b43ac78ba3'\n\n  // Prepare state diff object\n  const stateDiff = {\n    [permit2Contract]: {\n      stateDiff: {\n        [slot]: ethers.utils.hexZeroPad(\n          ethers.utils.hexlify(ethers.BigNumber.from(\"1461501637330902918203684832716283019655932142975\")),\n          32\n        )\n        ,\n      },\n    },\n  };\n\n  // Function selector for allowance(address,address,address)\n  const allowanceSelector = \"0x927da105\";\n  // Encode the owner and spender addresses\n  const encodedAddresses = ethers.utils.defaultAbiCoder\n    .encode([\"address\", \"address\", \"address\"], [mockAddress, tokenAddress, spenderAddress])\n    .slice(2);\n  const getAllowanceCalldata = allowanceSelector + encodedAddresses;\n\n\n  const callParams = [\n    {\n      to: permit2Contract,\n      data: getAllowanceCalldata,\n    },\n    \"latest\",\n  ];\n\n  const allowanceResponse = await baseProvider.send(\"eth_call\", [\n    ...callParams,\n    stateDiff,\n  ]);\n\n  // convert the response to a BigNumber\n  const approvalAmount = ethers.BigNumber.from(\n    ethers.utils.defaultAbiCoder.decode([\"uint256\"], allowanceResponse)[0]\n  );\n\n  console.log(\n    `Mocked balance for ${mockAddress}: ${ethers.utils.formatUnits(\n      approvalAmount,\n      6\n    )} USDC`\n  );\n\n}\nfindStorageSlot().catch(console.error);\n```\n","readmeFilename":"README.md"}