社交恢復錢包部署完整指南:智慧合約開發、Guardian 網路建置與安全最佳實踐
社交恢復錢包是以太坊錢包安全架構的重大創新,透過引入Guardian概念解決私鑰遺失無法恢復的難題。本文提供從智慧合約開發到Guardian網路建置的完整指南,涵蓋合約架構設計、守護者配置、恢復流程實現、安全審計要點、以及運維監控最佳實踐。
社交恢復錢包部署完整指南:智慧合約開發、Guardian 網路建置與安全最佳實踐
執行摘要
社交恢復錢包是以太坊錢包安全架構的重大創新,透過引入「守護者」(Guardian)概念解決了私鑰遺失無法恢復的千古難題。與傳統錢包不同,社交恢復錢包允許用戶透過預先設定的信任網路來恢復帳戶訪問權限,為數位資產保護提供了前所未有的靈活性與安全性。截至 2026 年第一季度,社交恢復錢包已被全球超過 500 萬用戶採用,管理資產規模超過 200 億美元。本文提供從智慧合約開發到 Guardian 網路建置的完整部署指南,涵蓋合約架構設計、守護者配置、恢復流程實現、安全審計要點、以及運維監控最佳實踐。
第一章:社交恢復機制的理論基礎
1.1 傳統錢包的局限性
在深入社交恢復錢包之前,我們需要理解傳統錢包面臨的根本問題:
私鑰的單一失敗點:
傳統以太坊錢包採用外部擁有帳戶(EOA),其安全性完全依賴於私鑰的保密性。一旦私鑰遺失、被盜、或持有者無法訪問,帳戶中的資產將永遠無法恢復。這是區塊鏈「無信任」特性的必然結果——沒有所謂的「忘記密碼」選項。
現有解決方案的不完美:
- 助記詞備份:用戶需要安全存儲助記詞,但物理存儲有遺失、火災、被盜的風險;數位存儲有被黑客攻擊的風險。
- 硬體錢包:提供較好的安全性,但仍存在設備損壞、固件漏洞、甚至供應鏈攻擊的風險。
- 多簽名錢包:要求多個私鑰同時簽名才能執行交易,但每次交易都需要多方參與,體驗繁瑣;且無法解決「單一擁有者」場景。
- 服務商託管:將資產交給中心化服務商管理,但犧牲了去中心化的核心價值。
1.2 社交恢復的創新設計
社交恢復錢包的設計目標是:平時享受與傳統錢包相同的便利性,同時在緊急情況下可以透過信任網路恢復訪問。
核心設計原則:
- 分離平時與緊急場景:平時使用單一私鑰,體驗與 EOA 相同;緊急情況下動用守護者網路。
- 社會化信任層:利用人際關係建立恢復機制,而非純技術手段。
- 延遲與審批:恢復過程需要時間和多方確認,防止盜賊快速控制資產。
- 漸進式安全:可配置的安全參數,適應不同用戶的風險偏好。
與 MPC 錢包的比較:
社交恢復錢包與 MPC 錢包解決的是不同問題:
| 特性 | 社交恢復錢包 | MPC 錢包 |
|---|---|---|
| 日常使用 | 單一私鑰 | 閾值簽名 |
| 恢復機制 | 守護者網路 | 重新生成分片 |
| 隱私 | 較高 | 依賴服務商 |
| 成本 | 較低(智慧合約) | 較高(多方計算) |
| 適用場景 | 個人用戶 | 機構用戶 |
第二章:智慧合約架構設計
2.1 合約整體架構
社交恢復錢包的智慧合約通常由多個模組組成,每個模組負責特定功能:
┌─────────────────────────────────────────────────────────────┐
│ SocialRecoveryWallet │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Owner │ │ Guardian │ │ Recovery │ │
│ │ Module │ │ Module │ │ Module │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ ERC-4337 │ │ Security │ │ Token │ │
│ │ Entrypoint│ │ Module │ │ Manager │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
核心合約組件:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title SocialRecoveryWallet
* @dev 社交恢復錢包核心合約
*/
contract SocialRecoveryWallet {
// ═══════════════════════════════════════════════════════
// 事件定義
// ═══════════════════════════════════════════════════════
event OwnerChanged(address indexed oldOwner, address indexed newOwner);
event GuardianAdded(address indexed guardian);
event GuardianRemoved(address indexed guardian);
event RecoveryInitiated(
address indexed guardian,
address indexed newOwner,
uint256 executeAfter
);
event RecoveryExecuted(address indexed newOwner);
event RecoveryCancelled(address indexed guardian);
event TransactionExecuted(
address indexed to,
uint256 value,
bytes data,
uint256 nonce
);
// ═══════════════════════════════════════════════════════
// 狀態變量
// ═══════════════════════════════════════════════════════
// 當前所有者
address public owner;
// 守護者集合
mapping(address => bool) public guardians;
address[] public guardianList;
uint256 public guardianCount;
// 恢復請求
struct RecoveryRequest {
address newOwner;
uint256 confirmationCount;
uint256 executeAfter;
mapping(address => bool) confirmedBy;
}
mapping(bytes32 => RecoveryRequest) public recoveryRequests;
// 安全參數
uint256 public constant RECOVERY_DELAY = 2 days;
uint256 public constant GUARDIAN_DELAY = 1 days;
uint256 public constant MIN_GUARDIANS = 3;
uint256 public constant RECOVERY_THRESHOLD = 2;
// nonce 用於防重放攻擊
uint256 public nonce;
// ERC-4337 EntryPoint
address public immutable entryPoint;
// ═══════════════════════════════════════════════════════
// 修飾符
// ═══════════════════════════════════════════════════════
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier onlyGuardian() {
require(guardians[msg.sender], "Not guardian");
_;
}
// ═══════════════════════════════════════════════════════
// 建構函數
// ═══════════════════════════════════════════════════════
constructor(
address _owner,
address[] memory _guardians,
address _entryPoint
) {
require(_owner != address(0), "Invalid owner");
require(_guardians.length >= MIN_GUARDIANS, "Too few guardians");
owner = _owner;
entryPoint = _entryPoint;
// 添加初始守護者
for (uint256 i = 0; i < _guardians.length; i++) {
_addGuardian(_guardians[i]);
}
}
// ═══════════════════════════════════════════════════════
// 所有者功能
// ═══════════════════════════════════════════════════════
/**
* @dev 執行交易
*/
function execute(
address to,
uint256 value,
bytes calldata data,
uint256 gasLimit
) external onlyOwner returns (bytes memory) {
require(to != address(0), "Invalid target");
nonce++;
(bool success, bytes memory result) = to.call{value: value, gas: gasLimit}(data);
require(success, "Transaction failed");
emit TransactionExecuted(to, value, data, nonce);
return result;
}
/**
* @dev 批量執行交易
*/
function executeBatch(
address[] calldata tos,
uint256[] calldata values,
bytes[] calldata datas
) external onlyOwner {
require(tos.length == values.length, "Length mismatch");
require(tos.length == datas.length, "Length mismatch");
for (uint256 i = 0; i < tos.length; i++) {
(bool success, ) = tos[i].call{value: values[i]}(datas[i]);
require(success, "Batch transaction failed");
}
}
/**
* @dev 添加守護者(需要延遲生效)
*/
function addGuardian(address guardian) external onlyOwner {
require(guardian != address(0), "Invalid guardian");
require(guardian != owner, "Owner cannot be guardian");
require(!guardians[guardian], "Already guardian");
_addGuardian(guardian);
emit GuardianAdded(guardian);
}
/**
* @dev 移除守護者(立即生效)
*/
function removeGuardian(address guardian) external onlyOwner {
require(guardians[guardian], "Not guardian");
require(guardianCount > MIN_GUARDIANS, "Too few guardians");
guardians[guardian] = false;
guardianCount--;
// 從列表中移除
for (uint256 i = 0; i < guardianList.length; i++) {
if (guardianList[i] == guardian) {
guardianList[i] = guardianList[guardianList.length - 1];
guardianList.pop();
break;
}
}
emit GuardianRemoved(guardian);
}
/**
* @dev 內部添加守護者
*/
function _addGuardian(address guardian) internal {
guardians[guardian] = true;
guardianList.push(guardian);
guardianCount++;
}
// ═══════════════════════════════════════════════════════
// 守護者功能
// ═══════════════════════════════════════════════════════
/**
* @dev 發起恢復請求
*/
function initiateRecovery(address newOwner) external onlyGuardian {
require(newOwner != address(0), "Invalid new owner");
require(newOwner != owner, "Already owner");
bytes32 requestId = keccak256(abi.encodePacked(newOwner, block.timestamp));
RecoveryRequest storage request = recoveryRequests[requestId];
request.newOwner = newOwner;
request.confirmationCount = 1;
request.executeAfter = block.timestamp + RECOVERY_DELAY;
request.confirmedBy[msg.sender] = true;
emit RecoveryInitiated(msg.sender, newOwner, request.executeAfter);
}
/**
* @dev 確認恢復請求
*/
function confirmRecovery(bytes32 requestId) external onlyGuardian {
RecoveryRequest storage request = recoveryRequests[requestId];
require(request.executeAfter > 0, "No pending request");
require(!request.confirmedBy[msg.sender], "Already confirmed");
require(block.timestamp < request.executeAfter, "Request expired");
request.confirmationCount++;
request.confirmedBy[msg.sender] = true;
}
/**
* @dev 執行恢復
*/
function executeRecovery(bytes32 requestId) external {
RecoveryRequest storage request = recoveryRequests[requestId];
require(request.executeAfter > 0, "No pending request");
require(
block.timestamp >= request.executeAfter,
"Recovery not ready"
);
require(
request.confirmationCount >= RECOVERY_THRESHOLD,
"Insufficient confirmations"
);
address oldOwner = owner;
owner = request.newOwner;
// 清除請求
delete recoveryRequests[requestId];
emit RecoveryExecuted(owner);
emit OwnerChanged(oldOwner, owner);
}
/**
* @dev 取消恢復請求
*/
function cancelRecovery(bytes32 requestId) external onlyOwner {
RecoveryRequest storage request = recoveryRequests[requestId];
require(request.executeAfter > 0, "No pending request");
delete recoveryRequests[requestId];
emit RecoveryCancelled(msg.sender);
}
// ═══════════════════════════════════════════════════════
// 查詢功能
// ═══════════════════════════════════════════════════════
/**
* @dev 獲取守護者列表
*/
function getGuardians() external view returns (address[] memory) {
return guardianList;
}
/**
* @dev 檢查地址是否是守護者
*/
function isGuardian(address addr) external view returns (bool) {
return guardians[addr];
}
/**
* @dev 計算恢復請求 ID
*/
function getRecoveryId(address newOwner) external pure returns (bytes32) {
return keccak256(abi.encodePacked(newOwner, block.timestamp));
}
// ═══════════════════════════════════════════════════════
// ERC-4337 支持
// ═══════════════════════════════════════════════════════
/**
* @dev 驗證用戶操作
*/
function validateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingWalletFunds
) external view returns (uint256) {
require(userOp.sender == address(this), "Wrong wallet");
bytes32 hash = keccak256(
abi.encode(
userOpHash,
nonce,
uint256(0),
uint256(0),
block.chainid
)
);
if (owner != ecrecover(hash, userOp.signature[0],
bytes32(userOp.signature[1:33]),
bytes32(userOp.signature[33:65]))) {
return 1;
}
return 0;
}
receive() external payable {}
}
2.2 合約升級機制
智慧合約需要支持升級以修復可能的漏洞和添加新功能:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/proxy/Proxy.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
/**
* @title WalletFactory
* @dev 錢包工廠合約
*/
contract WalletFactory {
address public immutable implementation;
address public owner;
mapping(address => bool) public wallets;
mapping(address => address[]) public userWallets;
event WalletCreated(address indexed wallet, address indexed owner);
constructor(address _implementation) {
implementation = _implementation;
owner = msg.sender;
}
/**
* @dev 創建錢包
*/
function createWallet(
address[] calldata guardians,
bytes calldata initData
) external returns (address) {
ERC1967Proxy proxy = new ERC1967Proxy(
implementation,
initData
);
address wallet = address(proxy);
wallets[wallet] = true;
userWallets[msg.sender].push(wallet);
emit WalletCreated(wallet, msg.sender);
return wallet;
}
/**
* @dev 獲取用戶的所有錢包
*/
function getUserWallets(address user)
external view returns (address[] memory) {
return userWallets[user];
}
}
2.3 安全模組設計
安全模組負責額外的安全檢查:
/**
* @title SecurityModule
* @dev 安全模組合約
*/
contract SecurityModule {
// 每日交易限額
mapping(address => uint256) public dailyLimits;
mapping(address => uint256) public dailySpent;
mapping(address => uint256) public lastResetDay;
// 地址白名單
mapping(address => mapping(address => bool)) public whitelist;
// 緊急暫停
bool public paused;
address public securityAdmin;
modifier whenNotPaused() {
require(!paused, "Paused");
_;
}
modifier onlySecurityAdmin() {
require(msg.sender == securityAdmin, "Not admin");
_;
}
/**
* @dev 檢查交易是否在限額內
*/
function checkDailyLimit(
address wallet,
uint256 amount
) internal returns (bool) {
uint256 today = block.timestamp / 1 days;
if (lastResetDay[wallet] != today) {
dailySpent[wallet] = 0;
lastResetDay[wallet] = today;
}
require(
dailySpent[wallet] + amount <= dailyLimits[wallet],
"Daily limit exceeded"
);
dailySpent[wallet] += amount;
return true;
}
/**
* @dev 檢查目標地址是否在白名單
*/
function checkWhitelist(
address wallet,
address to
) internal view returns (bool) {
if (whitelist[wallet][to]) {
return true;
}
// 允許與已知 DeFi 合約交互
return isKnownContract(to);
}
/**
* @dev 緊急暫停
*/
function pause() external onlySecurityAdmin {
paused = true;
}
/**
* @dev 恢復正常
*/
function unpause() external onlySecurityAdmin {
paused = false;
}
}
第三章:Guardian 網路建置
3.1 Guardian 選擇策略
Guardian 的選擇直接影響錢包的安全性:
數量建議:
- 最少:3 個守護者(最小安全閾值)
- 推薦:5 個守護者(較好平衡)
- 高安全:7 個或更多
守護者類型:
- 個人 Guardian:家人、朋友、自己控制的其他錢包
- 專業服務:法律機構、審計公司、專業託管服務
- 硬體設備:自己控制的硬體錢包
選擇原則:
- 多元化:Guardian 之間不應有共同的攻擊面
- 可達性:能夠及時響應恢復請求
- 誠信:值得信任不會串通盜取資產
3.2 Guardian 客戶端配置
Guardian 需要運行專門的客戶端軟體來參與恢復流程:
# guardian_client/client.py
import asyncio
import logging
from typing import Optional, Dict, Any
from dataclasses import dataclass
from enum import Enum
import web3
from eth_account import Account
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class GuardianState(Enum):
IDLE = "idle"
MONITORING = "monitoring"
RECOVERY_REQUESTED = "recovery_requested"
RECOVERY_CONFIRMED = "recovery_confirmed"
@dataclass
class GuardianConfig:
private_key: str
wallet_address: str
rpc_url: str
wallet_contract: str
notification_endpoint: Optional[str] = None
auto_confirm: bool = False
class GuardianClient:
def __init__(self, config: GuardianConfig):
self.config = config
self.w3 = web3.Web3(web3.HTTPProvider(config.rpc_url))
self.account = Account.from_key(config.private_key)
self.state = GuardianState.IDLE
# 錢包合約ABI
self.wallet_abi = [...]
async def start_monitoring(self):
"""開始監控錢包狀態"""
logger.info(f"Guardian {self.account.address} starting monitoring")
self.state = GuardianState.MONITORING
# 監控區塊新區塊
while True:
try:
await self._check_recovery_requests()
await asyncio.sleep(60) # 每分鐘檢查一次
except Exception as e:
logger.error(f"Monitoring error: {e}")
await asyncio.sleep(5)
async def _check_recovery_requests(self):
"""檢查是否有待處理的恢復請求"""
# 查詢合約事件
contract = self.w3.eth.contract(
address=self.config.wallet_contract,
abi=self.wallet_abi
)
# 獲取最新事件
current_block = self.w3.eth.block_number
# 過濾 RecoveryInitiated 事件
events = contract.events.RecoveryInitiated.getLogs(
fromBlock=current_block - 1000,
argument_filters={'guardian': self.account.address}
)
for event in events:
await self._handle_recovery_event(event)
async def _handle_recovery_event(self, event):
"""處理恢復事件"""
request_id = event['args']['requestId']
new_owner = event['args']['newOwner']
execute_after = event['args']['executeAfter']
logger.info(
f"Recovery request detected: "
f"newOwner={new_owner}, executeAfter={execute_after}"
)
self.state = GuardianState.RECOVERY_REQUESTED
# 發送通知
if self.config.notification_endpoint:
await self._send_notification(new_owner, execute_after)
# 自動確認或等待手動確認
if self.config.auto_confirm:
await self._confirm_recovery(request_id)
else:
logger.info("Waiting for manual confirmation")
await self._wait_for_manual_confirmation(request_id)
async def _confirm_recovery(self, request_id: str):
"""確認恢復請求"""
logger.info(f"Confirming recovery request: {request_id}")
contract = self.w3.eth.contract(
address=self.config.wallet_contract,
abi=self.wallet_abi
)
# 構建交易
tx = contract.functions.confirmRecovery(request_id).buildTransaction({
'from': self.account.address,
'nonce': self.w3.eth.get_transaction_count(self.account.address),
'gas': 100000,
'gasPrice': self.w3.eth.gas_price
})
# 簽名並發送
signed_tx = self.account.sign_transaction(tx)
tx_hash = self.w3.eth.send_raw_transaction(signed_tx.rawTransaction)
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
logger.info(f"Recovery confirmed: {receipt['transactionHash'].hex()}")
self.state = GuardianState.RECOVERY_CONFIRMED
async def _wait_for_manual_confirmation(self, request_id: str):
"""等待手動確認"""
# 這裡可以實現用戶界面交互
# 簡化版本:等待輸入
logger.info("Press 'y' to confirm recovery...")
# 實際實現中需要用戶界面
user_input = input()
if user_input.lower() == 'y':
await self._confirm_recovery(request_id)
else:
logger.info("Recovery not confirmed")
self.state = GuardianState.MONITORING
3.3 分布式 Guardian 協調
多個 Guardian 需要協調工作:
# guardian_client/coordinator.py
import asyncio
import logging
from typing import List, Dict
from dataclasses import dataclass
@dataclass
class RecoverySession:
request_id: str
new_owner: str
confirmed_guardians: List[str]
threshold: int
status: str
class GuardianCoordinator:
"""多個 Guardian 之間的協調器"""
def __init__(self, guardians: List[str], threshold: int):
self.guardians = guardians
self.threshold = threshold
self.sessions: Dict[str, RecoverySession] = {}
async def start_recovery(
self,
request_id: str,
new_owner: str
) -> RecoverySession:
"""開始新的恢復會話"""
session = RecoverySession(
request_id=request_id,
new_owner=new_owner,
confirmed_guardians=[],
threshold=self.threshold,
status="pending"
)
self.sessions[request_id] = session
return session
async def add_confirmation(
self,
request_id: str,
guardian: str
) -> bool:
"""添加確認"""
if request_id not in self.sessions:
return False
session = self.sessions[request_id]
if guardian in session.confirmed_guardians:
return False
session.confirmed_guardians.append(guardian)
# 檢查是否達到閾值
if len(session.confirmed_guardians) >= session.threshold:
session.status = "ready"
return True
return False
async def broadcast_request(
self,
request_id: str,
new_owner: str
):
"""廣播恢復請求給所有 Guardian"""
# 實際實現中通過消息隊列或 P2P 網路廣播
for guardian in self.guardians:
await self._send_to_guardian(guardian, {
'type': 'recovery_request',
'request_id': request_id,
'new_owner': new_owner
})
第四章:前端整合與用戶體驗
4.1 錢包連接與認證
錢包需要與 DApp 整合:
// wallet-client/index.ts
import { ethers } from 'ethers';
import { ERC4337EthersProvider } from '@account-abstraction/sdk';
export class SocialRecoveryWallet {
private provider: ethers.providers.JsonRpcProvider;
private wallet: ethers.Contract;
private entryPoint: string;
constructor(
walletAddress: string,
rpcUrl: string,
entryPoint: string = '0x5FF137D4b0FD9CDe4383A0a2d4E4D0f5D5a8f5F3'
) {
this.provider = new ethers.providers.JsonRpcProvider(rpcUrl);
this.entryPoint = entryPoint;
this.wallet = new ethers.Contract(
walletAddress,
WALLET_ABI,
this.provider
);
}
/**
* 連接錢包(使用瀏�器錢包)
*/
async connect(signer: ethers.Signer): Promise<string> {
const address = await signer.getAddress();
return address;
}
/**
* 發送交易
*/
async sendTransaction(
to: string,
value: ethers.BigNumber,
data: string,
signer: ethers.Signer
): Promise<ethers.TransactionReceipt> {
const walletAddress = this.wallet.address;
// 構建交易
const tx = {
to,
value,
data
};
// 通過錢包合約執行
const txResponse = await this.wallet
.connect(signer)
.execute(
tx.to,
tx.value,
tx.data,
50000 // gas limit
);
return txResponse.wait();
}
/**
* 發起恢復(需要錢包所有權)
*/
async initiateRecovery(
newOwner: string,
signer: ethers.Signer
): Promise<string> {
const tx = await this.wallet
.connect(signer)
.initiateRecovery(newOwner);
const receipt = await tx.wait();
return receipt.transactionHash;
}
/**
* 添加守護者
*/
async addGuardian(
guardian: string,
signer: ethers.Signer
): Promise<ethers.TransactionReceipt> {
const tx = await this.wallet
.connect(signer)
.addGuardian(guardian);
return tx.wait();
}
/**
* 獲取守護者列表
*/
async getGuardians(): Promise<string[]> {
return this.wallet.getGuardians();
}
/**
* 獲取錢包餘額
*/
async getBalance(): Promise<ethers.BigNumber> {
return this.provider.getBalance(this.wallet.address);
}
}
4.2 恢復流程用戶界面
恢復流程的 UI 設計至關重要:
// wallet-ui/RecoveryFlow.tsx
import React, { useState, useEffect } from 'react';
import { useWallet } from './hooks/useWallet';
import { GuardianClient } from './guardian-client';
export const RecoveryFlow: React.FC = () => {
const { wallet } = useWallet();
const [recoveryState, setRecoveryState] = useState<string>('idle');
const [newOwner, setNewOwner] = useState<string>('');
const [guardians, setGuardians] = useState<string[]>([]);
const [confirmations, setConfirmations] = useState<number>(0);
const [timeRemaining, setTimeRemaining] = useState<number>(0);
useEffect(() => {
loadRecoveryState();
}, []);
const loadRecoveryState = async () => {
const guardianList = await wallet.getGuardians();
setGuardians(guardianList);
// 檢查是否有待處理的恢復
// ...
};
const initiateRecovery = async () => {
if (!ethers.utils.isAddress(newOwner)) {
alert('Invalid address');
return;
}
setRecoveryState('initiating');
try {
await wallet.initiateRecovery(newOwner, signer);
setRecoveryState('initiated');
// 啟動計時器
startCountdown();
} catch (error) {
console.error('Recovery initiation failed:', error);
setRecoveryState('idle');
}
};
const confirmRecovery = async () => {
setRecoveryState('confirming');
try {
// 這裡調用 Guardian 確認
await GuardianClient.confirm(requestId);
setConfirmations(prev => prev + 1);
} catch (error) {
console.error('Confirmation failed:', error);
}
};
return (
<div className="recovery-flow">
{recoveryState === 'idle' && (
<div className="initiate-section">
<h2>Initiate Account Recovery</h2>
<p>Enter the address that will become the new owner:</p>
<input
type="text"
value={newOwner}
onChange={(e) => setNewOwner(e.target.value)}
placeholder="0x..."
/>
<button onClick={initiateRecovery}>
Start Recovery
</button>
</div>
)}
{recoveryState === 'initiated' && (
<div className="confirmation-section">
<h2>Waiting for Guardian Confirmations</h2>
<div className="progress">
{confirmations} / {guardians.length} confirmations
</div>
<div className="countdown">
Time remaining: {formatTime(timeRemaining)}
</div>
<div className="guardians-status">
{guardians.map((guardian, index) => (
<GuardianStatus
key={index}
address={guardian}
confirmed={index < confirmations}
/>
))}
</div>
</div>
)}
{recoveryState === 'ready' && (
<div className="execute-section">
<h2>Recovery Ready to Execute</h2>
<p>The recovery will change ownership to:</p>
<code>{newOwner}</code>
<button onClick={executeRecovery}>
Execute Recovery
</button>
</div>
)}
</div>
);
};
第五章:安全審計與測試
5.1 合約審計清單
部署前必須完成以下審計項目:
訪問控制審計:
- [ ] owner 變更需要正確的權限檢查
- [ ] Guardian 功能僅對Guardian開放
- [ ] 沒有未受保護的敏感函數
邏輯漏洞檢查:
- [ ] 恢復延遲機制正確實現
- [ ] 閾值計算正確
- [ ] 防重放攻擊機制有效
邊界條件測試:
- [ ] 臨界 Guardian 數量(MIN_GUARDIANS)
- [ ] 閾值邊界(RECOVERY_THRESHOLD)
- [ ] 時間邊界(RECOVERY_DELAY)
5.2 自動化測試套件
// test/SocialRecoveryWallet.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("SocialRecoveryWallet", function() {
let wallet;
let owner;
let guardian1, guardian2, guardian3;
let addrs;
beforeEach(async function() {
[owner, guardian1, guardian2, guardian3, ...addrs] = await ethers.getSigners();
const Wallet = await ethers.getContractFactory("SocialRecoveryWallet");
wallet = await Wallet.deploy(
owner.address,
[guardian1.address, guardian2.address, guardian3.address],
"0x5FF137D4b0FD9CDe4383A0a2d4E4D0f5D5a8f5F3"
);
});
describe("Ownership", function() {
it("should set correct owner", async function() {
expect(await wallet.owner()).to.equal(owner.address);
});
it("should allow owner to execute transactions", async function() {
const [_, addr1] = await ethers.getSigners();
await owner.sendTransaction({
to: wallet.address,
value: ethers.utils.parseEther("1")
});
const balance = await wallet.provider.getBalance(wallet.address);
expect(balance).to.equal(ethers.utils.parseEther("1"));
});
});
describe("Guardians", function() {
it("should set initial guardians", async function() {
expect(await wallet.guardians(guardian1.address)).to.be.true;
expect(await wallet.guardians(guardian2.address)).to.be.true;
expect(await wallet.guardians(guardian3.address)).to.be.true;
expect(await wallet.guardianCount()).to.equal(3);
});
it("should allow adding new guardian", async function() {
const newGuardian = addrs[0];
await wallet.addGuardian(newGuardian.address);
expect(await wallet.guardians(newGuardian.address)).to.be.true;
expect(await wallet.guardianCount()).to.equal(4);
});
it("should prevent owner from being guardian", async function() {
await expect(
wallet.addGuardian(owner.address)
).to.be.revertedWith("Owner cannot be guardian");
});
});
describe("Recovery", function() {
it("should allow guardian to initiate recovery", async function() {
const newOwner = addrs[1].address;
await wallet.connect(guardian1).initiateRecovery(newOwner);
// 檢查事件
// ...
});
it("should require threshold confirmations to execute", async function() {
const newOwner = addrs[1].address;
// 只有一個 Guardian 確認
await wallet.connect(guardian1).initiateRecovery(newOwner);
await wallet.connect(guardian1).confirmRecovery(
ethers.utils.keccak256(ethers.utils.toUtf8Bytes(newOwner + Date.now().toString()))
);
// 嘗試執行應該失敗
await expect(
wallet.executeRecovery(
ethers.utils.keccak256(ethers.utils.toUtf8Bytes(newOwner + Date.now().toString()))
)
).to.be.revertedWith("Insufficient confirmations");
});
});
});
5.3 滲透測試要點
智能合約滲透測試:
- 重入攻擊:檢查所有外部調用
- 整數溢位:使用 SafeMath 或 Solidity 0.8+
- 訪問控制:繞過所有權限檢查
- DoS 攻擊:嘗試使合約無法使用
前端滲透測試:
- XSS 攻擊:注入惡意腳本
- CSRF 攻擊:偽造用戶操作
- 中間人攻擊:攔截通信
結論
社交恢復錢包代表了以太坊錢包安全的重大進步,通過引入守護者網路實現了安全性與便利性的平衡。本文提供了從智慧合約開發到 Guardian 網路建置的完整指南。
正確部署社交恢復錢包需要:
- 安全的合約架構設計
- 合理的守護者配置
- 完善的監控與警報系統
- 全面的審計與測試
遵循這些最佳實踐,用戶可以獲得比傳統錢包更高的安全性,同時保持良好的使用體驗。
相關文章
- 社交恢復錢包技術實作完整指南:智慧合約錢包架構、守護者機制與安全設計深度分析 — 社交恢復錢包解決了傳統加密貨幣錢包的核心痛點:私鑰遺失導致資產永久無法訪問的問題。本文深入分析社交恢復錢包的技術架構,包括智慧合約實現、守護者機制設計、恢復流程、安全考量等各個層面,提供完整的程式碼範例和安全分析。
- MPC 錢包完整技術指南:多方計算錢包架構、安全模型與實作深度分析 — 多方計算(Multi-Party Computation)錢包代表了區塊鏈資產安全管理的前沿技術方向。本文深入剖析 MPC 錢包的密碼學原理、主流實現方案、安全架構,涵蓋 Shamir 秘密分享、BLS 閾值簽名、分散式金鑰生成等核心技術,並提供完整的部署指南與最佳實踐建議。
- 以太坊錢包安全實務進階指南:合約錢包與 EOA 安全差異、跨鏈橋接風險評估 — 本文深入探討以太坊錢包的安全性實務,特別聚焦於合約錢包與外部擁有帳戶(EOA)的安全差異分析,以及跨鏈橋接的風險評估方法。我們將從密碼學基礎出發,詳細比較兩種帳戶類型的安全模型,並提供完整的程式碼範例展示如何實現安全的多重簽名錢包。同時,本文系統性地分析跨鏈橋接面臨的各類風險,提供風險評估框架和最佳實踐建議,幫助讀者建立全面的錢包安全知識體系。
- 以太坊錢包安全模型深度比較:EOA、智慧合約錢包與 MPC 錢包的技術架構、風險分析與選擇框架 — 本文深入分析以太坊錢包技術的三大類型:外部擁有帳戶(EOA)、智慧合約錢包(Smart Contract Wallet)與多方計算錢包(MPC Wallet)。我們從技術原理、安全模型、風險維度等面向進行全面比較,涵蓋 ERC-4337 帳戶抽象標準、Shamir 秘密分享方案、閾值簽名等核心技術,並提供針對不同資產規模和使用場景的選擇框架。截至 2026 年第一季度,以太坊生態系統的錢包技術持續演進,理解這些技術差異對於保護數位資產至關重要。
- 以太坊生態應用案例實作完整指南:DeFi、質押、借貸與錢包交互 — 本文提供以太坊生態系統中最常見應用場景的完整實作範例,涵蓋去中心化金融操作、質押服務、智慧合約部署、錢包管理和跨鏈交互等多個維度。所有範例均基於 2026 年第一季度最新的協議版本,並包含可直接運行的程式碼和詳細的操作流程說明。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!