# @d3or/slotseek 中文文档

规范地址：https://npmcn.com/package/%40d3or/slotseek/

版本：1\.3\.1
npm 元数据更新时间：2026-08-10T22:08:09.773Z
中文译文更新时间：2026-11-11T08:15:07.918Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40d3or/slotseek
原文对照：https://npmcn.com/package/%40d3or/slotseek/?lang=original
项目主页：https://github.com/d3or/slotseek#readme
源码仓库：https://github.com/d3or/slotseek

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：103 kB，26 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=14\.0\.0
- 运行依赖：1 个
- peer 依赖：ethers ^5\.7\.2
- 首次发布：2024-07-06T23:54:47.194Z
- 最新版本发布：2026-08-10T22:08:09.604Z
- 近 30 天下载量：3573（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：7（近 12 个月 3 个）

## 中文简介

用于在 ERC20 令牌上找到余额和批准值的存储槽的图书馆,可用于在估计如果地址没有所需bal将失败的交易的天然气成本时,嘲笑地址的余额和批准值

## 安装

```sh
npm install @d3or/slotseek
```

也可以使用：`pnpm add @d3or/slotseek`、`yarn add @d3or/slotseek`、`bun add @d3or/slotseek`

## README

# 时档搜索

[![NPM version](https://img.shields.io/npm/v/@d3or/slotseek.svg)](https://www.npmjs.com/package/@d3or/slotseek/) [![Follow on Twitter](https://img.shields.io/twitter/follow/deor.svg?style=social&label=Follow%20@deor)](https://twitter.com/intent/follow?screen_name=deor) [![Build Status](https://github.com/d3or/slotseek/actions/workflows/test.yml/badge.svg)](https://github.com/d3or/slotseek/actions/workflows/test.yml)

槽seek是一个javascript 库,用于帮助找到存储槽 `balanceOf` 财务报告和审定财务报表 `allowance` 一份ERC20令牌合同中的绘图,以及许可证2允许绘图。 它还提供了一种生成模拟数据的方法,可以用来推翻合同状态 `eth_call` 或者说 `eth_estimateGas` 电话.

这个库的主要用途是估算如果地址没有所需的余额或批准,将失败的交易的天然气成本.

例如,在用户批准使用其代币的合同之前,估计交易在互换时将消耗的气体.

## 特征

-   查找存储槽 `balanceOf` 财务报告和审定财务报表 `allowance` eRC20令牌合同中的绘图和许可证2
-   生成可用于推翻合同状态的模拟数据 `eth_call`编号 :`eth_estimateGas` 调用
-   支助 [vyper 存储布局](https://docs.vyperlang.org/en/stable/scoping-and-declarations.html#storage-layout)

## 如何运作

库使用蛮横的强力方法寻找存储槽 `balanceOf` 财务报告和审定财务报表 `allowance` 一份ERC20令牌合约中的绘图. 它通过使用一个用户提供的地址来做到这一点,我们知道该地址有平衡或批准,然后通过合同的存储槽通过 `eth_getStorageAt` JSON-RPC方法直到找到存储值与用户的余额或批准相匹配的槽.

这不是一个完美的方法,除了直接与RPC的合同互动外,还有更有效的方法可以找到存储槽. 但若不需要设置更多的工具/基本工具,特别是多链支持和运行时的气体估计,则很难做到这一点. 而且,在Javascript中帮助解决这个问题的工具也不多.

## 安装

```bash
npm install @d3or/slotseek
# or
yarn add @d3or/slotseek
```

## 可选校验出缓存

应用程序可以提供任何Aync缓存执行. 槽seek 不创建 网络连接或依赖于特定的缓存客户端.

```typescript
import { generateMockBalanceData, StorageLayoutCacheAdapter } from "@d3or/slotseek";

const cache: StorageLayoutCacheAdapter = {
  get: async (key) => JSON.parse((await redis.get(key)) ?? "null"),
  set: async (key, layout, ttlSeconds) => {
    await redis.set(key, JSON.stringify(layout), "EX", ttlSeconds);
  },
};

await generateMockBalanceData(provider, {
  tokenAddress,
  holderAddress,
  mockAddress,
  cache,
  chainId: 8453, // optional; otherwise resolved from provider.getNetwork()
  cacheTtlSeconds: 7 * 24 * 60 * 60,
  cacheTimeoutMs: 200,
});
```

密钥通过布局类型,链式ID,和小写代号来进行版本和范围化. 根据正链上余额或备抵核实的布局被缓存 长 `cacheTtlSeconds` TTL( 默认7天) 。 格式错误、 过期、 失败或 缓存读取的缓存被视作缺失, 缓存写入失败不会失败槽 发现.

### 负缓冲

失败发现也被缓存, TTL 较短 (单位:千美元)`negativeCacheTtlSeconds`,默认为15分钟),所以重复引用 不支持的符号不会在每次呼叫时重运行完整的存储探测器 :

-   发现零备抵(理由) `zero-allowance`)或(或) 用尽所有探测器(理由) `not-found`)写作负标记. 标记 键入 `(kind, chainId, token)`,但津贴为所有者/发放者 具体,那么新鲜 `zero-allowance` 击出以 a 为单位的当前配对 单人 `allowance()` 电话:如果仍然是零,呼叫者收到 倒置批准槽(10),没有储存探测器;如果是正的 标记被忽略了 完全发现这对.
-   平衡的发现,所有探测器都耗尽了 尽管在链上呈正值 余额写入 a `not-found` 标记,没有RPC,新鲜的点击会很快失败. 零平衡的结果是持有者特有的,从不负的.
-   `not-found` 标记记录探测预算(`maxSlots`已使用;一个呼叫者 搜索比所覆盖的标记更多的插槽会忽略它并重试.
-   拒绝的存储探测器( 速率限制、 提供者错误) 的搜索 视之为瞬息万变 绝不是阴性.
-   负标记带有绝对值 `expiresAt` 作者设置; 读者 尊重最低程度和他们自己的 `negativeCacheTtlSeconds`,所以一个 标记永远不会超过它的作家TTL。 期满后,发现再次运行.
-   同一发现的同名呼叫者(同同名信使和同名信使) 持有者/所有人/喷雾器和探测器预算)分享飞行中的结果,包括: 失败和拒绝,所以爆发 相同的引用最多一个 探测器序列.

