# [ERC6932] 定期支払いを自動化する仕組みを理解しよう！

> ERCサブスクリプショントークンは、既存のERC20トークン（残高管理や送金のインターフェースが標準化されたトークン仕様）に、「定期支払い（サブスクリプション）」の仕組みを追加したトークンです。

- URL: https://cardene.dev/articles/b13d8405b157c0a717ce
- 公開日: 2025-11-22
- 更新日: 2025-11-22
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

今回は、定期的な支払いが必要なサービスへのアクセスを自動化し、ユーザーと提供者の双方がスムーズに利用継続できる仕組みを提案している**ERC6932**についてまとめていきます！

:::message
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-6932

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

## 概要

**ERCサブスクリプショントークン**は、既存の**ERC20**トークン（残高管理や送金のインターフェースが標準化されたトークン仕様）に、「**定期支払い（サブスクリプション）**」の仕組みを追加したトークンです。
通常の**ERC20**トークンと同じように残高を持ち、送金もできますが、これに加えて以下のようなサブスクリプション専用の機能を持ちます。

:::message
**ERC20**については以下の記事を参考にしてください。
:::

[\[ERC20\] ERC20を1からわかりやすく解説](/articles/aeefe2d89dd5332c9f43)

* `subscribe` / `unsubscribe` 関数
    ユーザーがあるサービスに「**登録する**」、「**登録をやめる**」ための関数です。
    `subscribe` を呼ぶことでサブスクリプションが開始され、`unsubscribe` を呼ぶことで今後の定期課金が止まります。

* `subscriptionFee` と `subscriptionFrequency` 変数
    `subscriptionFee` はサブスクリプションの料金、`subscriptionFrequency` は課金の間隔（例えば30日ごと、90日ごとなど）を表します。
    これらはコントラクト内の変数として保持され、ユーザーが支払うべき金額と、その支払いがどのくらいの周期で発生するかを一意に決めます。

* `nextPaymentDate` マッピング
    マッピング（アドレスごとに値を紐づけて管理する仕組み）を使って、各ユーザーが「**次にいつ支払いが発生するか**」の日時を記録します。
    例えば、あるユーザーが今日サブスク登録した場合、そのユーザーのアドレスに対して「**次回支払い日**」が記録され、時間が進んでその日時を過ぎると、トークンコントラクトがその情報を使って自動的に料金を差し引きます。

**ERCサブスクリプショントークン**は、マーチャント（サービス提供者）が決めた条件にもとづいて、ユーザーの残高から定期的にトークンを引き落とせるようにすることを目指しています。
これにより、Ethereum上で動くサブスクリプション型サービスに対して、自動課金の仕組みを提供できます。

ユーザーとマーチャントの両方にとっての利点として、以下のような点があります。

* ユーザーは毎回手動で支払いトランザクションを送る必要がなくなり、一度サブスク登録すれば、あとは自動的に決められた間隔で支払いが行われます。
* マーチャント側は、トークンの仕様に沿ってサブスクリプションを実装すればよいため、独自実装に比べて扱いやすくなり、システム全体のシンプルさと効率が向上します。

また、**ERCサブスクリプショントークン**には `renewSubscription` というメソッドも用意されます。
これはトークン保有者が、自分のサブスクリプションを更新するときに呼び出すための関数です。
定期的な支払いが必要なサービスやプロダクトに対して、トークンを使った継続課金を行うときに利用されます。

## 動機

#### サブスクリプションモデルが抱えている課題

近年、多くのサービスが「**毎月・毎年など決まった周期で支払う**」サブスクリプション型のビジネスモデルを採用しています。
しかしEthereum上でこれを実現しようとすると、ユーザーが毎回自分でトランザクションを送って支払いを行うケースが多く、次のような問題が生じます。

* 支払いのたびにユーザーが操作する必要があり、手間がかかる
* ユーザーが支払いを忘れると、そのサービスが一時的に利用できなくなることがある

