> For the complete documentation index, see [llms.txt](https://help.tokenpocket.pro/developer-en/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-en/agentic-wallet/eip-7715-account-with-permissions/available-permissions/erc-20-token-permissions.md).

# ERC-20 Token Permissions

TokenPocket supports two ERC-20 execution permission types: **periodic** and **streaming**. Both require `tokenAddress` in `permission.data`, and amounts use hex-encoded smallest units.

### Common Fields <a href="#common-fields" id="common-fields"></a>

| Field           | Type      | Description                                 |
| --------------- | --------- | ------------------------------------------- |
| `tokenAddress`  | `address` | ERC-20 contract address                     |
| `justification` | `string`  | Authorization description shown to the user |

Request-level fields (independent of permission type):

| Field                 | Description                                                                    |
| --------------------- | ------------------------------------------------------------------------------ |
| `chainId`             | Hex string, e.g. `"0x38"`                                                      |
| `to`                  | Session Account                                                                |
| `rules`               | Includes `expiry` rule to define expiration time                               |
| `isAdjustmentAllowed` | Whether the wallet may adjust the authorization scope before user confirmation |

***

### ERC-20 Periodic Permission `erc20-token-periodic` <a href="#erc-20-periodic-permission-erc20-token-periodic" id="erc-20-periodic-permission-erc20-token-periodic"></a>

Maximum transfer amount allowed per period; the allowance resets at the start of each new period.

#### Use Cases <a href="#use-cases" id="use-cases"></a>

* Daily subscription payments
* Daily transaction limits
* Periodic recurring investment caps

#### `permission.data` Fields <a href="#permissiondata-fields" id="permissiondata-fields"></a>

| Field            | Type      | Description                                    |
| ---------------- | --------- | ---------------------------------------------- |
| `tokenAddress`   | `address` | ERC-20 address                                 |
| `periodAmount`   | hex       | Maximum amount per period (smallest unit)      |
| `periodDuration` | `number`  | Period length in seconds, e.g. `86400` = 1 day |
| `justification`  | `string`  | Authorization description                      |

#### Example: 10 USDT per Day (BSC) <a href="#example-10-usdt-per-day-bsc" id="example-10-usdt-per-day-bsc"></a>

USDT on BSC (`0x55d398...`) has 18 decimals:

```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: "Allow DApp/Agent to transfer up to 10 USDT per day",
        },
      },
      rules: [{ type: "expiry", data: { timestamp: expiry } }],
    },
  ],
});
```

#### Allowance Calculation Logic <a href="#allowance-calculation-logic" id="allowance-calculation-logic"></a>

```
periodIndex = floor((now - startTime) / periodDuration)
usedThisPeriod + requestedAmount ≤ periodAmount  →  allowed
```

> `startTime` is set by the wallet at authorization time, typically when the authorization takes effect.

#### Redeem Execution Content <a href="#redeem-execution-content" id="redeem-execution-content"></a>

For ERC-20 periodic permissions, redeem executes `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 Streaming Permission `erc20-token-stream` <a href="#erc-20-streaming-permission-erc20-token-stream" id="erc-20-streaming-permission-erc20-token-stream"></a>

Allowance accrues linearly over time: starts with `initialAmount`, then increases at `amountPerSecond`, capped at `maxAmount`.

#### Use Cases <a href="#use-cases-1" id="use-cases-1"></a>

* Subscription allowance released per second/minute
* Smoothly rate-limited automated trading
* Streaming payroll / reward distribution

#### `permission.data` Fields <a href="#permissiondata-fields-1" id="permissiondata-fields-1"></a>

| Field             | Type      | Description                           |
| ----------------- | --------- | ------------------------------------- |
| `tokenAddress`    | `address` | ERC-20 address                        |
| `initialAmount`   | hex       | Immediately available amount at start |
| `maxAmount`       | hex       | Cumulative available cap              |
| `amountPerSecond` | hex       | Amount added per second               |
| `startTime`       | `number`  | Streaming start time (Unix seconds)   |
| `justification`   | `string`  | Authorization description             |

#### Flow Rate Conversion <a href="#flow-rate-conversion" id="flow-rate-conversion"></a>

Calculate `amountPerSecond` as max amount per period divided by period length in seconds:

```
amountPerSecond = periodMaxAmount / periodSeconds
```

Example: 10 USDT per month → `parseUnits('10', 18) / (30 * 86400)` then `toHex`

#### Example: Streaming USDC <a href="#example-streaming-usdc" id="example-streaming-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: "Streaming USDC authorization for automated trading",
        },
      },
      rules: [{ type: "expiry", data: { timestamp: expiry } }],
    },
  ],
});
```

#### Allowance Calculation Logic <a href="#allowance-calculation-logic-1" id="allowance-calculation-logic-1"></a>

```
elapsedSeconds = max(0, now - startTime)
accruedAllowance = initialAmount + amountPerSecond × elapsedSeconds
availableAllowance = min(accruedAllowance, maxAmount) - usedAllowance
```

> The flow rate is not a per-transaction cap, but a pool that accrues continuously over time; each transfer is still constrained by the currently available allowance.

#### Validation Constraints <a href="#validation-constraints" id="validation-constraints"></a>

* `maxAmount >= initialAmount`
* `amountPerSecond > 0` (must not be 0 after decimal conversion)
* `startTime` is determined by the business logic