### 观察性

过 `onCacheEvent` 以接收结构化缓存遥测以连接到 指标或日志。 活动 `type`, (中文(简体) ), `kind` (单位:千美元)`balance` 编号 : `approval`, (中文(简体) ), `chainId`, (中文(简体) ), `tokenAddress`,和(对于负面结果) `reason`。事件类型 :

-   `local_hit` - 从程序内缓存得到服务
-   `external_hit` - 从应用程序提供的缓存适配器服务
-   `negative_hit` - 一个新的负标记被消耗
-   `verified` - 发现成功,布局被缓存
-   `discovery_failed` - 发现失败,结果为负面
-   `cache_error` - 外部适配器抛出或超时(无法打开)

回调可以是同步或同步;例外和拒绝被吞噬.

```typescript
await generateMockApprovalData(provider, {
  ...args,
  cache,
  onCacheEvent: (event) => metrics.increment(`slotseek.cache.${event.type}`),
});
```

## 开始

-   [x] 添加缓存选项以减少 RPC 调用的次数并减少再次找到相同槽所需的时间

## 用户通过 eth call 实现平衡的示例

```javascript
import { ethers } from "ethers";
import { generateMockBalanceData } from "@d3or/slotseek";

async function fakeUserBalance() {
  // Setup - Base RPC
  const provider = new ethers.providers.JsonRpcProvider("YOUR_RPC_URL");

  // Constants
  const tokenAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // USDC on Base
  const holderAddress = "0x0000c3Caa36E2d9A8CD5269C976eDe05018f0000"; // USDC holder
  const mockAddress = ethers.Wallet.createRandom().address; // Address to fake balance for
  const mockBalanceAmount = "1000000000000"; // 1 million USDC (6 decimal places), optional. If not provided, defaults to the balance of the holder

  // Generate mock balance data
  const data = await generateMockBalanceData(provider, {
    tokenAddress,
    holderAddress,
    mockAddress,
    mockBalanceAmount,
  });

  // Prepare state diff object
  const stateDiff = {
    [tokenAddress]: {
      stateDiff: {
        [data.slot]: data.balance,
      },
    },
  };

  // Prepare balanceOf call
  const balanceOfSelector = "0x70a08231";
  const encodedAddress = ethers.utils.defaultAbiCoder
    .encode(["address"], [mockAddress])
    .slice(2);
  const getBalanceCalldata = balanceOfSelector + encodedAddress;

  // Make the eth_call with state overrides, or eth_estimateGas
  const balanceOfResponse = await provider.send("eth_call", [
    {
      from: mockAddress,
      to: tokenAddress,
      data: getBalanceCalldata,
    },
    "latest",
    stateDiff,
  ]);

  // Decode and log the result
  const balance = ethers.BigNumber.from(
    ethers.utils.defaultAbiCoder.decode(["uint256"], balanceOfResponse)[0]
  );

  console.log(
    `Mocked balance for ${mockAddress}: ${ethers.utils.formatUnits(
      balance,
      6
    )} USDC`
  );
}

fakeUserBalance().catch(console.error);
```