**ERCサブスクリプショントークン**は、こうした不便さやサービスの中断を解消するために、「**定期支払い**」を標準的なかたちで扱えるようにすることを目的としています。

### ERCサブスクリプショントークンが目指すもの

**ERCサブスクリプショントークン**は、ユーザーがあらかじめ定期的なトークンの引き落としを許可しておくことで、シームレスにサブスクリプションを継続できるようにします。
これにより、ユーザーは支払いを意識せずにサービスを使い続けることができ、マーチャント側も安定した課金処理を行いやすくなります。

このトークンは、サブスクリプション型のコンテンツプラットフォーム、ゲームサービスなど、定期的な支払いが必要な幅広いサービスに利用できます。
複数のサービスがこのトークン仕様にしたがってサブスクリプションを実装することで、実装の一貫性や他サービスとの互換性も得られます。

#### 主な特徴

**ERCサブスクリプショントークン**の特徴として以下の4点が挙げられています。

| 機能名 | 内容 |
| ------ | -------- |
| 自動引き落とし（Auto Deduction） | マーチャントは、自分のサービスに対するサブスクリプションの間隔と支払い金額を設定します。この情報はトークンコントラクト内に記録されます。これにより、ユーザーが一度サブスクを開始すれば、指定された間隔ごとにユーザーの残高から自動的にトークンが差し引かれます。毎回ユーザーが手動でトランザクションを送る必要はありません。               |
| 残高確認（Balance Check）     | ユーザーは、いつでも自分のサブスクリプショントークンの残高を確認できます。どれくらいトークンが残っているかを見られることで、自分のサブスクを継続するかどうか、どのタイミングでトークンを追加購入するかなどを判断しやすくなります。  |
| 柔軟性（Flexibility）        | **ERCサブスクリプショントークン**は、月額・四半期（3か月ごと）・年額といったさまざまな課金サイクルに対応できる設計を想定しています。またマーチャントは、無料トライアル期間の設定、プランのアップグレード・ダウングレード、解約ポリシーなども定義できる前提になっており、さまざまなサブスクリプションモデルに対応できる柔軟な枠組みを提供します。 |
| セキュリティ（Security）        | **ERCサブスクリプショントークン**は、暗号署名（秘密鍵でトランザクションに署名して正当性を証明する仕組み）など、既存のセキュリティ手法を利用して、サブスクリプションに関するトランザクションの安全性を確保します。これにより、ユーザーやマーチャントが不正アクセスや悪意ある操作から守られることを意図しています。                 |

#### 期待される効果

**ERCサブスクリプショントークン**を利用することで、サブスクリプション型サービスは以下のようなメリットを得られます。

* 定期課金の流れが標準化されることで、異なるサービス間でも同じトークン仕様をベースに実装でき、実装の再利用性や相互運用性が高まります。
* ユーザーは一度サブスクを開始すれば、その後は自動的に支払いが行われ、サービスが途切れにくくなります。
* マーチャントは、定期支払いに必要なロジックをトークン仕様に沿って実装するだけでよいため、サービス側の実装負荷を減らしつつ、安定した継続課金を実現できます。

このように、**ERCサブスクリプショントークン**は、Ethereum上でサブスクリプションビジネスを行うための基盤として、ユーザーとマーチャントの両方にとって便利で扱いやすい形を目指しています。

## 仕様

#### インターフェース

```js
interface ISubscriptionERC20 {
  /// @dev map subscribers address, returns address(0) if `idx` is not found
  /// @param idx: the key of the map values
  /// @return the address at key `idx` of subscribers map
  function subscribers(uint idx) external view returns (address);

  /// @dev information of the subscription token contract
  /// @return subscriptionID, subscriptionName, subscriptionDesc, subscriptionTandC
  function subscriptionInfo() external view returns ( uint, string memory, string memory, string memory );

  /// @dev subscribes to the subscription, can be payable
  function subscribe() external;

  /// @dev unsubscribe the subscription
  function unsubscribe() external;

  /// @dev view or pure can be used
  /// @return the subscription fee
  function subscriptionFee() external view returns (uint256);

  /// @dev view or pure can be used
  /// @return get the subscription frequency
  function subscriptionFrequency() external view returns (uint);
}
```

