Skip to content
On this page

L1-7: Dead Man's Switch(デッドマンズスイッチ)

1. 問題

ユーザーがETHを預け入れ、定期的なチェックイン機構を設定するコントラクトを作成します。設定された時間間隔内にユーザーがチェックインしなかった場合、指定された受益者がその資金を引き出すことができます。ユーザー自身は常に引き出し可能です。

2. 理由

現実のシナリオ:暗号資産保有者は事故(死亡、失踪、秘密鍵の紛失)により資産にアクセスできなくなる可能性があります。デッドマンズスイッチは「デジタル遺産」の仕組みを提供します:

  • 運営者は定期的にチェックインして「まだ活動中」であることを証明する
  • 連絡不能になった場合、資金は事前に指定された受益者に自動的に移転する
  • 第三者カストディアンを信頼する必要がない

3. 解決策

コアメカニズム

  • 各ユーザーが自身の checkInInterval を設定する
  • deposit() / checkIn()lastCheckIn[user] を更新する
  • アカウント保有者は常に引き出し可能
  • 受益者は block.timestamp > lastCheckIn[account] + checkInInterval[account] の場合のみ引き出し可能

データ構造

solidity
mapping(address => uint256) public balanceOf;
mapping(address => uint256) public lastCheckIn;
mapping(address => uint256) public checkInInterval;
mapping(address => address[]) public beneficiaries;

4. 遭遇した落とし穴

4.1 受益者はインターバルを設定する必要がない

ユーザーが checkInInterval を設定しない場合、deadline = lastCheckIn + 0 = lastCheckIn となり、block.timestamp > deadline が常に true になります。つまり受益者は即座に引き出し可能です。

4.2 入金時の自動チェックイン

deposit() は同時に lastCheckIn も更新すべきです。さもなければ、新規入金後に即座に「連絡不能」と見なされる可能性があります。

4.3 受益者リストの管理

removeBeneficiary は配列から要素を削除する必要があります。swap-and-pop パターンを使用して O(1) で実現します。

5. 落とし穴の理由

5.1

checkInInterval のデフォルトは 0 です。lastCheckIn + 0 = lastCheckIn となるため、任意の block.timestamp > lastCheckIn が条件を満たしてしまいます。interval == 0 の明示的なチェックが必要です。

5.2

入金時に lastCheckIn を更新しないと、多額のETHを預け入れたばかりのユーザーの資金が受益者に引き出される可能性があります(以前のインターバルが既に経過しているため)。

5.3

Solidityの配列には組み込みの削除メソッドがありません。list[i] = list[last]; list.pop() を使用するのが最適解です。

6. 落とし穴の解決方法

solidity
function withdraw(address account, uint256 amount) external {
    if (!isAccountHolder) {
        uint256 interval = checkInInterval[account];
        if (interval == 0) revert NotExpired(); // 明示的に設定する必要あり
        if (block.timestamp <= lastCheckIn[account] + interval) revert NotExpired();
    }
    // ...
}

7. 技術的ポイント

ポイント説明
時間条件付きアクセス制御block.timestamp の比較
受益者パターン複数の受益者が引き出し権限を共有
チェックイン機構対話ごとに自動更新
配列のスワップ&ポップO(1) 削除
receive() 入金直接ETH転送を自動処理

Built with AiAda