Appearance
L1-4: Custom Stream(高度なストリーミング支払い)
1. 課題
ETH Streamingをベースに、より高度なストリーミング支払いシステムを構築します。複数受信者、マルチストリーム管理、ストリームキャンセルなどの機能をサポートします。
2. 背景
基本的なETH Streamingは単一のETHストリームのみをサポートします。実際のアプリケーションでは以下が必要です:
- 複数の並行ストリームの管理
- ERC20トークンストリームのサポート(ETHだけでなく)
- ストリームのライフサイクル管理(作成、キャンセル、資金回収)
3. 解決策
アーキテクチャ設計
solidity
struct Stream {
address token; // ERC20トークンアドレス(address(0) = ETH)
uint256 cap; // 1サイクルあたりの最大引き出し可能量
uint256 unlockTime; // フルアンロックサイクルの期間
uint256 timeOfLastWithdrawal;
uint256 remainingBalance; // このストリームの残り総予算
}
マルチストリーム管理
streamIdで各ストリームを識別streamId → recipient → Streamの二層マッピング- 各ストリームは独立して予算とアンロック状態を追跡
ストリームライフサイクル
createStream(token, totalBudget)— 作成と資金提供addRecipient(streamId, recipient, cap, unlockTime)— 受信者を追加withdraw(streamId, amount)— 受信者が引き出しcancelStream(streamId)— オーナーがストリームをキャンセル
4. 遭遇した落とし穴
4.1 remainingBalanceとcapの混同
capはアンロックサイクルごとの最大引き出し可能量(時間経過で回復)であり、remainingBalanceはその受信者の総予算上限(回復しません)です。
4.2 キャンセル後も引き出しがブロックされない
ストリームキャンセル後にactiveStreamsフラグをクリアしないと、受信者が引き続き引き出しできる可能性があります。
5. 落とし穴の原因
5.1
capは時間ベースのアンロックモデル(L1-3と同様)に従って動作しますが、remainingBalanceは固定の予算上限です。この2つは別々に処理する必要があり、withdrawでは最初にunlocked(時間次元)をチェックし、次にremainingBalance(予算次元)をチェックします。
5.2
ストリームキャンセルが内部状態を変更した後、withdrawの入口でactiveStreams[streamId]をチェックする必要があります。
6. 落とし穴の解決方法
solidity
if (amount > unlocked) revert InsufficientUnlockedBalance();
if (amount > stream.remainingBalance) revert InsufficientStreamBalance();
7. 技術的ポイント
| ポイント | 説明 |
|---|---|
| マルチストリームアーキテクチャ | streamId + 二層マッピング |
| 予算 vs レート | remainingBalance(総量)vs cap(レート) |
| ストリームライフサイクル | create → fund → addRecipient → withdraw → cancel |
| 拡張性 | 各ストリームに独立したunlockTimeを設定可能 |