#### 関数

##### `subscribers`

```javascript
function subscribers(uint idx) external view returns (address);
```

登録済みユーザーのアドレスを取得する関数。
`subscribers` は、サブスクリプションに登録しているアドレス一覧のうち、指定した番号のアドレスを返します。
この一覧は内部的にマッピングとして管理されており、`idx` に該当するアドレスが存在しない場合は `address(0)` が返されます。
これにより、サブスクリプションコントラクトにどのユーザーが登録しているかを外部から確認できます。

**引数**

* `idx`
  * アドレス一覧のキーとなる番号。

**戻り値**

* `address`
  * 指定したキーに対応する登録アドレス。存在しない場合は `address(0)`。

##### `subscriptionInfo`

```javascript
function subscriptionInfo() external view returns (
    uint,
    string memory,
    string memory,
    string memory
);
```

サブスクリプションに関するメタデータを取得する関数。
`subscriptionInfo` は、サブスクリプションの基本情報をまとめて取得するために使用します。
返却される値には、サブスクリプションを識別するためのID、名前、説明文、利用規約などが含まれます。
外部サービスやUIは、この関数を利用してユーザーにサブスクリプション内容を表示できます。

**戻り値**

* `uint`
  * サブスクリプションID。
* `string`
  * サブスクリプション名。
* `string`
  * サブスクリプションの説明。
* `string`
  * サブスクリプションの利用規約・条件。

##### `subscribe`

```javascript
function subscribe() external;
```

アドレスをサブスクリプションに登録する関数。
`subscribe` は、ユーザーがサブスクリプションサービスに登録するときに呼び出す関数です。
内部では、呼び出したユーザーアドレスを登録リストに追加し、必要に応じて最初の支払い処理を行います。
また、この関数は支払いが発生する可能性があるため `payable` として実装される場合があります。

##### `unsubscribe`

```javascript
function unsubscribe() external;
```

登録済みのサブスクリプションを解除する関数。
`unsubscribe` は、ユーザーがサブスクリプション状態を解除するために利用します。
登録解除後は、自動的なトークンの引き落としが行われなくなります。
この関数は登録していたユーザーのみが呼び出す想定であり、内部では登録解除処理が行われます。

##### `subscriptionFee`

```javascript
function subscriptionFee() external view returns (uint256);
```

サブスクリプションの料金を取得する関数。
`subscriptionFee` は、サブスクリプション1回あたりの料金を取得するために使用します。
料金はトークン単位で指定され、登録時や定期支払い時にこの額がユーザーから差し引かれます。
外部アプリやUIはこの関数を呼び出すことで料金を表示できます。

**戻り値**

* `uint256`
  * サブスクリプション料金。

##### `subscriptionFrequency`

```javascript
function subscriptionFrequency() external view returns (uint);
```

サブスクリプションが課金される周期を取得する関数。
`subscriptionFrequency` は、次の支払いが発生するまでの間隔を秒単位で返します。
例えば、1日なら `86400` 秒、1週間なら `604800` 秒といった形です。
これにより、サービス側やUIはユーザーへ「**次回支払いがいつ発生するのか**」を表示できるようになります。

**戻り値**

* `uint`
  * 課金間隔（秒）。

## 補足

**ERCサブスクリプショントークン**は、サブスクリプションに必要な「**定期的な支払い**」を自動的に計算し、利用者の残高から正しく差し引くために、数式にもとづく処理を組み込んでいます。
これは、単純に残高を減らすだけではなく、ユーザーがサブスクを開始してからの経過時間や支払間隔を考慮し、正確な支払い量を導くための仕組みです。

以下は、支払い量を求めるロジックの一部です。

