> For the complete documentation index, see [llms.txt](https://help.tokenpocket.pro/developer-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.tokenpocket.pro/developer-cn/agentic-wallet/eip-7715-account-with-permissions/ke-yong-quan-xian/erc20-dai-bi-quan-xian.md).

# ERC-20 代币权限

okenPocket 支持两种 ERC-20 执行权限：**周期性（periodic）** 与 **流式（stream）**。均需在 `permission.data` 中指定 `tokenAddress`，金额使用 hex 编码的最小单位。

### 公共字段 <a href="#e5-85-ac-e5-85-b1-e5-a-d-97-e6-ae-b5" id="e5-85-ac-e5-85-b1-e5-a-d-97-e6-ae-b5"></a>

| 字段              | 类型        | 说明          |
| --------------- | --------- | ----------- |
| `tokenAddress`  | `address` | ERC-20 合约地址 |
| `justification` | `string`  | 展示给用户的授权说明  |

请求级字段（与权限类型无关）：

| 字段                    | 说明                  |
| --------------------- | ------------------- |
| `chainId`             | hex 字符串，如 `"0x38"`  |
| `to`                  | Session Account     |
| `rules`               | 含 `expiry` 规则定义过期时间 |
| `isAdjustmentAllowed` | 是否允许钱包在用户确认前调整授权范围  |

***

### ERC-20 周期性权限 `erc20-token-periodic` <a href="#erc-20-e5-91-a8-e6-9c-9f-e6-80-a7-e6-9d-83-e9-99-90-erc20-token-periodic" id="erc-20-e5-91-a8-e6-9c-9f-e6-80-a7-e6-9d-83-e9-99-90-erc20-token-periodic"></a>

每个周期内允许使用的最大转账额度；新周期开始时额度重置。

#### 适用场景 <a href="#e9-80-82-e7-94-a8-e5-9c-ba-e6-99-af" id="e9-80-82-e7-94-a8-e5-9c-ba-e6-99-af"></a>

* 每日订阅扣款
* 每日交易限额
* 周期性定投上限

#### `permission.data` 字段 <a href="#permissiondata-e5-a-d-97-e6-ae-b5" id="permissiondata-e5-a-d-97-e6-ae-b5"></a>

| 字段               | 类型        | 说明                      |
| ---------------- | --------- | ----------------------- |
| `tokenAddress`   | `address` | ERC-20 地址               |
| `periodAmount`   | hex       | 每周期最大额度（最小单位）           |
| `periodDuration` | `number`  | 周期长度（秒），如 `86400` = 1 天 |
| `justification`  | `string`  | 授权说明                    |

#### 示例：每日 10 USDT（BSC） <a href="#e7-a4-ba-e4-be-8b-e6-af-8f-e6-97-a5-10-usdtbsc" id="e7-a4-ba-e4-be-8b-e6-af-8f-e6-97-a5-10-usdtbsc"></a>

BSC 上 USDT (`0x55d398...`) 精度为 18：

```typescript
import { parseUnits, toHex } from "viem";

const expiry = Math.floor(Date.now() / 1000) + 7 * 86400;

await provider.request({
  method: "wallet_requestExecutionPermissions",
  params: [
    {
      chainId: "0x38",
      to: sessionAddress,
      permission: {
        type: "erc20-token-periodic",
        isAdjustmentAllowed: true,
        data: {
          tokenAddress: "0x55d398326f99059fF775485246999027B3197955",
          periodAmount: toHex(parseUnits("10", 18)),
          periodDuration: 86400,
          justification: "允许 DApp/Agent 每日最多转出 10 USDT",
        },
      },
      rules: [{ type: "expiry", data: { timestamp: expiry } }],
    },
  ],
});
```

#### 额度计算逻辑 <a href="#e9-a2-9d-e5-ba-a6-e8-ae-a1-e7-ae-97-e9-80-bb-e8-be-91" id="e9-a2-9d-e5-ba-a6-e8-ae-a1-e7-ae-97-e9-80-bb-e8-be-91"></a>

```
周期编号 = floor((当前时间 - startTime) / periodDuration)
本周期已用 + 本次请求 ≤ periodAmount  →  允许
```

> `startTime` 由钱包在授权时设定，通常为授权生效时刻。

#### Redeem 执行内容 <a href="#redeem-e6-89-a7-e8-a1-8c-e5-86-85-e5-ae-b9" id="redeem-e6-89-a7-e8-a1-8c-e5-86-85-e5-ae-b9"></a>

对 ERC-20 周期性权限，redeem 时执行 `token.transfer(payee, amount)`：

```typescript
import { encodeFunctionData } from "viem";

const calldata = encodeFunctionData({
  abi: erc20Abi,
  functionName: "transfer",
  args: [payeeAddress, amountInBaseUnit],
});
// execution.target = tokenAddress, execution.value = 0
```

***

### ERC-20 流式权限 `erc20-token-stream` <a href="#erc-20-e6-b5-81-e5-bc-8f-e6-9d-83-e9-99-90-erc20-token-stream" id="erc-20-e6-b5-81-e5-bc-8f-e6-9d-83-e9-99-90-erc20-token-stream"></a>

额度随时间线性累计：起始释放 `initialAmount`，之后按 `amountPerSecond` 增速，总量不超过 `maxAmount`。

#### 适用场景 <a href="#e98082e794a8e59cbae699af-1" id="e98082e794a8e59cbae699af-1"></a>

* 按秒/按分钟释放的订阅额度
* 平滑限流的自动交易
* 工资/奖励流式发放

#### `permission.data` 字段 <a href="#permissiondata-e5ad97e6aeb5-1" id="permissiondata-e5ad97e6aeb5-1"></a>

| 字段                | 类型        | 说明             |
| ----------------- | --------- | -------------- |
| `tokenAddress`    | `address` | ERC-20 地址      |
| `initialAmount`   | hex       | 开始时立即可用额度      |
| `maxAmount`       | hex       | 累计可用上限         |
| `amountPerSecond` | hex       | 每秒新增额度         |
| `startTime`       | `number`  | 流式开始时间（Unix 秒） |
| `justification`   | `string`  | 授权说明           |

#### 流速换算 <a href="#e6-b5-81-e9-80-9f-e6-8d-a2-e7-ae-97" id="e6-b5-81-e9-80-9f-e6-8d-a2-e7-ae-97"></a>

按「每周期最大数量 ÷ 周期秒数」计算 `amountPerSecond`：

```
amountPerSecond = periodMaxAmount / periodSeconds
```

示例：每月 10 USDT → `parseUnits('10', 18) / (30 * 86400)` 再 `toHex`

#### 示例：流式 USDC <a href="#e7-a4-ba-e4-be-8b-e6-b5-81-e5-bc-8f-usdc" id="e7-a4-ba-e4-be-8b-e6-b5-81-e5-bc-8f-usdc"></a>

```typescript
import { parseUnits, toHex } from "viem";

const now = Math.floor(Date.now() / 1000);
const expiry = now + 30 * 86400;
const amountPerSecond = parseUnits("0.1", 6);

await provider.request({
  method: "wallet_requestExecutionPermissions",
  params: [
    {
      chainId: "0x89", // Polygon
      to: sessionAddress,
      permission: {
        type: "erc20-token-stream",
        isAdjustmentAllowed: true,
        data: {
          tokenAddress: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
          initialAmount: toHex(parseUnits("1", 6)),
          maxAmount: toHex(parseUnits("100", 6)),
          amountPerSecond: toHex(amountPerSecond),
          startTime: now + 120,
          justification: "流式 USDC 授权，用于自动交易",
        },
      },
      rules: [{ type: "expiry", data: { timestamp: expiry } }],
    },
  ],
});
```

#### 额度计算逻辑 <a href="#e9a29de5baa6e8aea1e7ae97e980bbe8be91-1" id="e9a29de5baa6e8aea1e7ae97e980bbe8be91-1"></a>

```
已过秒数 = max(0, 当前时间 - startTime)
累计额度 = initialAmount + amountPerSecond × 已过秒数
可用额度 = min(累计额度, maxAmount) - 已使用额度
```

> 流速不是单笔上限，而是随时间持续累计的池子；单笔转账仍受当前可用额度约束。

#### 校验约束 <a href="#e6-a0-a1-e9-aa-8c-e7-ba-a6-e6-9d-9f" id="e6-a0-a1-e9-aa-8c-e7-ba-a6-e6-9d-9f"></a>

* `maxAmount >= initialAmount`
* `amountPerSecond > 0`（按 decimals 换算后不能为 0）
* `startTime` 由业务决定