这也可以用来伪造批准,使用 `generateMockApprovalData` 函数代替 `generateMockBalanceData`。 。 。 .

```javascript
import { ethers } from "ethers";
import { generateMockApprovalData } from "@d3or/slotseek";

async function fakeUserApproval() {
  // Setup
  const provider = new ethers.providers.JsonRpcProvider("YOUR_RPC_URL");

  // Constants
  const tokenAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // USDC on Base
  const ownerAddress = "0x0000c3Caa36E2d9A8CD5269C976eDe05018f0000"; // USDC holder
  const spenderAddress = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; // Spender address
  const mockAddress = ethers.Wallet.createRandom().address; // Address to fake balance for
  const mockApprovalAmount = "1000000000000"; // 1 million USDC (6 decimal places)

  // Generate mock approval data
  const mockApprovalData = await generateMockApprovalData(provider, {
    tokenAddress,
    ownerAddress,
    spenderAddress,
    mockAddress,
    mockApprovalAmount,
  });

  // Prepare state diff object
  const stateDiff = {
    [tokenAddress]: {
      stateDiff: {
        [mockApprovalData.slot]: mockApprovalData.approval,
      },
    },
  };

  // Function selector for allowance(address,address)
  const allowanceSelector = "0xdd62ed3e";
  // Encode the owner and spender addresses
  const encodedAddresses = ethers.utils.defaultAbiCoder
    .encode(["address", "address"], [mockAddress, spenderAddress])
    .slice(2);
  const getAllowanceCalldata = allowanceSelector + encodedAddresses;

  // Make the eth_call with state overrides, or eth_estimateGas
  const allowanceResponse = await provider.send("eth_call", [
    {
      from: mockAddress,
      to: tokenAddress,
      data: getAllowanceCalldata,
    },
    "latest",
    stateDiff,
  ]);

  // Decode and log the result
  const allowance = ethers.BigNumber.from(
    ethers.utils.defaultAbiCoder.decode(["uint256"], allowanceResponse)[0]
  );

  console.log(
    `Mocked allowance for ${mockAddress}: ${ethers.utils.formatUnits(
      allowance,
      6
    )} USDC`
  );
}

fakeUserApproval().catch(console.error);
```

您也可以同时通过提供 `balance` 财务报告和审定财务报表 `approval` 状态 diff 对象中的字段.

## 在合同中找到存储槽的例子