```javascript
uint256 intervals = ( block.timestamp - info.start ) / info.frequency;
uint256 amount = info.amount * intervals;

uint256 localEffectiveBalance = effectiveBalance[account];

if ( (totalAmount + amount) > localEffectiveBalance ) {
    amount = localEffectiveBalance;
}

totalAmount += ( localEffectiveBalance - amount );
```

#### ロジックの説明

この処理では、ユーザーに対して引き落とされるべき金額を計算するため、以下の考え方が使われています。

##### 経過時間から支払い回数を求める

`block.timestamp`（現在のブロックの時間）と `info.start`（サブスク開始時刻）の差を使い、どれだけ時間が経過したかを算出します。
さらに、それを `info.frequency`（支払い間隔）で割ることで「**何回分の支払いが発生しているか**」を求めます。

```
intervals = 経過時間 ÷ 支払い間隔
```

この値が支払い回数となります。

##### 支払額を算出する

支払い回数に `info.amount`（一回あたりの料金）を掛け、累計の支払額を計算します。

```
amount = 一回の支払い額 × 発生した支払い回数
```

##### そのアドレスの有効残高（effectiveBalance）を確認する

有効残高とは、すでに引き落とし対象になってロックされている残高を差し引いた「**実際に利用できる残高**」です。
これは通常の `balanceOf` とは異なり、サブスクの引き落とし処理に特化した値です。

##### 支払い額が残高を超えている場合は調整する

支払い額が有効残高より大きくなる場合、残高の範囲内に収めるため、支払い額は残高上限に調整されます。

##### 最終的に差し引かれる金額を更新する

`totalAmount` に差し引くべき金額を加算していきます。

この一連の処理により、ユーザーの残高が不足している場合も安全に対処しつつ、正確な金額を差し引くことができます。
また、送金が行われるたびに「**有効残高**」が更新されるため、サブスクの支払いに必要な残高管理が自動的に一貫して行われます。

## セキュリティ

**ERCサブスクリプショントークン**では、ユーザーがサブスク登録や解除を行うとき、またはサブスクに関する重要な操作を行うとき、トランザクションへの署名が必要になります。
これは、暗号署名を用いて「**操作を行ったのが本当にユーザー本人であること**」を保証するためのものであり、Ethereumにおける基本的なセキュリティ要素です。

しかし、暗号署名には「**秘密鍵**」が必要となるため、秘密鍵が漏洩すると不正操作が可能になってしまいます。
そのため、利用者は次のようなセキュリティ対策を徹底する必要があります。

#### 秘密鍵管理の重要性

秘密鍵とは、ウォレットの所有権を完全に証明する鍵であり、流出すると資産が盗まれたり、勝手にサブスクが操作されたりする危険があります。
そのため、安全な場所に秘密鍵を保管し、第三者に触れさせないことが重要です。

#### 安全なウォレットの利用

セキュリティを高めたいユーザーには、以下のようなウォレットの利用が推奨されます。

* ハードウェアウォレット
    物理デバイスで鍵を管理するため、オンライン攻撃に対して強く、秘密鍵がデバイス外に出ない仕組みになっています。
* セキュアなキーストア
    暗号化された形式で秘密鍵を保存する仕組みであり、復号にはパスワードが必要です。

これらの方法を使うことで、秘密鍵の盗難や不正アクセスリスクを大きく減らせます。

#### ソーシャルエンジニアリング対策

フィッシングや偽サイト誘導といった手法により、ユーザーが間違った場所で秘密鍵や署名を提供してしまうケースがあります。
利用者は、接続しているdAppやウォレットの正当性を必ず確認し、見知らぬサイトで署名要求が出た場合は慎重に判断する必要があります。

## 引用

360 Core <hello@360coreinc.com>, Robin Rajput (@0xRobinR), "ERC-6932: Subscription-Based Token [DRAFT]," Ethereum Improvement Proposals, no. 6932, April 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6932.

## 最後に

今回は「定期的な支払いが必要なサービスへのアクセスを自動化し、ユーザーと提供者の双方がスムーズに利用継続できる仕組みを提案している**ERC6932**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