```javascript
import { ethers } from "ethers";
import { getErc20BalanceStorageSlot } from "@d3or/slotseek";

async function findStorageSlot() {
  // Setup - Base RPC
  const provider = new ethers.providers.JsonRpcProvider(
    "https://mainnet.base.org"
  );

  // Constants
  const tokenAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // USDC on Base
  const holderAddress = "0x0000c3Caa36E2d9A8CD5269C976eDe05018f0000"; // USDC holder
  const maxSlots = 100; // Max slots to search

  // Find the storage slot for the balance of the holde
  // or for approvals, use getErc20AllowanceStorageSlot
  const { slot, balance, isVyper } = await getErc20BalanceStorageSlot(
    provider,
    tokenAddress,
    holderAddress,
    maxSlots
  );

  console.log(
    `User has balance of ${ethers.utils.formatUnits(
      balance,
      6
    )} USDC stored at slot #${Number(slot)}`
  );
}

findStorageSlot().catch(console.error);
```

## 嘲弄许可证的图示2

```javascript
import { ethers } from "ethers";
import { computePermit2AllowanceStorageSlot } from "@d3or/slotseek";

async function findStorageSlot() {
  // Setup - Base RPC
  const provider = new ethers.providers.JsonRpcProvider(
    "https://mainnet.base.org"
  );

  // Constants
  const tokenAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // USDC on Base
  const mockAddress = "0x0000c3Caa36E2d9A8CD5269C976eDe05018f0000"; // USDC holder to mock approval for
  const spenderAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"

  // Compute storage slot of where the allowance would be held
  const { slot } = computePermit2AllowanceStorageSlot(mockAddress, tokenAddress, spenderAddress)

  const permit2Contract = '0x000000000022d473030f116ddee9f6b43ac78ba3'

  // Prepare state diff object
  const stateDiff = {
    [permit2Contract]: {
      stateDiff: {
        [slot]: ethers.utils.hexZeroPad(
          ethers.utils.hexlify(ethers.BigNumber.from("1461501637330902918203684832716283019655932142975")),
          32
        )
        ,
      },
    },
  };

  // Function selector for allowance(address,address,address)
  const allowanceSelector = "0x927da105";
  // Encode the owner and spender addresses
  const encodedAddresses = ethers.utils.defaultAbiCoder
    .encode(["address", "address", "address"], [mockAddress, tokenAddress, spenderAddress])
    .slice(2);
  const getAllowanceCalldata = allowanceSelector + encodedAddresses;


  const callParams = [
    {
      to: permit2Contract,
      data: getAllowanceCalldata,
    },
    "latest",
  ];

  const allowanceResponse = await baseProvider.send("eth_call", [
    ...callParams,
    stateDiff,
  ]);

  // convert the response to a BigNumber
  const approvalAmount = ethers.BigNumber.from(
    ethers.utils.defaultAbiCoder.decode(["uint256"], allowanceResponse)[0]
  );

  console.log(
    `Mocked balance for ${mockAddress}: ${ethers.utils.formatUnits(
      approvalAmount,
      6
    )} USDC`
  );

}
findStorageSlot().catch(console.error);
```

## 常见问题

### @d3or/slotseek 是什么？

用于在 ERC20 令牌上找到余额和批准值的存储槽的图书馆,可用于在估计如果地址没有所需bal将失败的交易的天然气成本时,嘲笑地址的余额和批准值

### 如何安装 @d3or/slotseek？

在项目目录运行 npm install @d3or/slotseek，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：ethers（^5\.7\.2）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @d3or/slotseek 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @d3or/slotseek 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @d3or/slotseek 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=14\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @d3or/slotseek 可以商用吗？

@d3or/slotseek 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @d3or/slotseek 的下载量有多少？

根据 npm 官方下载统计，@d3or/slotseek 在 2026/10/11 – 2026/11/09 这 30 天内被下载 3,573 次，日均约 119 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @d3or/slotseek 还在维护吗？

最新版本 1\.3\.1 发布于 2026/08/11（3 个月前）；至今共发布 7 个版本，其中过去 12 个月发布了 3 个；首次发布于 2024/07/07。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

