什么是区块链?它有哪些核心特征?
What is blockchain? What are its core characteristics?
*考察点:区块链基本概念理解。*
共 150 道题目
What is blockchain? What are its core characteristics?
What is blockchain? What are its core characteristics?
考察点:区块链基本概念理解。
答案:
区块链是一种分布式账本技术,通过密码学方法将数据区块按时间顺序链接成不可篡改的链式结构。它本质上是一个去中心化的数据库,所有参与节点共同维护一个统一的账本,无需依赖中央权威机构。
区块链的设计理念是通过技术手段解决信任问题,让原本需要依赖第三方机构的交易和数据存储,可以在没有中介的情况下安全可靠地进行。
核心特征:
实际应用:
What main information does a "block" in blockchain contain?
What main information does a “block” in blockchain contain?
考察点:区块结构基础知识。
答案:
区块是区块链的基本存储单元,每个区块包含一批交易数据和元数据。区块通过哈希值相互链接,形成不可断裂的链式结构。
每个区块的结构设计确保了数据的完整性和可追溯性,同时为网络共识提供了必要的技术基础。
主要信息组成:
区块头(Block Header):
交易数据(Transaction Data):
技术细节:
What is a hash function? What role does it play in blockchain?
What is a hash function? What role does it play in blockchain?
考察点:哈希函数在区块链中的应用。
答案:
哈希函数是一种将任意长度的数据映射为固定长度字符串的数学函数。输出的哈希值具有唯一性,即使输入数据只有微小变化,输出的哈希值也会完全不同。
在区块链中,哈希函数是确保数据完整性和安全性的核心技术,为整个系统的信任机制提供了密码学基础。
主要特性:
在区块链中的作用:
常用算法:
What are the roles of public and private keys? How do they ensure security?
What are the roles of public and private keys? How do they ensure security?
考察点:密码学基础概念。
答案:
公钥和私钥是非对称密码学的核心概念,它们成对生成且在数学上相关联。私钥由用户秘密保管,公钥可以公开分享,这种机制解决了传统对称加密中密钥分发的难题。
在区块链系统中,公私钥对为去中心化环境下的身份认证和交易安全提供了技术保障,是实现"信任"的基础工具。
主要作用:
私钥(Private Key):
公钥(Public Key):
安全保障机制:
实际应用:
What are the main differences between Bitcoin and Ethereum?
What are the main differences between Bitcoin and Ethereum?
考察点:主流区块链平台对比。
答案:
比特币和以太坊是两个最具影响力的区块链项目,但它们的设计理念和应用目标存在根本差异。比特币专注于数字货币功能,而以太坊则构建了一个通用的去中心化计算平台。
这两个平台在技术架构、共识机制、应用场景等方面都有显著差异,代表了区块链技术发展的不同方向。
主要区别对比:
设计目标:
技术特性:
共识机制:
区块时间:
应用场景:
What is a digital signature? How does it verify the authenticity of transactions?
What is a digital signature? How does it verify the authenticity of transactions?
考察点:数字签名机制理解。
答案:
数字签名是使用私钥对数据进行加密运算产生的唯一标识,相当于现实世界中的手写签名。它基于非对称密码学原理,能够证明数据的来源和完整性。
在区块链交易中,数字签名确保只有私钥持有者才能发起有效交易,同时让网络中的任何人都能验证交易的真实性,这是去中心化信任的技术基础。
工作原理:
签名生成:
签名验证:
验证交易真实性的机制:
技术优势:
What are "nodes" in a blockchain network? What types are there?
What are “nodes” in a blockchain network? What types are there?
考察点:网络节点基础概念。
答案:
节点是区块链网络中的参与计算机,每个节点都运行区块链客户端软件,参与网络的维护和运行。节点之间通过P2P网络协议进行通信,共同维护区块链账本的一致性。
不同类型的节点承担着不同的功能角色,这种分工协作的设计既保证了网络的去中心化特性,又提高了系统的整体效率。
主要节点类型:
全节点(Full Node):
轻节点(Light Node/SPV):
矿工节点(Miner Node):
档案节点(Archive Node):
节点作用:
What is decentralization? What are its advantages and disadvantages compared to traditional centralized systems?
What is decentralization? What are its advantages and disadvantages compared to traditional centralized systems?
考察点:去中心化核心理念。
答案:
去中心化是指系统的控制权和决策权分散到网络中的多个节点,而不是集中在单一的中央机构。这种架构消除了单点故障和权力集中的风险,让系统更加民主和透明。
去中心化代表了一种新的组织和治理模式,它通过技术手段重新分配权力,让参与者能够在无需信任中央权威的情况下进行协作。
去中心化的优势:
去中心化的劣势:
中心化系统对比:
应用场景选择:
What is a Gas fee? Why do we need to pay Gas fees?
What is a Gas fee? Why do we need to pay Gas fees?
考察点:以太坊Gas机制基础。
答案:
Gas费是在以太坊等区块链网络上执行交易和智能合约时需要支付的手续费。Gas衡量的是执行操作所需的计算资源量,而Gas费就是用ETH支付的这些计算成本。
Gas机制设计巧妙地解决了去中心化网络中的资源分配问题,通过经济激励确保网络的正常运行和安全性。
Gas机制组成:
Gas: 衡量计算复杂度的单位
Gas Price: 用户愿意为每单位Gas支付的ETH数量
Gas Limit: 用户设置的最大Gas消耗量
支付Gas费的原因:
实际影响:
How is the immutability of blockchain achieved?
How is the immutability of blockchain achieved?
考察点:区块链安全机制理解。
答案:
区块链的不可篡改性是通过多重密码学技术和经济激励机制共同实现的。这种设计让历史数据几乎无法被恶意修改,为去中心化系统提供了坚实的信任基础。
不可篡改性不是绝对的物理限制,而是通过将篡改成本提高到经济上不可行的程度来实现的实用安全性。
技术实现机制:
哈希链接:
Merkle树结构:
数字签名验证:
分布式共识:
经济保障机制:
实际防护效果:
What is a consensus mechanism? What are the common consensus mechanisms?
What is a consensus mechanism? What are the common consensus mechanisms?
考察点:共识机制基础概念。
答案:
共识机制是区块链网络中所有节点就数据状态达成一致的算法和规则。它解决了去中心化环境下的"拜占庭将军问题",确保即使有恶意节点存在,诚实节点也能就网络状态达成统一认识。
共识机制是区块链技术的核心创新,它让互不信任的参与者能够在没有中央权威的情况下维护一个共同的账本。
设计目标:
常见共识机制:
工作量证明(PoW):
权益证明(PoS):
委托权益证明(DPoS):
权威证明(PoA):
选择考虑因素:
What is the basic principle of PoW (Proof of Work)?
What is the basic principle of PoW (Proof of Work)?
考察点:PoW共识机制理解。
答案:
工作量证明(PoW)是通过计算工作来证明参与者对网络贡献的共识机制。矿工需要消耗大量计算资源来解决密码学难题,第一个找到解的矿工获得区块记账权和奖励。
PoW的核心思想是"一CPU一票",通过计算工作而非代币持有量来分配话语权,这种设计有效防止了女巫攻击和双花问题。
工作原理:
难题设置:
竞争挖矿:
激励机制:
安全保障:
技术特点:
实际应用:
What are the differences between PoS (Proof of Stake) and PoW?
What are the differences between PoS (Proof of Stake) and PoW?
考察点:主流共识机制对比。
答案:
权益证明(PoS)和工作量证明(PoW)代表了两种不同的共识机制设计理念。PoW通过计算工作分配权力,而PoS通过代币持有量和质押来分配权力,各有优劣。
这两种机制在安全性、效率、去中心化程度等方面存在重要差异,适合不同的应用场景和发展阶段。
核心机制对比:
| 对比维度 | PoW | PoS |
|---|---|---|
| 权力分配 | 算力大小 | 代币质押量 |
| 资源消耗 | 电力+硬件 | 代币锁定 |
| 安全基础 | 计算成本 | 经济成本 |
| 准入门槛 | 硬件投资 | 代币持有 |
主要区别:
能耗对比:
安全机制:
验证速度:
去中心化:
各自优势:
实际应用:
What is a blockchain fork? What are the differences between hard fork and soft fork?
What is a blockchain fork? What are the differences between hard fork and soft fork?
考察点:分叉概念与类型。
答案:
区块链分叉是指区块链协议规则的改变,导致网络中出现两条或多条不同的链。分叉可能是临时的(由网络延迟等原因造成)或永久的(由协议升级引起)。
分叉是区块链治理和升级的重要机制,它允许网络在保持去中心化的同时进行技术改进和功能扩展。
分叉类型详解:
1. 硬分叉(Hard Fork):
2. 软分叉(Soft Fork):
主要区别对比:
| 特征 | 硬分叉 | 软分叉 |
|---|---|---|
| 兼容性 | 不向后兼容 | 向后兼容 |
| 升级要求 | 全部节点必须升级 | 只需多数算力升级 |
| 风险 | 可能分裂网络 | 风险较小 |
| 灵活性 | 可以任意修改规则 | 只能收紧现有规则 |
著名分叉案例:
分叉影响:
What does the number of confirmations in a blockchain network mean? Why are multiple confirmations needed?
What does the number of confirmations in a blockchain network mean? Why are multiple confirmations needed?
考察点:交易确认机制。
答案:
确认数是指包含某笔交易的区块之后又新增的区块数量。例如,一笔交易在第100个区块被打包,当前区块高度是第106块,则该交易有6个确认。
确认数机制是区块链网络防范双花攻击和确保交易不可逆转的重要安全措施,它通过时间和工作量的积累来提高交易的安全性。
确认机制原理:
为什么需要多个确认:
防止区块重组:
抵御51%攻击:
网络同步延迟:
确认数建议:
影响因素:
What are the main improvements in Ethereum 2.0? Why are these upgrades necessary?
What are the main improvements in Ethereum 2.0? Why are these upgrades necessary?
考察点:以太坊技术演进理解。
答案:
以太坊2.0(现在称为共识层升级)是对以太坊网络的重大技术升级,旨在解决可扩展性、安全性和可持续性问题。这次升级代表了区块链技术从第一代向第二代的重要进化。
升级的根本动机是让以太坊能够支持全球规模的去中心化应用生态系统,同时保持去中心化和安全性。
主要技术改进:
共识机制转换:
分片技术(计划中):
信标链架构:
执行环境优化:
升级必要性:
实际影响:
What are Layer 2 solutions? What are the common Layer 2 technologies?
What are Layer 2 solutions? What are the common Layer 2 technologies?
考察点:区块链扩容技术。
答案:
Layer 2是构建在区块链主网(Layer 1)之上的扩容解决方案,通过在链下处理交易来提高吞吐量和降低成本,同时继承主网的安全性。这种分层架构是解决区块链不可能三角(去中心化、安全性、可扩展性)的重要途径。
Layer 2的核心思想是将计算工作转移到链下进行,只在主网上记录最终结果,从而实现性能的大幅提升。
主要Layer 2技术类型:
Rollup技术:
Optimistic Rollup: 乐观执行,发生争议时才验证
ZK Rollup: 零知识证明验证交易正确性
状态通道:
侧链:
Plasma:
技术对比:
实际应用:
What is the working principle of State Channels?
What is the working principle of State Channels?
考察点:Layer 2扩容方案理解。
答案:
状态通道是一种链下扩容技术,允许参与方在链下进行无限次交易,只在开启和关闭通道时与主网交互。这种设计实现了即时交易确认和零Gas费,特别适合高频小额交易场景。
状态通道的核心理念是"先锁定资金,链下交易,最后结算",通过密码学保证和经济激励确保交易的安全性。
工作原理步骤:
通道建立:
链下交易:
争议解决:
通道关闭:
技术特点:
限制条件:
实际应用:
What is a sidechain? How does it interact with the main chain?
What is a sidechain? How does it interact with the main chain?
考察点:侧链技术原理。
答案:
侧链是独立运行的区块链,通过双向桥接机制与主链连接,允许资产在两条链之间安全转移。侧链有自己的共识机制、验证者和治理规则,为主链提供了扩容和功能扩展的可能性。
侧链设计的核心目标是在保持与主链连接的同时,提供更高的性能、更低的成本或特殊的功能特性。
侧链核心特征:
与主链交互机制:
资产转入(Peg-in):
资产转出(Peg-out):
双向验证:
技术实现方式:
优势与挑战:
实际案例:
How does Rollup technology improve blockchain performance? What are the differences between Optimistic Rollup and ZK Rollup?
How does Rollup technology improve blockchain performance? What are the differences between Optimistic Rollup and ZK Rollup?
考察点:Rollup扩容技术对比。
答案:
Rollup技术通过将交易执行移到链下,只在主链上发布交易数据和状态根,实现了显著的性能提升。这种设计既保持了主链的安全性,又大幅提高了吞吐量和降低了成本。
Rollup被认为是最有前景的以太坊扩容方案,因为它在安全性和效率之间找到了最佳平衡点。
性能提升原理:
两种Rollup技术对比:
Optimistic Rollup:
ZK Rollup:
详细对比表:
| 特征 | Optimistic Rollup | ZK Rollup |
|---|---|---|
| 安全假设 | 至少一个诚实验证者 | 纯密码学保证 |
| 提现时间 | 7天争议期 | 几分钟 |
| 计算开销 | 低 | 高(生成证明) |
| EVM兼容性 | 完全兼容 | 需要定制虚拟机 |
| 数据可用性 | 需要 | 需要 |
实际项目案例:
发展趋势:
What is cross-chain technology? What are the common cross-chain solutions?
What is cross-chain technology? What are the common cross-chain solutions?
考察点:跨链技术理解。
答案:
跨链技术是实现不同区块链网络之间资产转移和信息交互的技术方案。它解决了区块链生态系统的孤岛问题,让用户能够在多个链之间无缝移动资产和数据。
跨链技术的出现是因为不同区块链各有优势,用户和开发者希望能够享受多链生态的便利,而不被锁定在单一网络中。
跨链需求背景:
主要跨链解决方案:
桥接协议(Bridge):
中继链架构:
侧链桥接:
哈希时间锁(HTLC):
预言机网络:
技术挑战:
风险控制措施:
未来发展:
What are the main aspects of blockchain performance bottlenecks?
What are the main aspects of blockchain performance bottlenecks?
考察点:区块链性能问题分析。
答案:
区块链的性能瓶颈主要源于其去中心化和安全性设计,这些限制体现在吞吐量、延迟、存储和计算等多个维度。理解这些瓶颈是设计扩容解决方案的前提。
区块链面临的是著名的"不可能三角"问题:去中心化、安全性和可扩展性很难同时达到最优,通常需要在三者之间进行权衡。
主要性能瓶颈:
共识机制限制:
吞吐量限制:
存储瓶颈:
网络传播延迟:
计算复杂度:
具体表现:
解决方向:
What is MEV (Maximum Extractable Value)? What impact does it have on blockchain networks?
What is MEV (Maximum Extractable Value)? What impact does it have on blockchain networks?
考察点:MEV概念与影响。
答案:
MEV(最大可提取价值)是指矿工、验证者或其他参与者通过重新排序、插入或审查交易来获得的超额利润。这种价值提取利用了区块链交易排序的权力和DeFi协议中的套利机会。
MEV反映了区块链经济系统中权力分配的不平衡,对网络公平性、用户体验和系统稳定性都产生重要影响。
MEV产生机制:
常见MEV策略:
抢跑交易(Front-running):
夹心攻击(Sandwich Attack):
套利交易(Arbitrage):
清算抢跑:
对网络的影响:
负面影响:
正面影响:
缓解方案:
技术发展:
How does sharding technology solve blockchain scalability issues?
How does sharding technology solve blockchain scalability issues?
考察点:分片技术原理。
答案:
分片技术通过将区块链网络分割成多个并行运行的子网络(分片),让不同分片同时处理不同的交易集合,从而实现水平扩容。这种设计打破了传统区块链"所有节点处理所有交易"的限制。
分片的核心思想借鉴了数据库的水平分区概念,但在去中心化环境下实现分片面临更多技术挑战,需要确保安全性和一致性。
分片扩容原理:
分片架构设计:
网络分片(Network Sharding):
交易分片(Transaction Sharding):
状态分片(State Sharding):
技术挑战与解决方案:
跨分片通信:
数据可用性:
安全性保证:
负载均衡:
实现阶段:
项目案例:
预期效果:
发展前景:
What is finality in consensus algorithms? How does finality differ across different consensus mechanisms?
What is finality in consensus algorithms? How does finality differ across different consensus mechanisms?
考察点:共识最终性深入理解。
答案:
最终性(Finality)是指区块链中的交易或区块达到不可撤销状态的特性。一旦交易获得最终确认,就不可能被回滚或修改,这为用户和应用提供了确定性保证。
最终性是衡量区块链安全性和可用性的重要指标,它直接影响用户体验、交易所充值确认时间和DeFi协议的设计。
最终性类型:
概率性最终性(Probabilistic Finality):
绝对最终性(Absolute Finality):
经济最终性(Economic Finality):
不同共识机制的最终性:
工作量证明(PoW):
权益证明(PoS):
拜占庭容错(BFT):
委托权益证明(DPoS):
最终性对比表:
| 共识机制 | 最终性类型 | 确认时间 | 安全保证 | 可扩展性 |
|---|---|---|---|---|
| PoW | 概率性 | 10-60分钟 | 很高 | 低 |
| PoS | 经济性 | 6-15分钟 | 高 | 中 |
| BFT | 绝对性 | 1-10秒 | 很高 | 中低 |
| DPoS | 概率性 | 1-3分钟 | 中高 | 高 |
实际应用考虑:
How is the security of blockchain networks assessed? What are common attack methods?
How is the security of blockchain networks assessed? What are common attack methods?
考察点:区块链安全威胁分析。
答案:
区块链安全性评估需要从多个维度综合考虑,包括共识机制强度、网络去中心化程度、代码审计质量和经济安全性等。安全性不是绝对的,而是一个动态平衡的状态。
评估区块链安全性的核心是理解其威胁模型,即确定可能的攻击者类型、攻击动机和可用资源,然后分析系统在这些威胁下的表现。
安全性评估维度:
共识层安全:
网络层安全:
应用层安全:
常见攻击方式:
共识层攻击:
51%攻击:
长程攻击(Long Range Attack):
无利害攻击(Nothing at Stake):
网络层攻击:
Eclipse攻击:
Sybil攻击:
应用层攻击:
智能合约漏洞:
私钥泄露:
MEV攻击:
评估方法和工具:
安全加固措施:
What is a 51% attack? How can this attack be prevented?
What is a 51% attack? How can this attack be prevented?
考察点:区块链安全机制。
答案:
51%攻击是指攻击者控制区块链网络中超过50%的计算能力(PoW)或质押代币(PoS),从而能够操纵共识过程,重写交易历史或进行双花攻击的安全威胁。
这种攻击利用了区块链"最长链规则"的设计,当攻击者拥有多数资源时,就能够创建并维持一个比诚实链更长的恶意分叉。
攻击机制原理:
算力/质押控制:
私有链构建:
历史重写:
攻击可能实现的行为:
无法实现的行为:
预防措施:
技术层面:
提高攻击成本:
检测和响应:
共识改进:
经济层面:
攻击成本分析:
激励对齐:
网络层面:
去中心化:
社会共识:
历史案例:
防护效果:
What is blockchain governance? What are the differences between on-chain and off-chain governance?
What is blockchain governance? What are the differences between on-chain and off-chain governance?
考察点:区块链治理理解。
答案:
区块链治理是指协调网络参与者就协议规则、参数调整和升级方案达成共识的机制和过程。它解决了去中心化系统中"如何集体做决定"的根本问题,是区块链长期发展和演进的关键。
治理机制的设计直接影响区块链的适应性、创新能力和社区凝聚力,是技术可持续发展的重要保障。
治理的核心要素:
链上治理(On-chain Governance):
特征:
运作机制:
代表项目:
链下治理(Off-chain Governance):
特征:
运作机制:
代表项目:
两种治理模式对比:
| 维度 | 链上治理 | 链下治理 |
|---|---|---|
| 透明度 | 高,所有投票公开 | 中等,部分过程不透明 |
| 效率 | 快速,自动执行 | 较慢,需要协调 |
| 参与门槛 | 需要代币,技术门槛低 | 需要技术知识和影响力 |
| 决策质量 | 可能被财阀控制 | 更注重技术合理性 |
| 灵活性 | 受智能合约限制 | 更灵活,可处理复杂情况 |
| 合法性 | 程序合法性强 | 依赖社会共识 |
混合治理模式:
现实中许多项目采用混合模式:
治理挑战:
发展趋势:
What is an Oracle? Why do blockchains need oracles?
What is an Oracle? Why do blockchains need oracles?
考察点:预言机作用与重要性。
答案:
预言机是连接区块链与外部世界的桥梁,负责将链外数据安全可靠地传输到链上,使智能合约能够访问和使用现实世界的信息。预言机解决了区块链的"信息孤岛"问题,是实现区块链实用价值的关键基础设施。
区块链本身是封闭的确定性系统,无法直接获取外部数据,而大多数有价值的应用都需要与现实世界交互,这就产生了对预言机的迫切需求。
预言机的必要性:
区块链的限制:
智能合约的需求:
预言机类型:
按数据流向分类:
输入预言机(Inbound Oracle):
输出预言机(Outbound Oracle):
按中心化程度分类:
中心化预言机:
去中心化预言机:
按数据类型分类:
技术实现机制:
数据聚合:
加密证明:
经济激励:
声誉系统:
主要预言机项目:
预言机问题与挑战:
应用场景:
What are the practical business application scenarios of blockchain? What are the advantages and challenges of each?
What are the practical business application scenarios of blockchain? What are the advantages and challenges of each?
考察点:区块链实际应用分析。
答案:
区块链技术已在多个行业找到实际应用场景,从最初的数字货币扩展到金融服务、供应链、数字身份、内容创作等领域。每个应用场景都利用了区块链的不同特性,同时也面临着特定的挑战。
区块链应用的核心价值在于解决多方协作中的信任问题,特别是在缺乏中央权威机构的环境下建立可信的数字化协作机制。
主要应用场景分析:
1. 去中心化金融(DeFi):
2. 供应链追溯:
3. 数字身份认证:
4. NFT和数字收藏品:
5. 版权保护和内容创作:
6. 投票和治理:
发展趋势:
What are the main differences between DApp and traditional web applications?
What are the main differences between DApp and traditional web applications?
考察点:DApp核心概念理解。
答案:
DApp(去中心化应用)与传统Web应用的主要区别体现在架构设计、数据存储、身份验证和用户交互等多个层面。传统Web应用通常采用中心化的服务器-客户端架构,而DApp基于区块链技术构建,具有去中心化的特性。
主要区别:
技术实现差异:
// 传统Web应用 - API调用
const response = await fetch('/api/user/profile', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: JSON.stringify(userData)
});
// DApp - 智能合约交互
const contract = new ethers.Contract(contractAddress, abi, signer);
const tx = await contract.updateProfile(userData);
await tx.wait(); // 等待区块确认
What core components are typically included in DApp frontend architecture?
What core components are typically included in DApp frontend architecture?
考察点:DApp架构设计基础。
答案:
DApp前端架构需要处理复杂的区块链交互、状态管理和用户体验优化。其核心组件设计需要考虑去中心化特性、异步交互和多种区块链网络的兼容性。
核心组件:
架构示例:
// DApp核心架构层次
DApp Frontend
├── Presentation Layer (React/Vue Components)
├── State Management (Redux/Zustand + Web3 State)
├── Web3 Integration Layer
│ ├── Wallet Connection Manager
│ ├── Contract Interaction Service
│ ├── Transaction Manager
│ └── Network Manager
├── Utility Layer
│ ├── Error Handler
│ ├── Cache Manager
│ └── BigNumber Formatter
└── External Services
├── IPFS Client
├── Subgraph Client
└── Price Oracle API
How to manage user authentication state in DApp?
How to manage user authentication state in DApp?
考察点:身份验证机制。
答案:
DApp的身份验证基于区块链地址和数字签名,无需传统的用户名密码系统。用户通过数字钱包连接DApp,钱包地址即为用户身份标识。身份验证状态管理需要处理钱包连接、地址变化、网络切换等多种场景。
身份验证流程:
实现示例:
// 身份验证状态管理
class AuthManager {
constructor() {
this.user = null;
this.isConnected = false;
this.listeners = [];
}
// 连接钱包
async connectWallet() {
try {
if (typeof window.ethereum !== 'undefined') {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
if (accounts.length > 0) {
this.user = {
address: accounts[0],
chainId: await window.ethereum.request({ method: 'eth_chainId' })
};
this.isConnected = true;
this.saveToStorage();
this.notifyListeners();
}
}
} catch (error) {
console.error('钱包连接失败:', error);
}
}
// 消息签名验证
async verifySignature(message) {
try {
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, this.user.address]
});
return signature;
} catch (error) {
console.error('签名验证失败:', error);
return null;
}
}
// 监听账户变化
setupEventListeners() {
if (window.ethereum) {
window.ethereum.on('accountsChanged', (accounts) => {
if (accounts.length === 0) {
this.logout();
} else {
this.user.address = accounts[0];
this.notifyListeners();
}
});
window.ethereum.on('chainChanged', (chainId) => {
this.user.chainId = chainId;
this.notifyListeners();
});
}
}
}
状态管理策略:
What is a Web3 Provider? What is its role in DApp?
What is a Web3 Provider? What is its role in DApp?
考察点:Provider概念理解。
答案:
Web3 Provider是DApp与区块链网络之间的通信桥梁,它提供了一套标准化的API接口,使前端应用能够与以太坊或其他兼容区块链进行交互。Provider负责处理网络连接、账户管理、交易签名等核心功能。
Provider的核心作用:
常见Provider类型:
// 1. 浏览器内置Provider (MetaMask)
if (typeof window.ethereum !== 'undefined') {
const provider = window.ethereum;
// 请求连接账户
await provider.request({ method: 'eth_requestAccounts' });
}
// 2. ethers.js Provider
import { ethers } from 'ethers';
// 使用浏览器Provider
const provider = new ethers.providers.Web3Provider(window.ethereum);
// 使用RPC Provider
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR-PROJECT-ID');
// 3. WalletConnect Provider
import WalletConnectProvider from '@walletconnect/web3-provider';
const provider = new WalletConnectProvider({
infuraId: 'YOUR-INFURA-ID',
qrcode: true
});
await provider.enable();
Provider使用示例:
// 获取账户信息
const accounts = await provider.request({ method: 'eth_accounts' });
const balance = await provider.request({
method: 'eth_getBalance',
params: [accounts[0], 'latest']
});
// 发送交易
const transactionParameters = {
to: '0x...',
value: ethers.utils.parseEther('1.0'),
gasLimit: '21000',
};
const txHash = await provider.request({
method: 'eth_sendTransaction',
params: [transactionParameters]
});
Provider管理最佳实践:
How does state management in DApp differ from traditional applications?
How does state management in DApp differ from traditional applications?
考察点:状态管理差异。
答案:
DApp的状态管理比传统应用更加复杂,需要同时处理链上状态和链下状态,并应对区块链的异步特性、不可逆性和网络延迟等挑战。状态管理策略必须考虑数据一致性、用户体验和性能优化的平衡。
主要差异:
状态管理架构:
// DApp状态管理结构
const dappState = {
// 用户相关状态
user: {
address: null,
balance: null,
isConnected: false,
chainId: null
},
// 链上状态 (只读,通过合约查询)
onChain: {
contractData: {},
tokenBalances: {},
transactionHistory: []
},
// 链下状态 (本地管理)
offChain: {
uiState: {},
cache: {},
pendingTransactions: []
},
// 交易状态管理
transactions: {
pending: [],
confirmed: [],
failed: []
}
};
// 状态更新策略
class DAppStateManager {
constructor() {
this.state = dappState;
this.listeners = [];
}
// 乐观更新 - 立即更新UI,等待链上确认
async optimisticUpdate(action, txPromise) {
// 1. 立即更新本地状态
this.updateLocalState(action);
// 2. 等待交易确认
try {
const receipt = await txPromise;
this.confirmTransaction(action, receipt);
} catch (error) {
// 3. 交易失败时回滚状态
this.revertState(action);
throw error;
}
}
// 监听链上事件更新状态
subscribeToChainEvents() {
contract.on('Transfer', (from, to, amount) => {
this.syncChainState();
});
}
}
状态同步策略:
// 实时状态同步
class StateSync {
// 定期同步链上状态
async syncChainState() {
const [balance, contractData] = await Promise.all([
provider.getBalance(userAddress),
contract.getData()
]);
this.updateState({
user: { balance },
onChain: { contractData }
});
}
// 处理pending交易状态
trackPendingTransaction(txHash) {
const interval = setInterval(async () => {
const receipt = await provider.getTransactionReceipt(txHash);
if (receipt) {
clearInterval(interval);
this.confirmTransaction(txHash, receipt);
}
}, 3000);
}
}
最佳实践:
How to handle asynchronous blockchain interactions in DApp?
How to handle asynchronous blockchain interactions in DApp?
考察点:异步处理基础。
答案:
区块链交互本质上是异步的,包括网络请求延迟、交易确认等待、区块生成时间等。DApp需要妥善处理这些异步操作,提供良好的用户体验,同时确保数据的准确性和一致性。
异步处理策略:
基本异步处理模式:
// 1. 基础异步交互模式
class BlockchainService {
async executeTransaction(contractMethod, ...args) {
try {
// 设置loading状态
this.setLoading(true);
// 估算Gas费用
const gasEstimate = await contractMethod.estimateGas(...args);
// 发送交易
const tx = await contractMethod(...args, {
gasLimit: gasEstimate.mul(120).div(100) // 增加20%缓冲
});
// 返回交易哈希,开始监听确认
this.trackTransaction(tx.hash);
return tx;
} catch (error) {
this.handleError(error);
throw error;
} finally {
this.setLoading(false);
}
}
// 交易确认监听
async trackTransaction(txHash) {
return new Promise((resolve, reject) => {
const checkConfirmation = async () => {
try {
const receipt = await provider.getTransactionReceipt(txHash);
if (receipt) {
if (receipt.status === 1) {
resolve(receipt);
} else {
reject(new Error('Transaction failed'));
}
} else {
// 继续等待确认
setTimeout(checkConfirmation, 3000);
}
} catch (error) {
reject(error);
}
};
checkConfirmation();
});
}
}
React Hook异步处理:
// 自定义Hook处理区块链交互
function useContractCall(contract, method, args = []) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const execute = useCallback(async (...newArgs) => {
try {
setLoading(true);
setError(null);
const result = await contract[method](...(newArgs.length ? newArgs : args));
setData(result);
return result;
} catch (err) {
setError(err);
throw err;
} finally {
setLoading(false);
}
}, [contract, method, args]);
return { data, loading, error, execute };
}
// 使用示例
function TokenBalance() {
const { data: balance, loading, execute: fetchBalance } = useContractCall(
tokenContract,
'balanceOf',
[userAddress]
);
useEffect(() => {
fetchBalance();
}, [fetchBalance]);
if (loading) return <Spinner />;
return <div>余额: {ethers.utils.formatEther(balance || 0)} ETH</div>;
}
并发处理和批量操作:
// 批量异步调用优化
class BatchProcessor {
async batchCall(calls) {
try {
// 使用Promise.all并发执行
const results = await Promise.all(
calls.map(async (call) => {
try {
return await call();
} catch (error) {
return { error };
}
})
);
return results;
} catch (error) {
throw new Error('Batch operation failed');
}
}
// 错误重试机制
async retryCall(fn, maxRetries = 3, delay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, i)));
}
}
}
}
用户体验优化:
What is transaction confirmation? How to display transaction status in UI?
What is transaction confirmation? How to display transaction status in UI?
考察点:交易状态管理。
答案:
交易确认是指交易被包含在区块中并得到网络认可的过程。每个新区块的产生都会为之前的交易增加一次确认,确认数越多,交易被回滚的概率越低。通常1-3个确认即可认为交易安全,重要交易建议等待6个或更多确认。
交易生命周期:
UI状态设计:
// 交易状态枚举
const TransactionStatus = {
IDLE: 'idle', // 初始状态
PREPARING: 'preparing', // 准备交易
PENDING: 'pending', // 等待确认
CONFIRMING: 'confirming', // 确认中
SUCCESS: 'success', // 成功
FAILED: 'failed' // 失败
};
// 交易状态组件
function TransactionStatus({ tx }) {
const [status, setStatus] = useState(TransactionStatus.IDLE);
const [confirmations, setConfirmations] = useState(0);
const [error, setError] = useState(null);
const handleTransaction = async () => {
try {
setStatus(TransactionStatus.PREPARING);
// 发送交易
const transaction = await contract.transfer(to, amount);
setStatus(TransactionStatus.PENDING);
// 监听确认
const receipt = await transaction.wait(1); // 等待1个确认
setStatus(TransactionStatus.CONFIRMING);
// 监听更多确认
watchConfirmations(transaction.hash);
} catch (err) {
setStatus(TransactionStatus.FAILED);
setError(err.message);
}
};
const watchConfirmations = (txHash) => {
const interval = setInterval(async () => {
try {
const currentBlock = await provider.getBlockNumber();
const receipt = await provider.getTransactionReceipt(txHash);
if (receipt) {
const confirmationCount = currentBlock - receipt.blockNumber + 1;
setConfirmations(confirmationCount);
if (confirmationCount >= 3) {
setStatus(TransactionStatus.SUCCESS);
clearInterval(interval);
}
}
} catch (error) {
clearInterval(interval);
}
}, 15000); // 每15秒检查一次
};
return (
<div className="transaction-status">
{status === TransactionStatus.PREPARING && (
<div className="status preparing">
<Spinner /> 准备交易中...
</div>
)}
{status === TransactionStatus.PENDING && (
<div className="status pending">
<Spinner /> 交易已提交,等待打包...
<small>交易哈希: {tx?.hash}</small>
</div>
)}
{status === TransactionStatus.CONFIRMING && (
<div className="status confirming">
<ProgressBar value={confirmations} max={3} />
确认中 ({confirmations}/3)
</div>
)}
{status === TransactionStatus.SUCCESS && (
<div className="status success">
<CheckIcon /> 交易成功!({confirmations} 个确认)
</div>
)}
{status === TransactionStatus.FAILED && (
<div className="status failed">
<ErrorIcon /> 交易失败: {error}
</div>
)}
</div>
);
}
状态展示最佳实践:
// 交易状态管理Hook
function useTransactionTracker(txHash) {
const [state, setState] = useState({
status: 'pending',
confirmations: 0,
blockNumber: null,
gasUsed: null
});
useEffect(() => {
if (!txHash) return;
const trackTransaction = async () => {
try {
// 获取交易receipt
const receipt = await provider.waitForTransaction(txHash, 1);
if (receipt.status === 0) {
setState(prev => ({ ...prev, status: 'failed' }));
return;
}
setState(prev => ({
...prev,
status: 'confirmed',
blockNumber: receipt.blockNumber,
gasUsed: receipt.gasUsed
}));
// 继续监听更多确认
const subscription = provider.on('block', (blockNumber) => {
const confirmations = blockNumber - receipt.blockNumber + 1;
setState(prev => ({
...prev,
confirmations,
status: confirmations >= 3 ? 'finalized' : 'confirmed'
}));
});
return () => provider.removeAllListeners('block');
} catch (error) {
setState(prev => ({ ...prev, status: 'failed', error }));
}
};
trackTransaction();
}, [txHash]);
return state;
}
用户体验增强:
How to handle network errors and timeouts in DApp?
How to handle network errors and timeouts in DApp?
考察点:错误处理基础。
答案:
DApp面临的网络错误比传统Web应用更复杂,包括RPC节点故障、网络拥堵、Gas费不足、交易被拒绝等。有效的错误处理策略需要识别错误类型、提供用户友好的提示,并实现自动恢复机制。
常见错误类型:
错误处理实现:
// 错误分类和处理
class DAppErrorHandler {
static handleError(error) {
// 用户拒绝错误
if (error.code === 4001) {
return {
type: 'USER_REJECTED',
message: '用户取消了操作',
recoverable: true
};
}
// Gas相关错误
if (error.message.includes('insufficient funds')) {
return {
type: 'INSUFFICIENT_FUNDS',
message: '余额不足,请检查ETH余额',
recoverable: true,
action: 'CHECK_BALANCE'
};
}
// 网络错误
if (error.message.includes('network') || error.code === 'NETWORK_ERROR') {
return {
type: 'NETWORK_ERROR',
message: '网络连接失败,请检查网络设置',
recoverable: true,
action: 'RETRY'
};
}
// 合约执行错误
if (error.message.includes('revert')) {
return {
type: 'CONTRACT_REVERT',
message: '交易被合约拒绝,请检查操作条件',
recoverable: false
};
}
return {
type: 'UNKNOWN_ERROR',
message: error.message || '发生未知错误',
recoverable: false
};
}
}
// 重试机制
class RetryManager {
async executeWithRetry(fn, options = {}) {
const {
maxRetries = 3,
baseDelay = 1000,
maxDelay = 10000,
backoffFactor = 2
} = options;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
const errorInfo = DAppErrorHandler.handleError(error);
// 不可恢复的错误直接抛出
if (!errorInfo.recoverable || attempt === maxRetries - 1) {
throw error;
}
// 计算延迟时间
const delay = Math.min(
baseDelay * Math.pow(backoffFactor, attempt),
maxDelay
);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
}
超时控制实现:
// 超时处理工具
class TimeoutManager {
// 带超时的Promise包装
static withTimeout(promise, timeoutMs = 30000) {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`操作超时 (${timeoutMs}ms)`));
}, timeoutMs);
})
]);
}
// 可取消的网络请求
static createCancellableRequest(requestFn, timeoutMs) {
let timeoutId;
let cancelled = false;
const promise = new Promise(async (resolve, reject) => {
timeoutId = setTimeout(() => {
cancelled = true;
reject(new Error('Request timeout'));
}, timeoutMs);
try {
if (!cancelled) {
const result = await requestFn();
if (!cancelled) {
clearTimeout(timeoutId);
resolve(result);
}
}
} catch (error) {
if (!cancelled) {
clearTimeout(timeoutId);
reject(error);
}
}
});
return {
promise,
cancel: () => {
cancelled = true;
clearTimeout(timeoutId);
}
};
}
}
React错误边界组件:
// DApp专用错误边界
class DAppErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
const errorDetails = DAppErrorHandler.handleError(error);
// 记录错误日志
console.error('DApp Error:', {
error,
errorInfo,
errorDetails
});
// 可选:发送错误报告到监控服务
this.reportError(error, errorInfo, errorDetails);
}
reportError = (error, errorInfo, errorDetails) => {
// 发送到错误监控服务
if (typeof window !== 'undefined' && window.analytics) {
window.analytics.track('DApp Error', {
errorType: errorDetails.type,
errorMessage: errorDetails.message,
stack: error.stack
});
}
};
render() {
if (this.state.hasError) {
const errorDetails = DAppErrorHandler.handleError(this.state.error);
return (
<div className="error-fallback">
<h2>出现错误</h2>
<p>{errorDetails.message}</p>
{errorDetails.action === 'RETRY' && (
<button onClick={() => window.location.reload()}>
重试
</button>
)}
{errorDetails.action === 'CHECK_BALANCE' && (
<button onClick={() => {/* 跳转到余额页面 */}}>
查看余额
</button>
)}
</div>
);
}
return this.props.children;
}
}
最佳实践:
What is Gas fee estimation? How to implement Gas fee estimation in frontend?
What is Gas fee estimation? How to implement Gas fee estimation in frontend?
考察点:Gas费用处理。
答案:
Gas费用是以太坊网络中执行交易或智能合约所需支付的费用,用于激励矿工打包交易。Gas费用估算帮助用户了解交易成本,选择合适的Gas价格以平衡交易速度和成本。前端需要实现准确的费用预估和用户友好的费用选择界面。
Gas费用组成:
EIP-1559费用结构(以太坊2.0后):
Gas费用估算实现:
// Gas费用估算服务
class GasEstimationService {
constructor(provider) {
this.provider = provider;
}
// 估算交易Gas Limit
async estimateGasLimit(transaction) {
try {
const gasEstimate = await this.provider.estimateGas(transaction);
// 增加15%的安全边距
return gasEstimate.mul(115).div(100);
} catch (error) {
console.error('Gas估算失败:', error);
// 返回默认值
return ethers.BigNumber.from('21000');
}
}
// 获取当前网络Gas价格
async getGasPrices() {
try {
// 获取网络建议的Gas价格
const feeData = await this.provider.getFeeData();
return {
slow: {
maxFeePerGas: feeData.maxFeePerGas?.mul(90).div(100), // 90%
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.mul(90).div(100)
},
standard: {
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
},
fast: {
maxFeePerGas: feeData.maxFeePerGas?.mul(110).div(100), // 110%
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.mul(110).div(100)
}
};
} catch (error) {
console.error('获取Gas价格失败:', error);
throw error;
}
}
// 计算预估费用
async calculateTransactionCost(transaction) {
const [gasLimit, gasPrices] = await Promise.all([
this.estimateGasLimit(transaction),
this.getGasPrices()
]);
return {
gasLimit,
slow: {
...gasPrices.slow,
totalCost: gasLimit.mul(gasPrices.slow.maxFeePerGas)
},
standard: {
...gasPrices.standard,
totalCost: gasLimit.mul(gasPrices.standard.maxFeePerGas)
},
fast: {
...gasPrices.fast,
totalCost: gasLimit.mul(gasPrices.fast.maxFeePerGas)
}
};
}
}
Gas费用选择组件:
// Gas费用选择器组件
function GasFeeSelector({ transaction, onFeeSelect }) {
const [feeOptions, setFeeOptions] = useState(null);
const [selectedOption, setSelectedOption] = useState('standard');
const [loading, setLoading] = useState(false);
const [customFee, setCustomFee] = useState(false);
useEffect(() => {
estimateFees();
}, [transaction]);
const estimateFees = async () => {
setLoading(true);
try {
const gasService = new GasEstimationService(provider);
const fees = await gasService.calculateTransactionCost(transaction);
setFeeOptions(fees);
// 默认选择标准费用
onFeeSelect(fees.standard);
} catch (error) {
console.error('费用估算失败:', error);
} finally {
setLoading(false);
}
};
const handleOptionSelect = (option) => {
setSelectedOption(option);
setCustomFee(false);
onFeeSelect(feeOptions[option]);
};
if (loading) {
return <div className="gas-fee-loading">估算Gas费用中...</div>;
}
return (
<div className="gas-fee-selector">
<h3>选择交易费用</h3>
{feeOptions && (
<div className="fee-options">
{['slow', 'standard', 'fast'].map((option) => {
const fee = feeOptions[option];
const isSelected = selectedOption === option && !customFee;
return (
<div
key={option}
className={`fee-option ${isSelected ? 'selected' : ''}`}
onClick={() => handleOptionSelect(option)}
>
<div className="option-label">
{option === 'slow' && '慢速 (~5分钟)'}
{option === 'standard' && '标准 (~2分钟)'}
{option === 'fast' && '快速 (~30秒)'}
</div>
<div className="option-price">
{ethers.utils.formatEther(fee.totalCost)} ETH
</div>
<div className="option-gwei">
{ethers.utils.formatUnits(fee.maxFeePerGas, 'gwei')} Gwei
</div>
</div>
);
})}
</div>
)}
<button
className="custom-fee-toggle"
onClick={() => setCustomFee(!customFee)}
>
{customFee ? '使用预设费用' : '自定义费用'}
</button>
{customFee && (
<CustomGasFeeInput
defaultValues={feeOptions?.standard}
onChange={onFeeSelect}
/>
)}
</div>
);
}
// 自定义Gas费用输入组件
function CustomGasFeeInput({ defaultValues, onChange }) {
const [maxFee, setMaxFee] = useState(
ethers.utils.formatUnits(defaultValues?.maxFeePerGas || 0, 'gwei')
);
const [priorityFee, setPriorityFee] = useState(
ethers.utils.formatUnits(defaultValues?.maxPriorityFeePerGas || 0, 'gwei')
);
const handleSubmit = () => {
onChange({
maxFeePerGas: ethers.utils.parseUnits(maxFee, 'gwei'),
maxPriorityFeePerGas: ethers.utils.parseUnits(priorityFee, 'gwei')
});
};
return (
<div className="custom-gas-input">
<div className="input-group">
<label>最大费用 (Gwei)</label>
<input
type="number"
value={maxFee}
onChange={(e) => setMaxFee(e.target.value)}
step="0.1"
/>
</div>
<div className="input-group">
<label>优先费用 (Gwei)</label>
<input
type="number"
value={priorityFee}
onChange={(e) => setPriorityFee(e.target.value)}
step="0.1"
/>
</div>
<button onClick={handleSubmit}>应用自定义费用</button>
</div>
);
}
实用工具函数:
// Gas费用工具函数
const GasUtils = {
// 格式化Gas价格显示
formatGasPrice(gasPrice) {
const gwei = ethers.utils.formatUnits(gasPrice, 'gwei');
return `${parseFloat(gwei).toFixed(1)} Gwei`;
},
// 格式化总费用显示
formatTotalFee(totalFee) {
const eth = ethers.utils.formatEther(totalFee);
return `${parseFloat(eth).toFixed(6)} ETH`;
},
// 预估确认时间
estimateConfirmationTime(gasPrice, networkCondition) {
// 基于Gas价格和网络状况的简单预估
const basePrice = ethers.utils.parseUnits('20', 'gwei');
const ratio = gasPrice.div(basePrice);
if (ratio.gte(2)) return '< 1分钟';
if (ratio.gte(1)) return '1-3分钟';
return '3-10分钟';
}
};
最佳实践:
How to implement user-friendly loading states in DApp?
How to implement user-friendly loading states in DApp?
考察点:用户体验基础。
答案:
DApp中的加载状态比传统应用更复杂,需要处理钱包连接、交易确认、区块链查询等多种异步操作。良好的加载状态设计能显著提升用户体验,减少用户焦虑,并提供清晰的操作反馈。
加载状态分类:
基础加载组件实现:
// 通用加载状态Hook
function useLoadingState() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [progress, setProgress] = useState(0);
const withLoading = async (asyncFn, progressCallback) => {
try {
setLoading(true);
setError(null);
setProgress(0);
const result = await asyncFn(setProgress);
return result;
} catch (err) {
setError(err);
throw err;
} finally {
setLoading(false);
setProgress(0);
}
};
return { loading, error, progress, withLoading };
}
// 交易加载状态组件
function TransactionLoader({ transaction, onComplete }) {
const [stage, setStage] = useState('preparing');
const [progress, setProgress] = useState(0);
const [error, setError] = useState(null);
useEffect(() => {
if (transaction) {
processTransaction();
}
}, [transaction]);
const processTransaction = async () => {
try {
// 阶段1: 准备交易
setStage('preparing');
setProgress(20);
await new Promise(resolve => setTimeout(resolve, 500));
// 阶段2: 等待用户签名
setStage('signing');
setProgress(40);
const signedTx = await transaction.wait(0);
// 阶段3: 提交到网络
setStage('submitting');
setProgress(60);
// 阶段4: 等待确认
setStage('confirming');
setProgress(80);
const receipt = await signedTx.wait(1);
// 阶段5: 完成
setStage('completed');
setProgress(100);
onComplete(receipt);
} catch (err) {
setError(err);
setStage('failed');
}
};
const stageLabels = {
preparing: '准备交易...',
signing: '等待签名确认...',
submitting: '提交到区块链网络...',
confirming: '等待交易确认...',
completed: '交易成功完成!',
failed: '交易失败'
};
return (
<div className="transaction-loader">
<div className="loader-header">
<h3>{stageLabels[stage]}</h3>
{stage !== 'failed' && stage !== 'completed' && (
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${progress}%` }}
/>
</div>
)}
</div>
<div className="stage-indicators">
{['preparing', 'signing', 'submitting', 'confirming', 'completed'].map((s, index) => (
<div
key={s}
className={`stage-indicator ${
s === stage ? 'active' :
['preparing', 'signing', 'submitting', 'confirming'].indexOf(stage) > index ? 'completed' : ''
}`}
>
{index + 1}
</div>
))}
</div>
{error && (
<div className="error-message">
错误: {error.message}
</div>
)}
</div>
);
}
骨架屏加载效果:
// 数据加载骨架屏组件
function DataSkeleton({ type = 'card', count = 1 }) {
const renderSkeleton = () => {
switch (type) {
case 'balance':
return (
<div className="skeleton-balance">
<div className="skeleton-line short" />
<div className="skeleton-line long" />
</div>
);
case 'transaction':
return (
<div className="skeleton-transaction">
<div className="skeleton-avatar" />
<div className="skeleton-content">
<div className="skeleton-line medium" />
<div className="skeleton-line short" />
</div>
<div className="skeleton-amount" />
</div>
);
case 'nft':
return (
<div className="skeleton-nft">
<div className="skeleton-image" />
<div className="skeleton-line short" />
<div className="skeleton-line medium" />
</div>
);
default:
return (
<div className="skeleton-card">
<div className="skeleton-line long" />
<div className="skeleton-line medium" />
<div className="skeleton-line short" />
</div>
);
}
};
return (
<div className="skeleton-container">
{Array.from({ length: count }, (_, i) => (
<div key={i} className="skeleton-item">
{renderSkeleton()}
</div>
))}
</div>
);
}
// 使用示例
function TokenList() {
const [tokens, setTokens] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadTokens();
}, []);
const loadTokens = async () => {
setLoading(true);
try {
const tokenData = await fetchUserTokens();
setTokens(tokenData);
} finally {
setLoading(false);
}
};
if (loading) {
return <DataSkeleton type="balance" count={5} />;
}
return (
<div className="token-list">
{tokens.map(token => (
<TokenCard key={token.address} token={token} />
))}
</div>
);
}
智能加载状态管理:
// 全局加载状态管理
class LoadingManager {
constructor() {
this.loadingStates = new Map();
this.listeners = [];
}
// 设置加载状态
setLoading(key, loading, message = '') {
this.loadingStates.set(key, { loading, message });
this.notifyListeners();
}
// 获取加载状态
isLoading(key) {
return this.loadingStates.get(key)?.loading || false;
}
// 获取所有加载状态
getAllLoadingStates() {
return Array.from(this.loadingStates.entries())
.filter(([_, state]) => state.loading)
.map(([key, state]) => ({ key, ...state }));
}
// 添加监听器
addListener(callback) {
this.listeners.push(callback);
}
// 通知监听器
notifyListeners() {
this.listeners.forEach(callback => callback(this.getAllLoadingStates()));
}
}
// React Hook封装
function useGlobalLoading() {
const [loadingStates, setLoadingStates] = useState([]);
useEffect(() => {
const loadingManager = LoadingManager.getInstance();
const handleLoadingChange = (states) => {
setLoadingStates(states);
};
loadingManager.addListener(handleLoadingChange);
return () => {
// 清理监听器
};
}, []);
return {
loadingStates,
isAnyLoading: loadingStates.length > 0,
setLoading: (key, loading, message) => {
LoadingManager.getInstance().setLoading(key, loading, message);
}
};
}
用户体验优化技巧:
How should local storage strategy be designed for DApp?
How should local storage strategy be designed for DApp?
考察点:数据存储策略。
答案:
DApp的本地存储策略需要平衡用户体验、数据安全和隐私保护。由于区块链数据的特殊性,需要区分不同类型的数据并采用相应的存储策略,同时考虑跨设备同步和数据一致性问题。
数据分类策略:
存储方案实现:
// 分层存储管理器
class DAppStorageManager {
constructor() {
this.storageTypes = {
SECURE: 'secure', // 加密存储
LOCAL: 'local', // 本地存储
SESSION: 'session', // 会话存储
MEMORY: 'memory', // 内存存储
CACHE: 'cache' // 缓存存储
};
this.memoryStorage = new Map();
this.encryptionKey = this.generateEncryptionKey();
}
// 根据数据类型选择存储方式
set(key, value, options = {}) {
const { type = this.storageTypes.LOCAL, ttl, encrypt = false } = options;
const data = {
value,
timestamp: Date.now(),
ttl: ttl ? Date.now() + ttl : null
};
if (encrypt) {
data.value = this.encrypt(JSON.stringify(value));
}
switch (type) {
case this.storageTypes.SECURE:
return this.setSecure(key, data);
case this.storageTypes.LOCAL:
return localStorage.setItem(key, JSON.stringify(data));
case this.storageTypes.SESSION:
return sessionStorage.setItem(key, JSON.stringify(data));
case this.storageTypes.MEMORY:
return this.memoryStorage.set(key, data);
case this.storageTypes.CACHE:
return this.setCache(key, data);
default:
throw new Error(`Unknown storage type: ${type}`);
}
}
// 获取数据
get(key, type = this.storageTypes.LOCAL) {
let data;
switch (type) {
case this.storageTypes.SECURE:
data = this.getSecure(key);
break;
case this.storageTypes.LOCAL:
data = localStorage.getItem(key);
break;
case this.storageTypes.SESSION:
data = sessionStorage.getItem(key);
break;
case this.storageTypes.MEMORY:
data = this.memoryStorage.get(key);
break;
case this.storageTypes.CACHE:
data = this.getCache(key);
break;
default:
return null;
}
if (!data) return null;
// 解析JSON数据
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch {
return null;
}
}
// 检查TTL过期
if (data.ttl && Date.now() > data.ttl) {
this.remove(key, type);
return null;
}
return data.value;
}
// 安全存储实现(使用Web Crypto API)
async setSecure(key, data) {
try {
const encrypted = await this.encryptData(JSON.stringify(data));
localStorage.setItem(`secure_${key}`, encrypted);
} catch (error) {
console.error('Secure storage failed:', error);
}
}
async getSecure(key) {
try {
const encrypted = localStorage.getItem(`secure_${key}`);
if (!encrypted) return null;
const decrypted = await this.decryptData(encrypted);
return JSON.parse(decrypted);
} catch (error) {
console.error('Secure retrieval failed:', error);
return null;
}
}
}
DApp专用存储配置:
// DApp存储配置
const DAppStorage = {
// 用户偏好设置
userPreferences: {
theme: { type: 'LOCAL', ttl: null },
language: { type: 'LOCAL', ttl: null },
currency: { type: 'LOCAL', ttl: null },
gasPreferences: { type: 'LOCAL', ttl: null }
},
// 连接状态
connectionState: {
lastConnectedWallet: { type: 'LOCAL', ttl: null },
connectedAddress: { type: 'SESSION', ttl: null },
networkId: { type: 'SESSION', ttl: null }
},
// 缓存数据
cache: {
tokenBalances: { type: 'CACHE', ttl: 300000 }, // 5分钟
tokenPrices: { type: 'CACHE', ttl: 60000 }, // 1分钟
transactionHistory: { type: 'CACHE', ttl: 600000 }, // 10分钟
contractAbi: { type: 'LOCAL', ttl: null } // 永久缓存
},
// 临时数据
temporary: {
formDrafts: { type: 'SESSION', ttl: null },
pendingTransactions: { type: 'MEMORY', ttl: 3600000 } // 1小时
}
};
// 存储操作封装
class DAppDataManager {
constructor() {
this.storage = new DAppStorageManager();
}
// 保存用户偏好
saveUserPreference(key, value) {
const config = DAppStorage.userPreferences[key];
if (config) {
this.storage.set(`pref_${key}`, value, config);
}
}
// 获取用户偏好
getUserPreference(key, defaultValue = null) {
const config = DAppStorage.userPreferences[key];
if (config) {
return this.storage.get(`pref_${key}`, config.type) || defaultValue;
}
return defaultValue;
}
// 缓存区块链数据
cacheBlockchainData(key, data) {
const config = DAppStorage.cache[key];
if (config) {
this.storage.set(`cache_${key}`, data, config);
}
}
// 获取缓存数据
getCachedData(key) {
const config = DAppStorage.cache[key];
if (config) {
return this.storage.get(`cache_${key}`, config.type);
}
return null;
}
// 清理过期数据
cleanup() {
// 清理过期缓存
Object.keys(DAppStorage.cache).forEach(key => {
this.storage.get(`cache_${key}`, DAppStorage.cache[key].type);
});
// 清理临时数据
Object.keys(DAppStorage.temporary).forEach(key => {
this.storage.get(`temp_${key}`, DAppStorage.temporary[key].type);
});
}
}
React Hook集成:
// DApp存储Hook
function useDAppStorage() {
const dataManager = useMemo(() => new DAppDataManager(), []);
// 用户偏好Hook
const useUserPreference = (key, defaultValue) => {
const [value, setValue] = useState(
() => dataManager.getUserPreference(key, defaultValue)
);
const updateValue = useCallback((newValue) => {
setValue(newValue);
dataManager.saveUserPreference(key, newValue);
}, [key]);
return [value, updateValue];
};
// 缓存数据Hook
const useCachedData = (key, fetchFn, dependencies = []) => {
const [data, setData] = useState(() => dataManager.getCachedData(key));
const [loading, setLoading] = useState(!data);
useEffect(() => {
if (!data) {
setLoading(true);
fetchFn().then(result => {
dataManager.cacheBlockchainData(key, result);
setData(result);
}).finally(() => {
setLoading(false);
});
}
}, dependencies);
const refresh = useCallback(() => {
setLoading(true);
fetchFn().then(result => {
dataManager.cacheBlockchainData(key, result);
setData(result);
}).finally(() => {
setLoading(false);
});
}, [key, fetchFn]);
return { data, loading, refresh };
};
return {
dataManager,
useUserPreference,
useCachedData
};
}
安全和隐私考虑:
How to handle display and calculation of large numbers (BigNumber) in DApp?
How to handle display and calculation of large numbers (BigNumber) in DApp?
考察点:数值处理基础。
答案:
区块链中的数值通常非常大,超出JavaScript原生Number类型的安全范围。例如以太坊使用wei作为最小单位,1 ETH = 10^18 wei。DApp必须使用BigNumber库进行精确的数值计算和显示,避免精度丢失和计算错误。
BigNumber使用场景:
基础BigNumber操作:
import { ethers } from 'ethers';
// BigNumber创建和转换
class BigNumberUtils {
// 创建BigNumber
static create(value, unit = 'wei') {
if (unit === 'ether' || unit === 'eth') {
return ethers.utils.parseEther(value.toString());
}
return ethers.BigNumber.from(value);
}
// 格式化显示
static format(bigNumber, decimals = 18, displayDecimals = 4) {
if (!bigNumber || bigNumber.isZero()) return '0';
// 转换为可读格式
const formatted = ethers.utils.formatUnits(bigNumber, decimals);
// 处理小数位数
const number = parseFloat(formatted);
if (number === 0) return '0';
if (number < 0.0001) return '< 0.0001';
return number.toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: displayDecimals
});
}
// 安全的数学运算
static safeAdd(a, b) {
try {
return ethers.BigNumber.from(a).add(ethers.BigNumber.from(b));
} catch (error) {
console.error('BigNumber addition error:', error);
return ethers.BigNumber.from(0);
}
}
static safeSub(a, b) {
try {
const bigA = ethers.BigNumber.from(a);
const bigB = ethers.BigNumber.from(b);
if (bigA.lt(bigB)) {
throw new Error('Subtraction would result in negative number');
}
return bigA.sub(bigB);
} catch (error) {
console.error('BigNumber subtraction error:', error);
return ethers.BigNumber.from(0);
}
}
static safeMul(a, b) {
try {
return ethers.BigNumber.from(a).mul(ethers.BigNumber.from(b));
} catch (error) {
console.error('BigNumber multiplication error:', error);
return ethers.BigNumber.from(0);
}
}
static safeDiv(a, b) {
try {
const bigB = ethers.BigNumber.from(b);
if (bigB.isZero()) {
throw new Error('Division by zero');
}
return ethers.BigNumber.from(a).div(bigB);
} catch (error) {
console.error('BigNumber division error:', error);
return ethers.BigNumber.from(0);
}
}
}
代币数量格式化组件:
// 代币数量显示组件
function TokenAmount({
amount,
decimals = 18,
symbol = '',
showFullPrecision = false,
prefix = '',
className = ''
}) {
const [showFull, setShowFull] = useState(showFullPrecision);
const formatAmount = (bigNumberAmount, showAll = false) => {
if (!bigNumberAmount) return '0';
try {
const formatted = ethers.utils.formatUnits(bigNumberAmount, decimals);
const number = parseFloat(formatted);
if (number === 0) return '0';
// 显示完整精度
if (showAll) {
return formatted;
}
// 智能显示策略
if (number >= 1000000) {
return (number / 1000000).toFixed(2) + 'M';
} else if (number >= 1000) {
return (number / 1000).toFixed(2) + 'K';
} else if (number >= 1) {
return number.toFixed(4);
} else if (number >= 0.0001) {
return number.toFixed(6);
} else {
return '< 0.0001';
}
} catch (error) {
console.error('Format amount error:', error);
return 'Error';
}
};
const displayAmount = formatAmount(amount, showFull);
const hasMorePrecision = amount && !ethers.BigNumber.from(amount).isZero() &&
displayAmount !== formatAmount(amount, true);
return (
<span className={`token-amount ${className}`}>
{prefix && <span className="prefix">{prefix}</span>}
<span
className={`amount ${hasMorePrecision ? 'clickable' : ''}`}
onClick={hasMorePrecision ? () => setShowFull(!showFull) : undefined}
title={showFull ? '点击简化显示' : '点击显示完整精度'}
>
{displayAmount}
</span>
{symbol && <span className="symbol">{symbol}</span>}
{hasMorePrecision && (
<span className="precision-toggle">
{showFull ? '⬆️' : '⬇️'}
</span>
)}
</span>
);
}
// 价格计算组件
function PriceCalculator({ tokenAmount, tokenPrice, tokenDecimals = 18 }) {
const calculateValue = () => {
try {
if (!tokenAmount || !tokenPrice) return ethers.BigNumber.from(0);
// 将代币数量转换为标准单位
const tokenInEther = ethers.utils.formatUnits(tokenAmount, tokenDecimals);
// 计算总价值 (使用字符串避免精度问题)
const totalValue = parseFloat(tokenInEther) * parseFloat(tokenPrice);
// 转换回BigNumber (以美分为单位保持精度)
return ethers.utils.parseUnits(totalValue.toFixed(2), 2);
} catch (error) {
console.error('Price calculation error:', error);
return ethers.BigNumber.from(0);
}
};
const totalValue = calculateValue();
return (
<div className="price-calculator">
<div className="token-amount">
<TokenAmount
amount={tokenAmount}
decimals={tokenDecimals}
symbol="TOKENS"
/>
</div>
<div className="multiplication">×</div>
<div className="token-price">
${tokenPrice}
</div>
<div className="equals">=</div>
<div className="total-value">
$<TokenAmount
amount={totalValue}
decimals={2}
showFullPrecision={true}
/>
</div>
</div>
);
}
输入验证和处理:
// BigNumber输入处理Hook
function useBigNumberInput(decimals = 18, maxValue = null) {
const [inputValue, setInputValue] = useState('');
const [bigNumberValue, setBigNumberValue] = useState(ethers.BigNumber.from(0));
const [error, setError] = useState('');
const handleInputChange = (value) => {
setInputValue(value);
setError('');
// 基础验证
if (!value || value === '') {
setBigNumberValue(ethers.BigNumber.from(0));
return;
}
// 验证数字格式
const numberRegex = /^\d*\.?\d*$/;
if (!numberRegex.test(value)) {
setError('请输入有效的数字');
return;
}
// 验证小数位数
const decimalPlaces = (value.split('.')[1] || '').length;
if (decimalPlaces > decimals) {
setError(`小数位数不能超过${decimals}位`);
return;
}
try {
// 转换为BigNumber
const bigNum = ethers.utils.parseUnits(value, decimals);
// 检查最大值限制
if (maxValue && bigNum.gt(maxValue)) {
setError('输入值超过最大限制');
return;
}
setBigNumberValue(bigNum);
} catch (err) {
setError('数值转换失败');
}
};
const setMaxValue = () => {
if (maxValue) {
const maxValueFormatted = ethers.utils.formatUnits(maxValue, decimals);
handleInputChange(maxValueFormatted);
}
};
return {
inputValue,
bigNumberValue,
error,
handleInputChange,
setMaxValue,
isValid: !error && !bigNumberValue.isZero()
};
}
// 使用示例
function TokenTransferForm() {
const {
inputValue,
bigNumberValue,
error,
handleInputChange,
setMaxValue,
isValid
} = useBigNumberInput(18, userBalance);
return (
<div className="transfer-form">
<div className="input-group">
<label>转账数量</label>
<input
type="text"
value={inputValue}
onChange={(e) => handleInputChange(e.target.value)}
placeholder="0.0"
className={error ? 'error' : ''}
/>
<button onClick={setMaxValue}>最大</button>
</div>
{error && <div className="error-message">{error}</div>}
<div className="balance-info">
余额: <TokenAmount amount={userBalance} decimals={18} />
</div>
<button disabled={!isValid}>
转账
</button>
</div>
);
}
最佳实践:
What is IPFS? How to integrate IPFS storage in DApp?
What is IPFS? How to integrate IPFS storage in DApp?
考察点:去中心化存储基础。
答案:
IPFS(InterPlanetary File System)是一个分布式的点对点文件系统,旨在创建一个全球统一的文件存储网络。IPFS使用内容寻址而非位置寻址,通过文件内容的哈希值来唯一标识文件,确保数据的不可篡改性和去中心化存储。
IPFS核心特性:
IPFS集成实现:
// IPFS客户端封装
import { create } from 'ipfs-http-client';
class IPFSManager {
constructor() {
// 连接到IPFS节点 (可以是本地节点或Infura等服务)
this.client = create({
host: 'ipfs.infura.io',
port: 5001,
protocol: 'https',
headers: {
authorization: `Basic ${Buffer.from(
`${projectId}:${projectSecret}`
).toString('base64')}`
}
});
}
// 上传文件到IPFS
async uploadFile(file) {
try {
const result = await this.client.add(file, {
progress: (prog) => console.log(`上传进度: ${prog}`)
});
return {
hash: result.cid.toString(),
path: result.path,
size: result.size
};
} catch (error) {
console.error('IPFS上传失败:', error);
throw error;
}
}
// 上传JSON数据
async uploadJSON(data) {
try {
const jsonString = JSON.stringify(data, null, 2);
const result = await this.client.add(jsonString);
return result.cid.toString();
} catch (error) {
console.error('JSON上传失败:', error);
throw error;
}
}
// 获取文件内容
async getFile(hash) {
try {
const stream = this.client.cat(hash);
let data = '';
for await (const chunk of stream) {
data += new TextDecoder().decode(chunk);
}
return data;
} catch (error) {
console.error('IPFS获取失败:', error);
throw error;
}
}
// 获取JSON数据
async getJSON(hash) {
try {
const data = await this.getFile(hash);
return JSON.parse(data);
} catch (error) {
console.error('JSON解析失败:', error);
throw error;
}
}
// 固定文件到节点
async pinFile(hash) {
try {
await this.client.pin.add(hash);
return true;
} catch (error) {
console.error('文件固定失败:', error);
return false;
}
}
// 生成IPFS网关URL
getGatewayUrl(hash, gateway = 'https://ipfs.io/ipfs/') {
return `${gateway}${hash}`;
}
}
React组件中的IPFS使用:
// IPFS文件上传组件
function IPFSFileUpload({ onUploadComplete }) {
const [uploading, setUploading] = useState(false);
const [progress, setProgress] = useState(0);
const [uploadedFiles, setUploadedFiles] = useState([]);
const ipfsManager = useMemo(() => new IPFSManager(), []);
const handleFileUpload = async (files) => {
setUploading(true);
setProgress(0);
try {
const uploadPromises = Array.from(files).map(async (file, index) => {
const result = await ipfsManager.uploadFile(file);
setProgress(((index + 1) / files.length) * 100);
return {
name: file.name,
hash: result.hash,
size: result.size,
url: ipfsManager.getGatewayUrl(result.hash)
};
});
const results = await Promise.all(uploadPromises);
setUploadedFiles(prev => [...prev, ...results]);
if (onUploadComplete) {
onUploadComplete(results);
}
} catch (error) {
console.error('上传失败:', error);
} finally {
setUploading(false);
setProgress(0);
}
};
return (
<div className="ipfs-upload">
<div
className="upload-zone"
onDrop={(e) => {
e.preventDefault();
handleFileUpload(e.dataTransfer.files);
}}
onDragOver={(e) => e.preventDefault()}
>
{uploading ? (
<div className="upload-progress">
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${progress}%` }}
/>
</div>
<span>上传中... {Math.round(progress)}%</span>
</div>
) : (
<div className="upload-prompt">
<p>拖拽文件到此处或点击选择</p>
<input
type="file"
multiple
onChange={(e) => handleFileUpload(e.target.files)}
style={{ display: 'none' }}
/>
</div>
)}
</div>
{uploadedFiles.length > 0 && (
<div className="uploaded-files">
<h4>已上传文件</h4>
{uploadedFiles.map((file, index) => (
<div key={index} className="file-item">
<span className="file-name">{file.name}</span>
<span className="file-hash">{file.hash}</span>
<a
href={file.url}
target="_blank"
rel="noopener noreferrer"
>
查看
</a>
</div>
))}
</div>
)}
</div>
);
}
// NFT元数据上传组件
function NFTMetadataUpload({ onMetadataUploaded }) {
const [metadata, setMetadata] = useState({
name: '',
description: '',
image: '',
attributes: []
});
const [uploading, setUploading] = useState(false);
const ipfsManager = useMemo(() => new IPFSManager(), []);
const handleImageUpload = async (file) => {
try {
const result = await ipfsManager.uploadFile(file);
setMetadata(prev => ({
...prev,
image: ipfsManager.getGatewayUrl(result.hash)
}));
} catch (error) {
console.error('图片上传失败:', error);
}
};
const handleMetadataUpload = async () => {
setUploading(true);
try {
// 验证元数据
if (!metadata.name || !metadata.image) {
throw new Error('名称和图片是必需的');
}
// 上传元数据JSON
const hash = await ipfsManager.uploadJSON(metadata);
const metadataUrl = ipfsManager.getGatewayUrl(hash);
if (onMetadataUploaded) {
onMetadataUploaded({
hash,
url: metadataUrl,
metadata
});
}
} catch (error) {
console.error('元数据上传失败:', error);
} finally {
setUploading(false);
}
};
return (
<div className="nft-metadata-upload">
<div className="metadata-form">
<input
type="text"
placeholder="NFT名称"
value={metadata.name}
onChange={(e) => setMetadata(prev => ({
...prev,
name: e.target.value
}))}
/>
<textarea
placeholder="NFT描述"
value={metadata.description}
onChange={(e) => setMetadata(prev => ({
...prev,
description: e.target.value
}))}
/>
<div className="image-upload">
<input
type="file"
accept="image/*"
onChange={(e) => {
if (e.target.files[0]) {
handleImageUpload(e.target.files[0]);
}
}}
/>
{metadata.image && (
<img src={metadata.image} alt="Preview" className="image-preview" />
)}
</div>
<button
onClick={handleMetadataUpload}
disabled={uploading || !metadata.name || !metadata.image}
>
{uploading ? '上传中...' : '上传元数据'}
</button>
</div>
</div>
);
}
IPFS数据获取Hook:
// IPFS数据获取Hook
function useIPFSData(hash) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const ipfsManager = useMemo(() => new IPFSManager(), []);
useEffect(() => {
if (!hash) return;
const fetchData = async () => {
setLoading(true);
setError(null);
try {
// 尝试解析为JSON,失败则返回原始文本
let result;
try {
result = await ipfsManager.getJSON(hash);
} catch {
result = await ipfsManager.getFile(hash);
}
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [hash]);
return { data, loading, error };
}
// 使用示例
function IPFSDataViewer({ hash }) {
const { data, loading, error } = useIPFSData(hash);
if (loading) return <div>加载IPFS数据中...</div>;
if (error) return <div>错误: {error}</div>;
if (!data) return <div>无数据</div>;
return (
<div className="ipfs-data-viewer">
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
IPFS应用场景:
最佳实践:
What special considerations are there for form validation in DApp compared to traditional applications?
What special considerations are there for form validation in DApp compared to traditional applications?
考察点:表单处理差异。
答案:
DApp表单验证除了传统的客户端验证外,还需要考虑区块链特有的约束条件,如Gas费用、网络状态、智能合约限制等。验证策略需要在用户体验和交易成本之间找到平衡,避免用户因验证不充分而产生失败交易的Gas费损失。
DApp表单验证特殊考虑:
DApp专用验证实现:
// DApp表单验证器
class DAppFormValidator {
constructor(provider, userAddress) {
this.provider = provider;
this.userAddress = userAddress;
}
// 验证用户余额
async validateBalance(requiredAmount, tokenContract = null) {
try {
let balance;
if (tokenContract) {
// ERC20代币余额
balance = await tokenContract.balanceOf(this.userAddress);
} else {
// ETH余额
balance = await this.provider.getBalance(this.userAddress);
}
if (balance.lt(requiredAmount)) {
return {
valid: false,
error: '余额不足',
current: balance,
required: requiredAmount
};
}
return { valid: true };
} catch (error) {
return {
valid: false,
error: '余额查询失败'
};
}
}
// 验证Gas费用
async validateGasFee(transaction) {
try {
// 估算Gas
const gasEstimate = await this.provider.estimateGas(transaction);
const feeData = await this.provider.getFeeData();
const gasCost = gasEstimate.mul(feeData.gasPrice || feeData.maxFeePerGas);
// 检查ETH余额是否足够支付Gas
const ethBalance = await this.provider.getBalance(this.userAddress);
if (ethBalance.lt(gasCost)) {
return {
valid: false,
error: 'ETH余额不足以支付Gas费用',
gasCost,
ethBalance
};
}
return {
valid: true,
gasCost,
gasEstimate
};
} catch (error) {
return {
valid: false,
error: 'Gas费用估算失败'
};
}
}
// 验证网络
async validateNetwork(requiredChainId) {
try {
const network = await this.provider.getNetwork();
if (network.chainId !== requiredChainId) {
return {
valid: false,
error: '请切换到正确的网络',
current: network.chainId,
required: requiredChainId
};
}
return { valid: true };
} catch (error) {
return {
valid: false,
error: '网络检查失败'
};
}
}
// 验证地址格式
validateAddress(address) {
if (!ethers.utils.isAddress(address)) {
return {
valid: false,
error: '无效的地址格式'
};
}
if (address === ethers.constants.AddressZero) {
return {
valid: false,
error: '不能使用零地址'
};
}
return { valid: true };
}
// 验证代币数量
validateTokenAmount(amount, decimals = 18, maxAmount = null) {
try {
const parsedAmount = ethers.utils.parseUnits(amount.toString(), decimals);
if (parsedAmount.isZero()) {
return {
valid: false,
error: '数量必须大于0'
};
}
if (maxAmount && parsedAmount.gt(maxAmount)) {
return {
valid: false,
error: '数量超过最大限制'
};
}
return {
valid: true,
parsedAmount
};
} catch (error) {
return {
valid: false,
error: '无效的数量格式'
};
}
}
// 智能合约状态验证
async validateContractState(contract, validationRules) {
const results = {};
for (const [key, rule] of Object.entries(validationRules)) {
try {
const value = await contract[rule.method](...(rule.params || []));
if (rule.validator) {
const validation = rule.validator(value);
results[key] = validation;
} else {
results[key] = { valid: true, value };
}
} catch (error) {
results[key] = {
valid: false,
error: `合约状态检查失败: ${key}`
};
}
}
return results;
}
}
React表单验证Hook:
// DApp表单验证Hook
function useDAppFormValidation(provider, userAddress, chainId) {
const [errors, setErrors] = useState({});
const [validating, setValidating] = useState(false);
const validator = useMemo(() =>
new DAppFormValidator(provider, userAddress),
[provider, userAddress]
);
const validateField = async (fieldName, value, validationConfig) => {
setValidating(true);
try {
let result = { valid: true };
switch (validationConfig.type) {
case 'address':
result = validator.validateAddress(value);
break;
case 'tokenAmount':
result = validator.validateTokenAmount(
value,
validationConfig.decimals,
validationConfig.maxAmount
);
break;
case 'balance':
result = await validator.validateBalance(
value,
validationConfig.tokenContract
);
break;
case 'network':
result = await validator.validateNetwork(chainId);
break;
default:
// 自定义验证函数
if (validationConfig.customValidator) {
result = await validationConfig.customValidator(value);
}
}
setErrors(prev => ({
...prev,
[fieldName]: result.valid ? null : result.error
}));
return result;
} catch (error) {
const errorMessage = `验证失败: ${error.message}`;
setErrors(prev => ({
...prev,
[fieldName]: errorMessage
}));
return { valid: false, error: errorMessage };
} finally {
setValidating(false);
}
};
const validateForm = async (formData, validationSchema) => {
setValidating(true);
const results = {};
try {
// 并行执行所有验证
const validationPromises = Object.entries(validationSchema).map(
async ([fieldName, config]) => {
const value = formData[fieldName];
const result = await validateField(fieldName, value, config);
return [fieldName, result];
}
);
const validationResults = await Promise.all(validationPromises);
validationResults.forEach(([fieldName, result]) => {
results[fieldName] = result;
});
const isValid = Object.values(results).every(r => r.valid);
return {
isValid,
results,
errors: Object.fromEntries(
Object.entries(results)
.filter(([_, result]) => !result.valid)
.map(([fieldName, result]) => [fieldName, result.error])
)
};
} finally {
setValidating(false);
}
};
return {
errors,
validating,
validateField,
validateForm,
clearErrors: () => setErrors({})
};
}
实际应用示例:
// 代币转账表单
function TokenTransferForm({ tokenContract }) {
const { provider, userAddress, chainId } = useWeb3();
const [formData, setFormData] = useState({
recipient: '',
amount: '',
gasPrice: ''
});
const { errors, validating, validateForm } = useDAppFormValidation(
provider,
userAddress,
chainId
);
const validationSchema = {
recipient: {
type: 'address'
},
amount: {
type: 'tokenAmount',
decimals: 18,
maxAmount: userTokenBalance
},
gasPrice: {
type: 'tokenAmount',
decimals: 9, // Gwei
customValidator: async (value) => {
const minGasPrice = ethers.utils.parseUnits('1', 'gwei');
const maxGasPrice = ethers.utils.parseUnits('100', 'gwei');
const gasPrice = ethers.utils.parseUnits(value, 'gwei');
if (gasPrice.lt(minGasPrice)) {
return { valid: false, error: 'Gas价格过低' };
}
if (gasPrice.gt(maxGasPrice)) {
return { valid: false, error: 'Gas价格过高' };
}
return { valid: true };
}
}
};
const handleSubmit = async (e) => {
e.preventDefault();
// 执行表单验证
const validation = await validateForm(formData, validationSchema);
if (!validation.isValid) {
console.log('表单验证失败:', validation.errors);
return;
}
// 额外的Gas费用验证
const transaction = {
to: tokenContract.address,
data: tokenContract.interface.encodeFunctionData('transfer', [
formData.recipient,
ethers.utils.parseEther(formData.amount)
])
};
const gasValidation = await validator.validateGasFee(transaction);
if (!gasValidation.valid) {
console.log('Gas费用验证失败:', gasValidation.error);
return;
}
// 提交交易
try {
const tx = await tokenContract.transfer(
formData.recipient,
ethers.utils.parseEther(formData.amount)
);
console.log('交易提交成功:', tx.hash);
} catch (error) {
console.error('交易失败:', error);
}
};
return (
<form onSubmit={handleSubmit} className="token-transfer-form">
<div className="field-group">
<label>接收地址</label>
<input
type="text"
value={formData.recipient}
onChange={(e) => setFormData(prev => ({
...prev,
recipient: e.target.value
}))}
className={errors.recipient ? 'error' : ''}
/>
{errors.recipient && (
<div className="error-message">{errors.recipient}</div>
)}
</div>
<div className="field-group">
<label>转账数量</label>
<input
type="text"
value={formData.amount}
onChange={(e) => setFormData(prev => ({
...prev,
amount: e.target.value
}))}
className={errors.amount ? 'error' : ''}
/>
{errors.amount && (
<div className="error-message">{errors.amount}</div>
)}
</div>
<button type="submit" disabled={validating}>
{validating ? '验证中...' : '发送转账'}
</button>
</form>
);
}
最佳实践:
How to design responsive layout for DApp to adapt to different devices?
How to design responsive layout for DApp to adapt to different devices?
考察点:响应式设计基础。
答案:
DApp的响应式设计需要特别考虑钱包交互、复杂数据展示和移动端的操作便利性。与传统Web应用相比,DApp需要处理更多的状态信息、交易确认界面和数值显示,这对响应式设计提出了更高的要求。
DApp响应式设计特殊考虑:
响应式布局实现:
/* DApp响应式基础样式 */
.dapp-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 16px;
/* 移动优先的设计 */
@media (min-width: 768px) {
padding: 0 24px;
}
@media (min-width: 1024px) {
padding: 0 32px;
}
}
/* 钱包连接按钮响应式 */
.wallet-connect {
display: flex;
flex-direction: column;
gap: 12px;
width: 100%;
@media (min-width: 768px) {
flex-direction: row;
width: auto;
gap: 16px;
}
}
.wallet-option {
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
border-radius: 12px;
background: var(--card-background);
border: 1px solid var(--border-color);
cursor: pointer;
transition: all 0.2s ease;
/* 移动端增大触摸区域 */
min-height: 48px;
@media (min-width: 768px) {
min-height: auto;
padding: 12px 20px;
}
}
/* 数据表格响应式 */
.transaction-table {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
table {
width: 100%;
min-width: 600px;
border-collapse: collapse;
}
/* 移动端卡片式布局 */
@media (max-width: 767px) {
table, thead, tbody, th, td, tr {
display: block;
}
thead tr {
position: absolute;
top: -9999px;
left: -9999px;
}
tr {
border: 1px solid var(--border-color);
border-radius: 8px;
margin-bottom: 12px;
padding: 16px;
background: var(--card-background);
}
td {
border: none;
position: relative;
padding: 8px 0;
&:before {
content: attr(data-label) ": ";
font-weight: bold;
color: var(--text-secondary);
}
}
}
}
/* 表单响应式布局 */
.form-layout {
display: grid;
gap: 20px;
/* 移动端单列布局 */
grid-template-columns: 1fr;
/* 平板端双列布局 */
@media (min-width: 768px) {
grid-template-columns: 1fr 1fr;
gap: 24px;
}
/* 桌面端更复杂的网格 */
@media (min-width: 1024px) {
grid-template-columns: 2fr 1fr;
gap: 32px;
}
}
.form-full-width {
grid-column: 1 / -1;
}
/* 数值显示响应式 */
.balance-display {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
@media (min-width: 768px) {
flex-direction: row;
align-items: center;
gap: 12px;
}
}
.balance-amount {
font-size: 24px;
font-weight: bold;
@media (min-width: 768px) {
font-size: 32px;
}
}
.balance-usd {
font-size: 14px;
color: var(--text-secondary);
@media (min-width: 768px) {
font-size: 16px;
}
}
React响应式组件:
// 响应式Hook
function useResponsive() {
const [breakpoint, setBreakpoint] = useState('mobile');
useEffect(() => {
const getBreakpoint = () => {
const width = window.innerWidth;
if (width >= 1024) return 'desktop';
if (width >= 768) return 'tablet';
return 'mobile';
};
const handleResize = () => {
setBreakpoint(getBreakpoint());
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return {
breakpoint,
isMobile: breakpoint === 'mobile',
isTablet: breakpoint === 'tablet',
isDesktop: breakpoint === 'desktop'
};
}
// 响应式钱包连接组件
function ResponsiveWalletConnect({ onConnect }) {
const { isMobile } = useResponsive();
const [showModal, setShowModal] = useState(false);
const walletOptions = [
{ name: 'MetaMask', icon: '🦊', id: 'metamask' },
{ name: 'WalletConnect', icon: '🔗', id: 'walletconnect' },
{ name: 'Coinbase', icon: '🔷', id: 'coinbase' }
];
if (isMobile) {
return (
<>
<button
className="wallet-connect-button mobile"
onClick={() => setShowModal(true)}
>
连接钱包
</button>
{showModal && (
<div className="wallet-modal mobile">
<div className="modal-content">
<div className="modal-header">
<h3>选择钱包</h3>
<button
className="close-button"
onClick={() => setShowModal(false)}
>
×
</button>
</div>
<div className="wallet-options">
{walletOptions.map(wallet => (
<button
key={wallet.id}
className="wallet-option mobile"
onClick={() => {
onConnect(wallet.id);
setShowModal(false);
}}
>
<span className="wallet-icon">{wallet.icon}</span>
<span className="wallet-name">{wallet.name}</span>
</button>
))}
</div>
</div>
</div>
)}
</>
);
}
return (
<div className="wallet-connect desktop">
{walletOptions.map(wallet => (
<button
key={wallet.id}
className="wallet-option"
onClick={() => onConnect(wallet.id)}
>
<span className="wallet-icon">{wallet.icon}</span>
<span className="wallet-name">{wallet.name}</span>
</button>
))}
</div>
);
}
// 响应式交易列表
function ResponsiveTransactionList({ transactions }) {
const { isMobile } = useResponsive();
if (isMobile) {
return (
<div className="transaction-list mobile">
{transactions.map(tx => (
<div key={tx.hash} className="transaction-card">
<div className="tx-header">
<span className="tx-type">{tx.type}</span>
<span className="tx-status">{tx.status}</span>
</div>
<div className="tx-details">
<div className="tx-amount">
{tx.amount} {tx.symbol}
</div>
<div className="tx-time">
{formatTime(tx.timestamp)}
</div>
<div className="tx-hash">
{shortenAddress(tx.hash)}
</div>
</div>
</div>
))}
</div>
);
}
return (
<div className="transaction-table desktop">
<table>
<thead>
<tr>
<th>类型</th>
<th>数量</th>
<th>状态</th>
<th>时间</th>
<th>交易哈希</th>
</tr>
</thead>
<tbody>
{transactions.map(tx => (
<tr key={tx.hash}>
<td>{tx.type}</td>
<td>{tx.amount} {tx.symbol}</td>
<td>{tx.status}</td>
<td>{formatTime(tx.timestamp)}</td>
<td>{shortenAddress(tx.hash)}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
// 响应式数值输入
function ResponsiveNumberInput({
value,
onChange,
symbol,
maxValue,
placeholder = "0.0"
}) {
const { isMobile } = useResponsive();
return (
<div className={`number-input ${isMobile ? 'mobile' : 'desktop'}`}>
<input
type="text"
inputMode="decimal"
pattern="[0-9]*\.?[0-9]*"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
className="amount-input"
/>
<div className="input-accessories">
{symbol && <span className="symbol">{symbol}</span>}
{maxValue && (
<button
type="button"
className="max-button"
onClick={() => onChange(maxValue)}
>
MAX
</button>
)}
</div>
</div>
);
}
移动端优化技巧:
// 移动端特殊处理
function MobileOptimizations() {
useEffect(() => {
// 防止双击缩放
document.addEventListener('touchstart', (e) => {
if (e.touches.length > 1) {
e.preventDefault();
}
});
// 防止长按选择文本
document.addEventListener('selectstart', (e) => {
if (e.target.classList.contains('no-select')) {
e.preventDefault();
}
});
// 优化输入框在iOS上的行为
const inputs = document.querySelectorAll('input[inputmode="decimal"]');
inputs.forEach(input => {
input.addEventListener('focus', () => {
// 滚动到视图中
setTimeout(() => {
input.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 300);
});
});
}, []);
return null;
}
// PWA支持
function usePWA() {
const [installPrompt, setInstallPrompt] = useState(null);
const [isInstallable, setIsInstallable] = useState(false);
useEffect(() => {
const handler = (e) => {
e.preventDefault();
setInstallPrompt(e);
setIsInstallable(true);
};
window.addEventListener('beforeinstallprompt', handler);
return () => window.removeEventListener('beforeinstallprompt', handler);
}, []);
const installApp = async () => {
if (!installPrompt) return;
const result = await installPrompt.prompt();
console.log('PWA安装结果:', result);
setInstallPrompt(null);
setIsInstallable(false);
};
return { isInstallable, installApp };
}
最佳实践:
How to design a scalable DApp state management architecture?
How to design a scalable DApp state management architecture?
考察点:架构设计能力。
答案:
可扩展的DApp状态管理架构需要处理多链环境、复杂的异步交互和大量的区块链数据。架构设计应该遵循模块化、可测试性和可维护性原则,同时考虑性能优化和用户体验。
架构设计原则:
核心架构实现:
// 状态管理架构基础
class DAppStateManager {
constructor() {
this.stores = new Map();
this.eventBus = new EventBus();
this.middleware = [];
this.subscriptions = new Map();
}
// 注册状态存储
registerStore(name, store) {
this.stores.set(name, store);
// 连接事件系统
store.setEventBus(this.eventBus);
return store;
}
// 获取状态存储
getStore(name) {
return this.stores.get(name);
}
// 注册中间件
use(middleware) {
this.middleware.push(middleware);
}
// 分发action
async dispatch(action) {
let result = action;
// 执行中间件
for (const middleware of this.middleware) {
result = await middleware(result, this);
}
// 分发到相关stores
const promises = [];
this.stores.forEach(store => {
if (store.canHandle(result)) {
promises.push(store.dispatch(result));
}
});
return Promise.all(promises);
}
}
// 基础Store类
class BaseStore {
constructor(initialState = {}) {
this.state = { ...initialState };
this.listeners = new Set();
this.eventBus = null;
}
setEventBus(eventBus) {
this.eventBus = eventBus;
}
// 更新状态
setState(partialState) {
const prevState = { ...this.state };
this.state = { ...this.state, ...partialState };
// 通知监听者
this.listeners.forEach(listener => {
listener(this.state, prevState);
});
// 发出状态变化事件
if (this.eventBus) {
this.eventBus.emit('stateChange', {
store: this.constructor.name,
state: this.state,
prevState
});
}
}
// 获取状态
getState() {
return { ...this.state };
}
// 订阅状态变化
subscribe(listener) {
this.listeners.add(listener);
return () => {
this.listeners.delete(listener);
};
}
// 判断是否能处理action
canHandle(action) {
return false; // 子类实现
}
// 分发action
async dispatch(action) {
// 子类实现
}
}
专用Store实现:
// Web3连接状态管理
class Web3Store extends BaseStore {
constructor() {
super({
isConnected: false,
address: null,
chainId: null,
provider: null,
signer: null,
network: null,
balance: null
});
}
canHandle(action) {
return action.type && action.type.startsWith('WEB3_');
}
async dispatch(action) {
switch (action.type) {
case 'WEB3_CONNECT':
return this.handleConnect(action.payload);
case 'WEB3_DISCONNECT':
return this.handleDisconnect();
case 'WEB3_ACCOUNT_CHANGED':
return this.handleAccountChanged(action.payload);
case 'WEB3_CHAIN_CHANGED':
return this.handleChainChanged(action.payload);
default:
return;
}
}
async handleConnect(provider) {
try {
const signer = provider.getSigner();
const address = await signer.getAddress();
const network = await provider.getNetwork();
const balance = await provider.getBalance(address);
this.setState({
isConnected: true,
address,
chainId: network.chainId,
provider,
signer,
network,
balance
});
// 监听账户和网络变化
this.setupEventListeners(provider);
} catch (error) {
console.error('Web3连接失败:', error);
throw error;
}
}
setupEventListeners(provider) {
if (provider.provider?.on) {
provider.provider.on('accountsChanged', (accounts) => {
this.dispatch({
type: 'WEB3_ACCOUNT_CHANGED',
payload: accounts[0] || null
});
});
provider.provider.on('chainChanged', (chainId) => {
this.dispatch({
type: 'WEB3_CHAIN_CHANGED',
payload: parseInt(chainId, 16)
});
});
}
}
}
// 合约状态管理
class ContractStore extends BaseStore {
constructor() {
super({
contracts: new Map(),
contractData: new Map(),
loading: new Set(),
errors: new Map()
});
}
canHandle(action) {
return action.type && action.type.startsWith('CONTRACT_');
}
async dispatch(action) {
switch (action.type) {
case 'CONTRACT_REGISTER':
return this.registerContract(action.payload);
case 'CONTRACT_CALL':
return this.callContract(action.payload);
case 'CONTRACT_SEND':
return this.sendTransaction(action.payload);
default:
return;
}
}
registerContract(contractConfig) {
const { name, address, abi, provider } = contractConfig;
const contract = new ethers.Contract(address, abi, provider);
const state = this.getState();
const contracts = new Map(state.contracts);
contracts.set(name, contract);
this.setState({ contracts });
}
async callContract({ contractName, method, params = [], cacheKey }) {
const contracts = this.getState().contracts;
const contract = contracts.get(contractName);
if (!contract) {
throw new Error(`Contract ${contractName} not found`);
}
// 检查缓存
if (cacheKey) {
const cachedData = this.getState().contractData.get(cacheKey);
if (cachedData && !this.isCacheExpired(cachedData)) {
return cachedData.data;
}
}
// 设置loading状态
const loading = new Set(this.getState().loading);
loading.add(cacheKey || `${contractName}.${method}`);
this.setState({ loading });
try {
const result = await contract[method](...params);
// 缓存结果
if (cacheKey) {
const contractData = new Map(this.getState().contractData);
contractData.set(cacheKey, {
data: result,
timestamp: Date.now(),
ttl: 300000 // 5分钟缓存
});
this.setState({ contractData });
}
return result;
} catch (error) {
const errors = new Map(this.getState().errors);
errors.set(cacheKey || `${contractName}.${method}`, error);
this.setState({ errors });
throw error;
} finally {
const loading = new Set(this.getState().loading);
loading.delete(cacheKey || `${contractName}.${method}`);
this.setState({ loading });
}
}
isCacheExpired(cacheItem) {
return Date.now() > (cacheItem.timestamp + cacheItem.ttl);
}
}
// 交易状态管理
class TransactionStore extends BaseStore {
constructor() {
super({
transactions: new Map(),
pending: new Set(),
confirmed: new Set(),
failed: new Set()
});
}
canHandle(action) {
return action.type && action.type.startsWith('TX_');
}
async dispatch(action) {
switch (action.type) {
case 'TX_SUBMIT':
return this.submitTransaction(action.payload);
case 'TX_UPDATE':
return this.updateTransaction(action.payload);
case 'TX_CONFIRM':
return this.confirmTransaction(action.payload);
default:
return;
}
}
submitTransaction(txData) {
const { hash, ...transaction } = txData;
const transactions = new Map(this.getState().transactions);
const pending = new Set(this.getState().pending);
transactions.set(hash, {
...transaction,
hash,
status: 'pending',
timestamp: Date.now()
});
pending.add(hash);
this.setState({ transactions, pending });
// 开始监听交易状态
this.watchTransaction(hash);
}
async watchTransaction(hash) {
const web3Store = this.eventBus.stateManager.getStore('web3');
const provider = web3Store.getState().provider;
if (!provider) return;
try {
const receipt = await provider.waitForTransaction(hash);
this.dispatch({
type: 'TX_CONFIRM',
payload: { hash, receipt }
});
} catch (error) {
this.dispatch({
type: 'TX_UPDATE',
payload: {
hash,
status: 'failed',
error: error.message
}
});
}
}
}
React集成Hook:
// 状态管理Hook
function useDAppState() {
const [stateManager] = useState(() => {
const manager = new DAppStateManager();
// 注册各种stores
manager.registerStore('web3', new Web3Store());
manager.registerStore('contract', new ContractStore());
manager.registerStore('transaction', new TransactionStore());
// 注册中间件
manager.use(async (action, stateManager) => {
console.log('Action dispatched:', action);
return action;
});
return manager;
});
return stateManager;
}
// 具体Store的Hook
function useWeb3Store() {
const stateManager = useDAppState();
const web3Store = stateManager.getStore('web3');
const [state, setState] = useState(web3Store.getState());
useEffect(() => {
const unsubscribe = web3Store.subscribe((newState) => {
setState(newState);
});
return unsubscribe;
}, [web3Store]);
const actions = {
connect: (provider) => stateManager.dispatch({
type: 'WEB3_CONNECT',
payload: provider
}),
disconnect: () => stateManager.dispatch({
type: 'WEB3_DISCONNECT'
})
};
return { state, actions };
}
// 使用示例
function DAppComponent() {
const { state: web3State, actions: web3Actions } = useWeb3Store();
const handleConnect = async () => {
try {
const provider = new ethers.providers.Web3Provider(window.ethereum);
await web3Actions.connect(provider);
} catch (error) {
console.error('连接失败:', error);
}
};
return (
<div>
{web3State.isConnected ? (
<div>已连接: {web3State.address}</div>
) : (
<button onClick={handleConnect}>连接钱包</button>
)}
</div>
);
}
架构优势:
How to implement offline functionality and data synchronization in DApp?
How to implement offline functionality and data synchronization in DApp?
考察点:离线功能设计。
答案:
DApp的离线功能设计需要在去中心化特性和用户体验之间找到平衡。由于区块链交互本质上需要网络连接,离线功能主要集中在数据缓存、交易队列和基础功能的本地实现上。
离线功能策略:
离线存储架构:
// 离线数据管理器
class OfflineDataManager {
constructor() {
this.dbName = 'dapp-offline-db';
this.version = 1;
this.db = null;
this.syncQueue = [];
this.isOnline = navigator.onLine;
this.setupEventListeners();
}
async initialize() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建对象存储
if (!db.objectStoreNames.contains('transactions')) {
const txStore = db.createObjectStore('transactions', {
keyPath: 'id',
autoIncrement: true
});
txStore.createIndex('hash', 'hash', { unique: false });
txStore.createIndex('status', 'status', { unique: false });
txStore.createIndex('timestamp', 'timestamp', { unique: false });
}
if (!db.objectStoreNames.contains('contractData')) {
const contractStore = db.createObjectStore('contractData', {
keyPath: 'key'
});
contractStore.createIndex('contractAddress', 'contractAddress', { unique: false });
contractStore.createIndex('timestamp', 'timestamp', { unique: false });
}
if (!db.objectStoreNames.contains('userPreferences')) {
db.createObjectStore('userPreferences', {
keyPath: 'key'
});
}
if (!db.objectStoreNames.contains('pendingActions')) {
const actionStore = db.createObjectStore('pendingActions', {
keyPath: 'id',
autoIncrement: true
});
actionStore.createIndex('type', 'type', { unique: false });
actionStore.createIndex('priority', 'priority', { unique: false });
}
};
});
}
setupEventListeners() {
window.addEventListener('online', () => {
this.isOnline = true;
this.onNetworkRestore();
});
window.addEventListener('offline', () => {
this.isOnline = false;
this.onNetworkLost();
});
}
// 缓存合约数据
async cacheContractData(key, data, contractAddress, ttl = 3600000) {
if (!this.db) return;
const transaction = this.db.transaction(['contractData'], 'readwrite');
const store = transaction.objectStore('contractData');
const cacheItem = {
key,
data,
contractAddress,
timestamp: Date.now(),
ttl,
expiresAt: Date.now() + ttl
};
await store.put(cacheItem);
}
// 获取缓存的合约数据
async getCachedContractData(key) {
if (!this.db) return null;
const transaction = this.db.transaction(['contractData'], 'readonly');
const store = transaction.objectStore('contractData');
return new Promise((resolve) => {
const request = store.get(key);
request.onsuccess = () => {
const result = request.result;
if (!result) {
resolve(null);
return;
}
// 检查是否过期
if (Date.now() > result.expiresAt) {
this.deleteCachedData('contractData', key);
resolve(null);
return;
}
resolve(result.data);
};
request.onerror = () => resolve(null);
});
}
// 添加离线交易到队列
async addPendingTransaction(transactionData) {
if (!this.db) return;
const transaction = this.db.transaction(['pendingActions'], 'readwrite');
const store = transaction.objectStore('pendingActions');
const pendingAction = {
type: 'TRANSACTION',
data: transactionData,
priority: transactionData.priority || 1,
timestamp: Date.now(),
retryCount: 0,
maxRetries: 3
};
const result = await store.add(pendingAction);
return result;
}
// 获取待处理的操作
async getPendingActions() {
if (!this.db) return [];
const transaction = this.db.transaction(['pendingActions'], 'readonly');
const store = transaction.objectStore('pendingActions');
const index = store.index('priority');
return new Promise((resolve) => {
const actions = [];
const request = index.openCursor(null, 'prev'); // 按优先级降序
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
actions.push(cursor.value);
cursor.continue();
} else {
resolve(actions);
}
};
request.onerror = () => resolve([]);
});
}
// 网络恢复时的处理
async onNetworkRestore() {
console.log('网络已恢复,开始同步数据...');
try {
// 处理待处理的操作
const pendingActions = await this.getPendingActions();
for (const action of pendingActions) {
try {
await this.processPendingAction(action);
await this.removePendingAction(action.id);
} catch (error) {
console.error('处理待处理操作失败:', error);
await this.incrementRetryCount(action.id);
}
}
// 触发数据同步事件
window.dispatchEvent(new CustomEvent('dapp-sync-start'));
} catch (error) {
console.error('数据同步失败:', error);
}
}
async processPendingAction(action) {
switch (action.type) {
case 'TRANSACTION':
return await this.submitPendingTransaction(action.data);
case 'CONTRACT_CALL':
return await this.executePendingContractCall(action.data);
default:
console.warn('未知的待处理操作类型:', action.type);
}
}
}
离线交易管理:
// 离线交易管理器
class OfflineTransactionManager {
constructor(offlineDataManager, web3Provider) {
this.offlineData = offlineDataManager;
this.provider = web3Provider;
this.queuedTransactions = new Map();
}
// 离线提交交易
async submitOfflineTransaction(transactionData) {
const {
to,
value,
data,
gasLimit,
gasPrice,
description,
priority = 1
} = transactionData;
// 生成本地交易ID
const localId = this.generateLocalTransactionId();
const offlineTx = {
localId,
to,
value: value?.toString(),
data,
gasLimit: gasLimit?.toString(),
gasPrice: gasPrice?.toString(),
description,
priority,
status: 'queued',
timestamp: Date.now(),
estimatedGasCost: gasLimit && gasPrice ?
gasLimit.mul(gasPrice).toString() : null
};
// 保存到离线存储
await this.offlineData.addPendingTransaction(offlineTx);
// 更新本地状态
this.queuedTransactions.set(localId, offlineTx);
// 触发UI更新事件
window.dispatchEvent(new CustomEvent('offline-transaction-queued', {
detail: offlineTx
}));
return localId;
}
// 网络恢复后提交交易
async submitPendingTransaction(txData) {
try {
// 重新获取最新的nonce和gas价格
const signer = this.provider.getSigner();
const nonce = await signer.getTransactionCount('pending');
const feeData = await this.provider.getFeeData();
const transaction = {
to: txData.to,
value: txData.value,
data: txData.data,
nonce,
gasLimit: txData.gasLimit,
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
};
const tx = await signer.sendTransaction(transaction);
// 更新交易状态
this.updateTransactionStatus(txData.localId, 'submitted', tx.hash);
return tx;
} catch (error) {
this.updateTransactionStatus(txData.localId, 'failed', null, error.message);
throw error;
}
}
generateLocalTransactionId() {
return `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
updateTransactionStatus(localId, status, hash = null, error = null) {
const tx = this.queuedTransactions.get(localId);
if (tx) {
tx.status = status;
tx.hash = hash;
tx.error = error;
tx.updatedAt = Date.now();
window.dispatchEvent(new CustomEvent('transaction-status-updated', {
detail: tx
}));
}
}
}
React离线功能Hook:
// 离线状态管理Hook
function useOfflineCapability() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
const [syncStatus, setSyncStatus] = useState('idle');
const [pendingActions, setPendingActions] = useState([]);
const [offlineDataManager] = useState(() => new OfflineDataManager());
useEffect(() => {
const handleOnline = () => {
setIsOnline(true);
setSyncStatus('syncing');
};
const handleOffline = () => {
setIsOnline(false);
setSyncStatus('offline');
};
const handleSyncStart = () => {
setSyncStatus('syncing');
};
const handleSyncComplete = () => {
setSyncStatus('idle');
};
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
window.addEventListener('dapp-sync-start', handleSyncStart);
window.addEventListener('dapp-sync-complete', handleSyncComplete);
// 初始化离线数据管理器
offlineDataManager.initialize();
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
window.removeEventListener('dapp-sync-start', handleSyncStart);
window.removeEventListener('dapp-sync-complete', handleSyncComplete);
};
}, [offlineDataManager]);
// 获取缓存数据
const getCachedData = useCallback(async (key) => {
return await offlineDataManager.getCachedContractData(key);
}, [offlineDataManager]);
// 缓存数据
const cacheData = useCallback(async (key, data, contractAddress, ttl) => {
return await offlineDataManager.cacheContractData(key, data, contractAddress, ttl);
}, [offlineDataManager]);
return {
isOnline,
syncStatus,
pendingActions,
getCachedData,
cacheData,
offlineDataManager
};
}
// 离线感知的数据获取Hook
function useOfflineAwareData(key, fetchFn, cacheOptions = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [fromCache, setFromCache] = useState(false);
const { isOnline, getCachedData, cacheData } = useOfflineCapability();
useEffect(() => {
const loadData = async () => {
setLoading(true);
setError(null);
try {
// 先尝试获取缓存数据
const cachedData = await getCachedData(key);
if (cachedData) {
setData(cachedData);
setFromCache(true);
setLoading(false);
// 如果在线,仍然尝试获取最新数据
if (!isOnline) {
return;
}
}
// 如果在线,获取最新数据
if (isOnline && fetchFn) {
const freshData = await fetchFn();
// 缓存新数据
if (freshData && cacheOptions.cache !== false) {
await cacheData(
key,
freshData,
cacheOptions.contractAddress,
cacheOptions.ttl
);
}
setData(freshData);
setFromCache(false);
}
} catch (err) {
setError(err);
// 如果获取失败但有缓存数据,使用缓存
const cachedData = await getCachedData(key);
if (cachedData) {
setData(cachedData);
setFromCache(true);
}
} finally {
setLoading(false);
}
};
loadData();
}, [key, isOnline, fetchFn, getCachedData, cacheData, cacheOptions]);
return { data, loading, error, fromCache, isOnline };
}
// 使用示例
function TokenBalance({ tokenAddress, userAddress }) {
const { data: balance, loading, fromCache, isOnline } = useOfflineAwareData(
`balance_${tokenAddress}_${userAddress}`,
async () => {
const contract = new ethers.Contract(tokenAddress, tokenABI, provider);
return await contract.balanceOf(userAddress);
},
{
contractAddress: tokenAddress,
ttl: 300000, // 5分钟缓存
cache: true
}
);
return (
<div className="token-balance">
{loading ? (
<span>加载中...</span>
) : (
<div>
<span>余额: {ethers.utils.formatEther(balance || 0)}</span>
{fromCache && (
<span className="cache-indicator">
📱 {isOnline ? '缓存数据' : '离线数据'}
</span>
)}
</div>
)}
</div>
);
}
离线功能最佳实践:
How to optimize DApp performance? Reduce the number of blockchain calls.
How to optimize DApp performance? Reduce the number of blockchain calls.
考察点:性能优化策略。
答案:
DApp性能优化的核心在于减少不必要的区块链调用,因为每次调用都涉及网络延迟和计算成本。优化策略需要从数据缓存、批量操作、智能合约设计和前端架构等多个维度进行。
性能优化策略:
缓存策略实现:
// 智能缓存管理器
class SmartCacheManager {
constructor() {
this.cache = new Map();
this.pendingRequests = new Map();
this.cacheConfig = new Map();
}
// 配置缓存策略
setCacheConfig(key, config) {
this.cacheConfig.set(key, {
ttl: config.ttl || 300000, // 默认5分钟
refreshThreshold: config.refreshThreshold || 0.8, // 80%时间后刷新
staleWhileRevalidate: config.staleWhileRevalidate || true,
maxAge: config.maxAge || 3600000 // 最长1小时
});
}
// 获取缓存数据
async get(key, fetcher, options = {}) {
const config = this.cacheConfig.get(key) || {};
const cacheItem = this.cache.get(key);
const now = Date.now();
// 检查缓存是否有效
if (cacheItem && !this.isExpired(cacheItem, config)) {
// 检查是否需要后台刷新
if (this.shouldRefresh(cacheItem, config)) {
this.backgroundRefresh(key, fetcher, config);
}
return cacheItem.data;
}
// 检查是否有正在进行的请求
if (this.pendingRequests.has(key)) {
return await this.pendingRequests.get(key);
}
// 创建新的请求
const fetchPromise = this.executeFetch(key, fetcher, config);
this.pendingRequests.set(key, fetchPromise);
try {
const result = await fetchPromise;
return result;
} finally {
this.pendingRequests.delete(key);
}
}
async executeFetch(key, fetcher, config) {
try {
const data = await fetcher();
// 缓存结果
this.cache.set(key, {
data,
timestamp: Date.now(),
accessCount: 1,
lastAccess: Date.now()
});
return data;
} catch (error) {
// 如果有过期但可用的缓存,返回它
const staleCache = this.cache.get(key);
if (staleCache && config.staleWhileRevalidate) {
console.warn(`使用过期缓存数据: ${key}`, error);
return staleCache.data;
}
throw error;
}
}
backgroundRefresh(key, fetcher, config) {
// 后台异步刷新缓存
this.executeFetch(key, fetcher, config).catch(error => {
console.warn(`后台缓存刷新失败: ${key}`, error);
});
}
isExpired(cacheItem, config) {
const age = Date.now() - cacheItem.timestamp;
return age > config.ttl;
}
shouldRefresh(cacheItem, config) {
const age = Date.now() - cacheItem.timestamp;
return age > (config.ttl * config.refreshThreshold);
}
// 清理过期缓存
cleanup() {
const now = Date.now();
for (const [key, item] of this.cache.entries()) {
const config = this.cacheConfig.get(key) || {};
const age = now - item.timestamp;
if (age > config.maxAge) {
this.cache.delete(key);
}
}
}
}
批量调用实现:
// Multicall批量调用管理器
class MulticallManager {
constructor(multicallAddress, provider) {
this.multicallAddress = multicallAddress;
this.provider = provider;
this.pendingCalls = [];
this.batchTimeout = null;
this.batchSize = 100;
this.batchDelay = 50; // 50ms延迟批处理
}
// 添加调用到批处理队列
async addCall(target, functionName, params = [], options = {}) {
return new Promise((resolve, reject) => {
const call = {
target,
functionName,
params,
options,
resolve,
reject,
timestamp: Date.now()
};
this.pendingCalls.push(call);
this.scheduleBatch();
});
}
scheduleBatch() {
if (this.batchTimeout) return;
this.batchTimeout = setTimeout(() => {
this.executeBatch();
this.batchTimeout = null;
}, this.batchDelay);
}
async executeBatch() {
if (this.pendingCalls.length === 0) return;
const calls = this.pendingCalls.splice(0, this.batchSize);
try {
// 构建Multicall参数
const multicallData = calls.map(call => ({
target: call.target,
callData: this.encodeCallData(call)
}));
// 执行批量调用
const multicall = new ethers.Contract(
this.multicallAddress,
MULTICALL_ABI,
this.provider
);
const results = await multicall.callStatic.aggregate(multicallData);
// 解析结果并分发给对应的Promise
calls.forEach((call, index) => {
try {
const result = this.decodeResult(call, results.returnData[index]);
call.resolve(result);
} catch (error) {
call.reject(error);
}
});
} catch (error) {
// 批量调用失败,逐个执行
this.fallbackToIndividualCalls(calls);
}
// 如果还有待处理的调用,继续处理
if (this.pendingCalls.length > 0) {
this.scheduleBatch();
}
}
async fallbackToIndividualCalls(calls) {
for (const call of calls) {
try {
const contract = new ethers.Contract(
call.target,
call.options.abi || [],
this.provider
);
const result = await contract[call.functionName](...call.params);
call.resolve(result);
} catch (error) {
call.reject(error);
}
}
}
encodeCallData(call) {
const iface = new ethers.utils.Interface(call.options.abi || []);
return iface.encodeFunctionData(call.functionName, call.params);
}
decodeResult(call, returnData) {
const iface = new ethers.utils.Interface(call.options.abi || []);
return iface.decodeFunctionResult(call.functionName, returnData);
}
}
React性能优化Hook:
// 优化的合约数据Hook
function useOptimizedContractData(contractAddress, abi, method, params = [], options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const cacheManager = useMemo(() => new SmartCacheManager(), []);
const multicallManager = useMemo(() => new MulticallManager(
MULTICALL_ADDRESS,
provider
), []);
// 生成缓存键
const cacheKey = useMemo(() => {
const paramsStr = JSON.stringify(params);
return `${contractAddress}_${method}_${paramsStr}`;
}, [contractAddress, method, params]);
// 配置缓存策略
useEffect(() => {
cacheManager.setCacheConfig(cacheKey, {
ttl: options.ttl || 300000,
refreshThreshold: 0.8,
staleWhileRevalidate: true
});
}, [cacheKey, options.ttl]);
// 数据获取函数
const fetchData = useCallback(async () => {
if (options.useBatch) {
// 使用批量调用
return await multicallManager.addCall(
contractAddress,
method,
params,
{ abi }
);
} else {
// 单独调用
const contract = new ethers.Contract(contractAddress, abi, provider);
return await contract[method](...params);
}
}, [contractAddress, method, params, options.useBatch]);
// 加载数据
useEffect(() => {
let mounted = true;
const loadData = async () => {
setLoading(true);
setError(null);
try {
const result = await cacheManager.get(cacheKey, fetchData);
if (mounted) {
setData(result);
}
} catch (err) {
if (mounted) {
setError(err);
}
} finally {
if (mounted) {
setLoading(false);
}
}
};
loadData();
return () => {
mounted = false;
};
}, [cacheKey, fetchData]);
// 手动刷新
const refresh = useCallback(async () => {
setLoading(true);
try {
const result = await fetchData();
setData(result);
return result;
} catch (err) {
setError(err);
throw err;
} finally {
setLoading(false);
}
}, [fetchData]);
return { data, loading, error, refresh };
}
// 批量数据获取Hook
function useBatchContractData(requests, options = {}) {
const [results, setResults] = useState({});
const [loading, setLoading] = useState(true);
const [errors, setErrors] = useState({});
useEffect(() => {
let mounted = true;
const loadBatchData = async () => {
setLoading(true);
const batchPromises = requests.map(async (request) => {
const { key, contractAddress, abi, method, params } = request;
try {
const contract = new ethers.Contract(contractAddress, abi, provider);
const result = await contract[method](...(params || []));
return { key, result, error: null };
} catch (error) {
return { key, result: null, error };
}
});
try {
const batchResults = await Promise.allSettled(batchPromises);
if (mounted) {
const newResults = {};
const newErrors = {};
batchResults.forEach((settledResult, index) => {
const request = requests[index];
if (settledResult.status === 'fulfilled') {
const { key, result, error } = settledResult.value;
newResults[key] = result;
if (error) newErrors[key] = error;
} else {
newErrors[request.key] = settledResult.reason;
}
});
setResults(newResults);
setErrors(newErrors);
}
} finally {
if (mounted) {
setLoading(false);
}
}
};
loadBatchData();
return () => {
mounted = false;
};
}, [JSON.stringify(requests)]);
return { results, loading, errors };
}
预加载策略:
// 数据预加载管理器
class DataPreloader {
constructor(cacheManager) {
this.cacheManager = cacheManager;
this.preloadQueue = [];
this.isPreloading = false;
}
// 添加预加载任务
addPreloadTask(key, fetcher, priority = 1) {
this.preloadQueue.push({
key,
fetcher,
priority,
timestamp: Date.now()
});
// 按优先级排序
this.preloadQueue.sort((a, b) => b.priority - a.priority);
if (!this.isPreloading) {
this.startPreloading();
}
}
async startPreloading() {
this.isPreloading = true;
while (this.preloadQueue.length > 0) {
const task = this.preloadQueue.shift();
try {
// 检查缓存中是否已存在
const existing = await this.cacheManager.get(task.key, null);
if (!existing) {
// 预加载数据
await this.cacheManager.get(task.key, task.fetcher);
}
} catch (error) {
console.warn(`预加载失败: ${task.key}`, error);
}
// 避免阻塞主线程
await new Promise(resolve => setTimeout(resolve, 10));
}
this.isPreloading = false;
}
// 预加载用户可能需要的数据
preloadUserData(userAddress, tokenAddresses = []) {
// 预加载ETH余额
this.addPreloadTask(
`eth_balance_${userAddress}`,
async () => {
return await provider.getBalance(userAddress);
},
10
);
// 预加载代币余额
tokenAddresses.forEach(tokenAddress => {
this.addPreloadTask(
`token_balance_${tokenAddress}_${userAddress}`,
async () => {
const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
return await contract.balanceOf(userAddress);
},
8
);
});
// 预加载交易历史
this.addPreloadTask(
`transaction_history_${userAddress}`,
async () => {
// 这里可以调用区块链浏览器API或子图
return await fetchTransactionHistory(userAddress);
},
5
);
}
}
性能监控和分析:
// 性能监控工具
class DAppPerformanceMonitor {
constructor() {
this.metrics = {
blockchainCalls: 0,
cacheHits: 0,
cacheMisses: 0,
averageResponseTime: 0,
slowQueries: []
};
}
trackBlockchainCall(method, startTime, endTime, fromCache = false) {
const duration = endTime - startTime;
this.metrics.blockchainCalls++;
if (fromCache) {
this.metrics.cacheHits++;
} else {
this.metrics.cacheMisses++;
}
// 更新平均响应时间
this.updateAverageResponseTime(duration);
// 记录慢查询
if (duration > 5000) { // 5秒以上的查询
this.metrics.slowQueries.push({
method,
duration,
timestamp: Date.now()
});
}
// 定期清理旧的慢查询记录
if (this.metrics.slowQueries.length > 100) {
this.metrics.slowQueries = this.metrics.slowQueries.slice(-50);
}
}
updateAverageResponseTime(newDuration) {
const totalCalls = this.metrics.blockchainCalls;
const currentAvg = this.metrics.averageResponseTime;
this.metrics.averageResponseTime =
(currentAvg * (totalCalls - 1) + newDuration) / totalCalls;
}
getPerformanceReport() {
const cacheHitRate = this.metrics.cacheHits /
(this.metrics.cacheHits + this.metrics.cacheMisses);
return {
...this.metrics,
cacheHitRate: (cacheHitRate * 100).toFixed(2) + '%',
averageResponseTime: this.metrics.averageResponseTime.toFixed(0) + 'ms'
};
}
}
最佳实践总结:
What is optimistic update? How to implement optimistic UI updates in DApp?
What is optimistic update? How to implement optimistic UI updates in DApp?
考察点:用户体验优化。
答案:
乐观更新是一种用户体验优化策略,即在等待服务器响应之前就先更新UI界面,假设操作会成功。在DApp中,由于区块链交易确认需要时间,乐观更新能显著改善用户体验,让界面响应更加流畅。
乐观更新应用场景:
乐观更新架构实现:
// 乐观更新管理器
class OptimisticUpdateManager {
constructor() {
this.optimisticStates = new Map();
this.pendingOperations = new Map();
this.rollbackHistory = new Map();
this.eventBus = new EventTarget();
}
// 执行乐观更新
async executeOptimisticUpdate(operationId, optimisticState, actualOperation, rollbackFn) {
try {
// 保存当前状态用于回滚
this.rollbackHistory.set(operationId, {
rollbackFn,
timestamp: Date.now()
});
// 立即应用乐观状态
this.applyOptimisticState(operationId, optimisticState);
// 执行实际操作
const result = await actualOperation();
// 操作成功,清理乐观状态
this.confirmOptimisticUpdate(operationId, result);
return result;
} catch (error) {
// 操作失败,回滚状态
this.rollbackOptimisticUpdate(operationId, error);
throw error;
}
}
applyOptimisticState(operationId, optimisticState) {
this.optimisticStates.set(operationId, {
state: optimisticState,
timestamp: Date.now(),
status: 'pending'
});
this.notifyStateChange(operationId, optimisticState, 'optimistic');
}
confirmOptimisticUpdate(operationId, actualResult) {
const optimisticData = this.optimisticStates.get(operationId);
if (optimisticData) {
optimisticData.status = 'confirmed';
optimisticData.actualResult = actualResult;
this.notifyStateChange(operationId, actualResult, 'confirmed');
// 清理资源
setTimeout(() => {
this.cleanup(operationId);
}, 5000);
}
}
rollbackOptimisticUpdate(operationId, error) {
const rollbackData = this.rollbackHistory.get(operationId);
if (rollbackData) {
// 执行回滚
rollbackData.rollbackFn();
this.notifyStateChange(operationId, null, 'rollback', error);
}
this.cleanup(operationId);
}
notifyStateChange(operationId, state, type, error = null) {
this.eventBus.dispatchEvent(new CustomEvent('optimisticStateChange', {
detail: {
operationId,
state,
type,
error,
timestamp: Date.now()
}
}));
}
cleanup(operationId) {
this.optimisticStates.delete(operationId);
this.rollbackHistory.delete(operationId);
this.pendingOperations.delete(operationId);
}
// 获取当前的乐观状态
getOptimisticState(operationId) {
return this.optimisticStates.get(operationId);
}
// 检查是否有待处理的乐观更新
hasPendingUpdates(filter = null) {
if (!filter) {
return this.optimisticStates.size > 0;
}
for (const [id, data] of this.optimisticStates.entries()) {
if (filter(id, data)) {
return true;
}
}
return false;
}
}
代币转账乐观更新实现:
// 代币转账乐观更新
class TokenTransferOptimistic {
constructor(tokenContract, optimisticManager) {
this.contract = tokenContract;
this.optimistic = optimisticManager;
this.balanceCache = new Map();
}
async optimisticTransfer(to, amount, userAddress) {
const operationId = `transfer_${Date.now()}_${Math.random()}`;
// 获取当前余额
const currentBalance = await this.getCurrentBalance(userAddress);
const currentRecipientBalance = await this.getCurrentBalance(to);
// 计算乐观状态
const optimisticState = {
type: 'TOKEN_TRANSFER',
sender: userAddress,
recipient: to,
amount,
senderNewBalance: currentBalance.sub(amount),
recipientNewBalance: currentRecipientBalance.add(amount),
timestamp: Date.now()
};
// 乐观更新函数
const applyOptimistic = () => {
this.balanceCache.set(userAddress, optimisticState.senderNewBalance);
this.balanceCache.set(to, optimisticState.recipientNewBalance);
};
// 回滚函数
const rollback = () => {
this.balanceCache.set(userAddress, currentBalance);
this.balanceCache.set(to, currentRecipientBalance);
};
// 实际转账操作
const actualTransfer = async () => {
const tx = await this.contract.transfer(to, amount);
// 等待交易确认
const receipt = await tx.wait();
// 获取实际的最新余额
const [actualSenderBalance, actualRecipientBalance] = await Promise.all([
this.contract.balanceOf(userAddress),
this.contract.balanceOf(to)
]);
return {
txHash: tx.hash,
receipt,
senderBalance: actualSenderBalance,
recipientBalance: actualRecipientBalance
};
};
// 立即应用乐观更新
applyOptimistic();
// 执行乐观更新
return await this.optimistic.executeOptimisticUpdate(
operationId,
optimisticState,
actualTransfer,
rollback
);
}
async getCurrentBalance(address) {
// 优先使用缓存的乐观余额
if (this.balanceCache.has(address)) {
return this.balanceCache.get(address);
}
// 从合约获取实际余额
const balance = await this.contract.balanceOf(address);
this.balanceCache.set(address, balance);
return balance;
}
// 清除地址的缓存余额
clearBalanceCache(address) {
this.balanceCache.delete(address);
}
}
React乐观更新Hook:
// 乐观更新Hook
function useOptimisticUpdate() {
const [optimisticManager] = useState(() => new OptimisticUpdateManager());
const [optimisticStates, setOptimisticStates] = useState(new Map());
useEffect(() => {
const handleStateChange = (event) => {
const { operationId, state, type, error } = event.detail;
setOptimisticStates(prev => {
const newStates = new Map(prev);
if (type === 'rollback') {
newStates.delete(operationId);
} else {
newStates.set(operationId, { state, type, error });
}
return newStates;
});
};
optimisticManager.eventBus.addEventListener('optimisticStateChange', handleStateChange);
return () => {
optimisticManager.eventBus.removeEventListener('optimisticStateChange', handleStateChange);
};
}, [optimisticManager]);
const executeOptimistic = useCallback(async (
optimisticState,
actualOperation,
rollbackFn,
operationId = null
) => {
const id = operationId || `op_${Date.now()}_${Math.random()}`;
return await optimisticManager.executeOptimisticUpdate(
id,
optimisticState,
actualOperation,
rollbackFn
);
}, [optimisticManager]);
return {
optimisticStates,
executeOptimistic,
optimisticManager
};
}
// 代币余额乐观更新Hook
function useOptimisticTokenBalance(tokenContract, address) {
const [actualBalance, setActualBalance] = useState(null);
const [displayBalance, setDisplayBalance] = useState(null);
const { optimisticStates } = useOptimisticUpdate();
const [loading, setLoading] = useState(true);
// 获取实际余额
useEffect(() => {
if (!tokenContract || !address) return;
const fetchBalance = async () => {
try {
const balance = await tokenContract.balanceOf(address);
setActualBalance(balance);
setDisplayBalance(balance);
} catch (error) {
console.error('获取余额失败:', error);
} finally {
setLoading(false);
}
};
fetchBalance();
// 监听代币转账事件
const handleTransfer = (from, to, amount) => {
if (from === address || to === address) {
fetchBalance();
}
};
tokenContract.on('Transfer', handleTransfer);
return () => {
tokenContract.off('Transfer', handleTransfer);
};
}, [tokenContract, address]);
// 应用乐观更新
useEffect(() => {
let calculatedBalance = actualBalance;
// 应用所有相关的乐观更新
for (const [operationId, optimisticData] of optimisticStates.entries()) {
const { state, type } = optimisticData;
if (type === 'optimistic' && state.type === 'TOKEN_TRANSFER') {
if (state.sender === address) {
calculatedBalance = state.senderNewBalance;
} else if (state.recipient === address) {
calculatedBalance = state.recipientNewBalance;
}
}
}
setDisplayBalance(calculatedBalance);
}, [actualBalance, optimisticStates, address]);
return {
balance: displayBalance,
actualBalance,
loading,
isPending: optimisticStates.size > 0
};
}
// 乐观转账组件示例
function OptimisticTransferForm({ tokenContract, userAddress }) {
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const [transferring, setTransferring] = useState(false);
const { executeOptimistic } = useOptimisticUpdate();
const { balance, isPending } = useOptimisticTokenBalance(tokenContract, userAddress);
const handleTransfer = async () => {
if (!recipient || !amount) return;
setTransferring(true);
try {
const transferAmount = ethers.utils.parseEther(amount);
const currentBalance = await tokenContract.balanceOf(userAddress);
const recipientCurrentBalance = await tokenContract.balanceOf(recipient);
await executeOptimistic(
// 乐观状态
{
type: 'TOKEN_TRANSFER',
sender: userAddress,
recipient,
amount: transferAmount,
senderNewBalance: currentBalance.sub(transferAmount),
recipientNewBalance: recipientCurrentBalance.add(transferAmount)
},
// 实际操作
async () => {
const tx = await tokenContract.transfer(recipient, transferAmount);
return await tx.wait();
},
// 回滚函数
() => {
console.log('转账失败,已回滚状态');
}
);
// 清空表单
setRecipient('');
setAmount('');
} catch (error) {
console.error('转账失败:', error);
} finally {
setTransferring(false);
}
};
return (
<div className="transfer-form">
<div className="balance-display">
<span>余额: {ethers.utils.formatEther(balance || 0)} TOKEN</span>
{isPending && <span className="pending-indicator">⏳ 交易处理中</span>}
</div>
<input
type="text"
placeholder="接收地址"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
/>
<input
type="text"
placeholder="转账数量"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<button
onClick={handleTransfer}
disabled={transferring || !recipient || !amount}
>
{transferring ? '转账中...' : '转账'}
</button>
</div>
);
}
乐观更新最佳实践:
How to handle complex asynchronous transaction workflows in DApp?
How to handle complex asynchronous transaction workflows in DApp?
考察点:复杂流程设计。
答案:
复杂的异步交易流程涉及多个步骤的交易序列,如DeFi协议交互、多签钱包操作、跨链交易等。需要设计状态机来管理流程状态,实现错误恢复和用户友好的进度展示。
流程管理架构:
// 交易工作流管理器
class TransactionWorkflowManager {
constructor() {
this.workflows = new Map();
this.stepDefinitions = new Map();
this.eventBus = new EventTarget();
}
// 定义工作流步骤
defineStep(stepId, stepConfig) {
this.stepDefinitions.set(stepId, {
id: stepId,
execute: stepConfig.execute,
validate: stepConfig.validate,
rollback: stepConfig.rollback,
retry: stepConfig.retry || 3,
timeout: stepConfig.timeout || 300000,
dependencies: stepConfig.dependencies || [],
description: stepConfig.description
});
}
// 创建工作流
createWorkflow(workflowId, steps, options = {}) {
const workflow = {
id: workflowId,
steps: steps.map(stepId => ({
id: stepId,
status: 'pending',
result: null,
error: null,
attempts: 0,
startTime: null,
endTime: null
})),
status: 'initialized',
currentStepIndex: 0,
options,
metadata: {},
startTime: Date.now(),
endTime: null
};
this.workflows.set(workflowId, workflow);
return workflow;
}
// 执行工作流
async executeWorkflow(workflowId, context = {}) {
const workflow = this.workflows.get(workflowId);
if (!workflow) {
throw new Error(`Workflow ${workflowId} not found`);
}
workflow.status = 'running';
this.emitEvent('workflowStarted', { workflowId, workflow });
try {
for (let i = workflow.currentStepIndex; i < workflow.steps.length; i++) {
const step = workflow.steps[i];
workflow.currentStepIndex = i;
await this.executeStep(workflowId, step, context);
// 检查是否需要暂停
if (workflow.status === 'paused') {
return workflow;
}
}
workflow.status = 'completed';
workflow.endTime = Date.now();
this.emitEvent('workflowCompleted', { workflowId, workflow });
return workflow;
} catch (error) {
workflow.status = 'failed';
workflow.endTime = Date.now();
workflow.error = error;
this.emitEvent('workflowFailed', { workflowId, workflow, error });
throw error;
}
}
async executeStep(workflowId, step, context) {
const stepDef = this.stepDefinitions.get(step.id);
if (!stepDef) {
throw new Error(`Step definition ${step.id} not found`);
}
step.status = 'running';
step.startTime = Date.now();
step.attempts++;
this.emitEvent('stepStarted', { workflowId, step });
try {
// 验证依赖
await this.validateDependencies(workflowId, stepDef.dependencies, context);
// 执行步骤
const result = await Promise.race([
stepDef.execute(context, step),
new Promise((_, reject) => {
setTimeout(() => reject(new Error('Step timeout')), stepDef.timeout);
})
]);
step.status = 'completed';
step.result = result;
step.endTime = Date.now();
this.emitEvent('stepCompleted', { workflowId, step, result });
return result;
} catch (error) {
step.status = 'failed';
step.error = error;
step.endTime = Date.now();
this.emitEvent('stepFailed', { workflowId, step, error });
// 重试逻辑
if (step.attempts < stepDef.retry) {
console.log(`重试步骤 ${step.id}, 尝试次数: ${step.attempts}/${stepDef.retry}`);
// 指数退避延迟
const delay = Math.pow(2, step.attempts - 1) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
return await this.executeStep(workflowId, step, context);
}
throw error;
}
}
async validateDependencies(workflowId, dependencies, context) {
const workflow = this.workflows.get(workflowId);
for (const depId of dependencies) {
const depStep = workflow.steps.find(s => s.id === depId);
if (!depStep || depStep.status !== 'completed') {
throw new Error(`Dependency ${depId} not satisfied`);
}
}
}
// 暂停工作流
pauseWorkflow(workflowId) {
const workflow = this.workflows.get(workflowId);
if (workflow && workflow.status === 'running') {
workflow.status = 'paused';
this.emitEvent('workflowPaused', { workflowId, workflow });
}
}
// 恢复工作流
async resumeWorkflow(workflowId, context = {}) {
const workflow = this.workflows.get(workflowId);
if (workflow && workflow.status === 'paused') {
workflow.status = 'running';
this.emitEvent('workflowResumed', { workflowId, workflow });
return await this.executeWorkflow(workflowId, context);
}
}
emitEvent(eventType, data) {
this.eventBus.dispatchEvent(new CustomEvent(eventType, { detail: data }));
}
}
DeFi交易流程示例:
// DeFi Swap交易流程
class DeFiSwapWorkflow {
constructor(workflowManager, swapRouter, tokenA, tokenB) {
this.workflowManager = workflowManager;
this.swapRouter = swapRouter;
this.tokenA = tokenA;
this.tokenB = tokenB;
this.setupSteps();
}
setupSteps() {
// 步骤1: 检查余额
this.workflowManager.defineStep('checkBalance', {
description: '检查代币余额',
execute: async (context) => {
const balance = await this.tokenA.balanceOf(context.userAddress);
if (balance.lt(context.amountIn)) {
throw new Error('余额不足');
}
return { balance, sufficient: true };
},
validate: (context) => context.userAddress && context.amountIn
});
// 步骤2: 检查授权
this.workflowManager.defineStep('checkAllowance', {
description: '检查代币授权',
dependencies: ['checkBalance'],
execute: async (context) => {
const allowance = await this.tokenA.allowance(
context.userAddress,
this.swapRouter.address
);
return {
allowance,
needsApproval: allowance.lt(context.amountIn)
};
}
});
// 步骤3: 授权代币
this.workflowManager.defineStep('approveToken', {
description: '授权代币',
dependencies: ['checkAllowance'],
execute: async (context, step) => {
const prevStep = this.getStepResult(context.workflowId, 'checkAllowance');
if (!prevStep.needsApproval) {
return { skipped: true, reason: '无需授权' };
}
const tx = await this.tokenA.approve(
this.swapRouter.address,
ethers.constants.MaxUint256
);
const receipt = await tx.wait();
return { txHash: tx.hash, receipt };
},
timeout: 300000
});
// 步骤4: 获取交易路径和价格
this.workflowManager.defineStep('getQuote', {
description: '获取交易报价',
dependencies: ['checkBalance'],
execute: async (context) => {
const path = [this.tokenA.address, this.tokenB.address];
const amounts = await this.swapRouter.getAmountsOut(
context.amountIn,
path
);
return {
path,
amountOut: amounts[amounts.length - 1],
priceImpact: this.calculatePriceImpact(context.amountIn, amounts[1])
};
}
});
// 步骤5: 执行交换
this.workflowManager.defineStep('executeSwap', {
description: '执行代币交换',
dependencies: ['approveToken', 'getQuote'],
execute: async (context) => {
const quote = this.getStepResult(context.workflowId, 'getQuote');
const minAmountOut = quote.amountOut.mul(95).div(100); // 5% slippage
const tx = await this.swapRouter.swapExactTokensForTokens(
context.amountIn,
minAmountOut,
quote.path,
context.userAddress,
Math.floor(Date.now() / 1000) + 1800 // 30分钟超时
);
const receipt = await tx.wait();
return { txHash: tx.hash, receipt, actualAmountOut: null };
},
timeout: 600000
});
// 步骤6: 验证结果
this.workflowManager.defineStep('verifyResult', {
description: '验证交易结果',
dependencies: ['executeSwap'],
execute: async (context) => {
const swapResult = this.getStepResult(context.workflowId, 'executeSwap');
const balanceAfter = await this.tokenB.balanceOf(context.userAddress);
return {
balanceAfter,
transactionHash: swapResult.txHash,
success: true
};
}
});
}
// 启动交换流程
async startSwap(userAddress, amountIn) {
const workflowId = `swap_${Date.now()}_${Math.random()}`;
const workflow = this.workflowManager.createWorkflow(workflowId, [
'checkBalance',
'checkAllowance',
'approveToken',
'getQuote',
'executeSwap',
'verifyResult'
]);
const context = {
workflowId,
userAddress,
amountIn,
tokenA: this.tokenA.address,
tokenB: this.tokenB.address
};
return await this.workflowManager.executeWorkflow(workflowId, context);
}
getStepResult(workflowId, stepId) {
const workflow = this.workflowManager.workflows.get(workflowId);
const step = workflow.steps.find(s => s.id === stepId);
return step ? step.result : null;
}
calculatePriceImpact(amountIn, amountOut) {
// 简化的价格影响计算
// 实际应用中需要更复杂的计算逻辑
return 0.01; // 1%
}
}
React工作流组件:
// 工作流进度组件
function WorkflowProgress({ workflowId, workflowManager }) {
const [workflow, setWorkflow] = useState(null);
const [currentStep, setCurrentStep] = useState(null);
useEffect(() => {
const workflow = workflowManager.workflows.get(workflowId);
setWorkflow(workflow);
const handleWorkflowEvent = (event) => {
const { workflowId: eventWorkflowId } = event.detail;
if (eventWorkflowId === workflowId) {
const updatedWorkflow = workflowManager.workflows.get(workflowId);
setWorkflow({ ...updatedWorkflow });
if (event.type === 'stepStarted' || event.type === 'stepCompleted') {
setCurrentStep(event.detail.step);
}
}
};
const events = [
'workflowStarted', 'workflowCompleted', 'workflowFailed',
'stepStarted', 'stepCompleted', 'stepFailed'
];
events.forEach(eventType => {
workflowManager.eventBus.addEventListener(eventType, handleWorkflowEvent);
});
return () => {
events.forEach(eventType => {
workflowManager.eventBus.removeEventListener(eventType, handleWorkflowEvent);
});
};
}, [workflowId, workflowManager]);
if (!workflow) return <div>工作流不存在</div>;
const completedSteps = workflow.steps.filter(s => s.status === 'completed').length;
const totalSteps = workflow.steps.length;
const progress = (completedSteps / totalSteps) * 100;
return (
<div className="workflow-progress">
<div className="progress-header">
<h3>交易进度</h3>
<span className="progress-text">
{completedSteps}/{totalSteps} 步骤完成
</span>
</div>
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${progress}%` }}
/>
</div>
<div className="steps-list">
{workflow.steps.map((step, index) => {
const stepDef = workflowManager.stepDefinitions.get(step.id);
return (
<div
key={step.id}
className={`step-item ${step.status}`}
>
<div className="step-icon">
{step.status === 'completed' && '✅'}
{step.status === 'running' && '⏳'}
{step.status === 'failed' && '❌'}
{step.status === 'pending' && '⏸️'}
</div>
<div className="step-content">
<div className="step-title">{stepDef?.description || step.id}</div>
{step.status === 'running' && (
<div className="step-status">执行中...</div>
)}
{step.status === 'failed' && (
<div className="step-error">
失败: {step.error?.message}
{step.attempts < 3 && <span> (将重试)</span>}
</div>
)}
{step.status === 'completed' && step.result && (
<div className="step-result">
{step.result.txHash && (
<a
href={`https://etherscan.io/tx/${step.result.txHash}`}
target="_blank"
rel="noopener noreferrer"
>
查看交易
</a>
)}
</div>
)}
</div>
</div>
);
})}
</div>
{workflow.status === 'failed' && (
<div className="workflow-actions">
<button onClick={() => workflowManager.resumeWorkflow(workflowId)}>
重试失败步骤
</button>
</div>
)}
</div>
);
}
// 使用示例
function SwapInterface() {
const [workflowId, setWorkflowId] = useState(null);
const [swapAmount, setSwapAmount] = useState('');
const workflowManager = useMemo(() => new TransactionWorkflowManager(), []);
const swapWorkflow = useMemo(() =>
new DeFiSwapWorkflow(workflowManager, swapRouter, tokenA, tokenB),
[workflowManager]
);
const handleSwap = async () => {
try {
const workflow = await swapWorkflow.startSwap(
userAddress,
ethers.utils.parseEther(swapAmount)
);
setWorkflowId(workflow.id);
} catch (error) {
console.error('交换失败:', error);
}
};
return (
<div className="swap-interface">
<div className="swap-form">
<input
type="text"
value={swapAmount}
onChange={(e) => setSwapAmount(e.target.value)}
placeholder="交换数量"
/>
<button onClick={handleSwap}>开始交换</button>
</div>
{workflowId && (
<WorkflowProgress
workflowId={workflowId}
workflowManager={workflowManager}
/>
)}
</div>
);
}
最佳实践:
How to design error recovery mechanisms for DApp? Handle transaction failure scenarios.
How to design error recovery mechanisms for DApp? Handle transaction failure scenarios.
考察点:错误恢复策略。
答案:
DApp错误恢复机制需要处理网络故障、交易失败、Gas费不足等多种异常情况。设计应包括自动重试、状态回滚、用户引导和数据恢复等策略。
错误恢复策略:
实现示例:
class ErrorRecoveryManager {
constructor() {
this.retryStrategies = new Map();
this.fallbackActions = new Map();
this.recoveryHistory = [];
}
async executeWithRecovery(operation, options = {}) {
const { maxRetries = 3, backoffBase = 1000, fallback = null } = options;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
const errorType = this.classifyError(error);
if (attempt === maxRetries || !this.isRecoverable(errorType)) {
if (fallback) {
return await fallback(error);
}
throw error;
}
await this.delay(backoffBase * Math.pow(2, attempt - 1));
}
}
}
classifyError(error) {
if (error.code === 4001) return 'USER_REJECTED';
if (error.message.includes('insufficient funds')) return 'INSUFFICIENT_FUNDS';
if (error.message.includes('network')) return 'NETWORK_ERROR';
return 'UNKNOWN_ERROR';
}
isRecoverable(errorType) {
return ['NETWORK_ERROR', 'RATE_LIMIT'].includes(errorType);
}
}
What is event-driven architecture? How to apply event patterns in DApp?
What is event-driven architecture? How to apply event patterns in DApp?
考察点:架构模式应用。
How to implement real-time data updates in DApp? Handle on-chain state changes.
How to implement real-time data updates in DApp? Handle on-chain state changes.
考察点:实时数据同步。
答案:
DApp中的实时数据更新主要通过事件监听、轮询和WebSocket连接实现。需要处理区块链事件、合约状态变化和网络延迟,确保数据的实时性和准确性。
实时更新策略:
实时数据管理实现:
// 实时数据管理器
class RealTimeDataManager {
constructor(provider) {
this.provider = provider;
this.subscriptions = new Map();
this.eventFilters = new Map();
this.pollingIntervals = new Map();
this.eventBus = new EventTarget();
this.isConnected = false;
}
// 订阅合约事件
subscribeToContractEvents(contractAddress, abi, eventName, filter = {}) {
const contract = new ethers.Contract(contractAddress, abi, this.provider);
const subscriptionId = `${contractAddress}_${eventName}_${JSON.stringify(filter)}`;
// 创建事件过滤器
const eventFilter = contract.filters[eventName](...Object.values(filter));
// 监听事件
contract.on(eventFilter, (...args) => {
const event = args[args.length - 1]; // 最后一个参数是事件对象
this.handleContractEvent({
subscriptionId,
contractAddress,
eventName,
args: args.slice(0, -1),
event,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash
});
});
this.subscriptions.set(subscriptionId, {
type: 'CONTRACT_EVENT',
contract,
eventFilter,
eventName,
filter
});
return subscriptionId;
}
// 订阅区块事件
subscribeToBlocks(callback) {
const subscriptionId = 'NEW_BLOCKS';
this.provider.on('block', async (blockNumber) => {
try {
const block = await this.provider.getBlock(blockNumber);
this.eventBus.dispatchEvent(new CustomEvent('newBlock', {
detail: { block, blockNumber }
}));
if (callback) callback(block);
} catch (error) {
console.error('获取区块信息失败:', error);
}
});
this.subscriptions.set(subscriptionId, {
type: 'BLOCK_EVENT',
callback
});
return subscriptionId;
}
// 设置数据轮询
setupPolling(key, dataFetcher, interval = 30000) {
// 清除现有的轮询
if (this.pollingIntervals.has(key)) {
clearInterval(this.pollingIntervals.get(key));
}
const pollData = async () => {
try {
const data = await dataFetcher();
this.eventBus.dispatchEvent(new CustomEvent('dataPolled', {
detail: { key, data, timestamp: Date.now() }
}));
} catch (error) {
console.error(`轮询数据失败 ${key}:`, error);
}
};
// 立即执行一次
pollData();
// 设置定时轮询
const intervalId = setInterval(pollData, interval);
this.pollingIntervals.set(key, intervalId);
return key;
}
// 处理合约事件
handleContractEvent(eventData) {
// 事件去重处理
const eventKey = `${eventData.transactionHash}_${eventData.event.logIndex}`;
if (this.processedEvents?.has(eventKey)) {
return; // 已处理过的事件
}
this.processedEvents = this.processedEvents || new Set();
this.processedEvents.add(eventKey);
// 发出事件
this.eventBus.dispatchEvent(new CustomEvent('contractEvent', {
detail: eventData
}));
// 清理旧事件记录(保留最近1000个)
if (this.processedEvents.size > 1000) {
const eventsArray = Array.from(this.processedEvents);
this.processedEvents = new Set(eventsArray.slice(-500));
}
}
// 取消订阅
unsubscribe(subscriptionId) {
const subscription = this.subscriptions.get(subscriptionId);
if (subscription) {
switch (subscription.type) {
case 'CONTRACT_EVENT':
subscription.contract.removeAllListeners(subscription.eventFilter);
break;
case 'BLOCK_EVENT':
this.provider.removeAllListeners('block');
break;
}
this.subscriptions.delete(subscriptionId);
}
// 清除轮询
if (this.pollingIntervals.has(subscriptionId)) {
clearInterval(this.pollingIntervals.get(subscriptionId));
this.pollingIntervals.delete(subscriptionId);
}
}
// 清理所有订阅
cleanup() {
this.subscriptions.forEach((_, id) => this.unsubscribe(id));
this.pollingIntervals.forEach(intervalId => clearInterval(intervalId));
this.subscriptions.clear();
this.pollingIntervals.clear();
}
}
React实时数据Hook:
// 实时合约数据Hook
function useRealTimeContractData(
contractAddress,
abi,
method,
params = [],
events = [],
options = {}
) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [lastUpdated, setLastUpdated] = useState(null);
const dataManager = useMemo(() => new RealTimeDataManager(provider), []);
// 数据获取函数
const fetchData = useCallback(async () => {
try {
const contract = new ethers.Contract(contractAddress, abi, provider);
const result = await contract[method](...params);
return result;
} catch (error) {
console.error('获取合约数据失败:', error);
throw error;
}
}, [contractAddress, method, JSON.stringify(params)]);
// 初始数据加载
useEffect(() => {
let mounted = true;
const loadInitialData = async () => {
try {
const result = await fetchData();
if (mounted) {
setData(result);
setLastUpdated(Date.now());
}
} catch (error) {
console.error('初始数据加载失败:', error);
} finally {
if (mounted) setLoading(false);
}
};
loadInitialData();
return () => {
mounted = false;
};
}, [fetchData]);
// 设置事件监听
useEffect(() => {
const subscriptionIds = [];
// 监听相关合约事件
events.forEach(eventName => {
const subscriptionId = dataManager.subscribeToContractEvents(
contractAddress,
abi,
eventName
);
subscriptionIds.push(subscriptionId);
});
// 监听事件并刷新数据
const handleContractEvent = async (event) => {
const { contractAddress: eventContract } = event.detail;
if (eventContract.toLowerCase() === contractAddress.toLowerCase()) {
try {
const newData = await fetchData();
setData(newData);
setLastUpdated(Date.now());
} catch (error) {
console.error('事件触发的数据刷新失败:', error);
}
}
};
dataManager.eventBus.addEventListener('contractEvent', handleContractEvent);
// 设置轮询(可选)
if (options.polling && options.pollingInterval) {
const pollingId = dataManager.setupPolling(
`${contractAddress}_${method}`,
async () => {
const newData = await fetchData();
setData(newData);
setLastUpdated(Date.now());
return newData;
},
options.pollingInterval
);
subscriptionIds.push(pollingId);
}
return () => {
subscriptionIds.forEach(id => dataManager.unsubscribe(id));
dataManager.eventBus.removeEventListener('contractEvent', handleContractEvent);
};
}, [contractAddress, fetchData, events, options]);
// 手动刷新
const refresh = useCallback(async () => {
setLoading(true);
try {
const newData = await fetchData();
setData(newData);
setLastUpdated(Date.now());
return newData;
} catch (error) {
console.error('手动刷新失败:', error);
throw error;
} finally {
setLoading(false);
}
}, [fetchData]);
return {
data,
loading,
lastUpdated,
refresh
};
}
// 实时余额Hook
function useRealTimeBalance(tokenAddress, userAddress) {
return useRealTimeContractData(
tokenAddress,
ERC20_ABI,
'balanceOf',
[userAddress],
['Transfer'], // 监听Transfer事件
{
polling: true,
pollingInterval: 30000 // 30秒轮询
}
);
}
// 实时价格Hook
function useRealTimePrice(tokenAddress, baseToken = WETH_ADDRESS) {
const [price, setPrice] = useState(null);
const [priceChange24h, setPriceChange24h] = useState(null);
useEffect(() => {
let ws;
let reconnectTimeout;
const connectWebSocket = () => {
// 连接价格数据WebSocket
ws = new WebSocket('wss://api.dexscreener.com/ws');
ws.onopen = () => {
console.log('价格WebSocket已连接');
// 订阅价格数据
ws.send(JSON.stringify({
type: 'subscribe',
token: tokenAddress,
base: baseToken
}));
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'priceUpdate') {
setPrice(data.price);
setPriceChange24h(data.change24h);
}
} catch (error) {
console.error('价格数据解析失败:', error);
}
};
ws.onclose = () => {
console.log('价格WebSocket已断开');
// 5秒后重连
reconnectTimeout = setTimeout(connectWebSocket, 5000);
};
ws.onerror = (error) => {
console.error('价格WebSocket错误:', error);
};
};
connectWebSocket();
return () => {
if (ws) {
ws.close();
}
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
}
};
}, [tokenAddress, baseToken]);
return { price, priceChange24h };
}
// 使用示例
function TokenDashboard({ tokenAddress, userAddress }) {
const { data: balance, loading: balanceLoading, lastUpdated } = useRealTimeBalance(
tokenAddress,
userAddress
);
const { price, priceChange24h } = useRealTimePrice(tokenAddress);
const formatTimestamp = (timestamp) => {
return new Date(timestamp).toLocaleTimeString();
};
return (
<div className="token-dashboard">
<div className="balance-section">
<h3>代币余额</h3>
{balanceLoading ? (
<div>加载中...</div>
) : (
<div>
<div className="balance-amount">
{ethers.utils.formatEther(balance || 0)} TOKEN
</div>
{lastUpdated && (
<div className="last-updated">
最后更新: {formatTimestamp(lastUpdated)}
</div>
)}
</div>
)}
</div>
<div className="price-section">
<h3>实时价格</h3>
{price ? (
<div>
<div className="price-amount">${price}</div>
<div className={`price-change ${priceChange24h >= 0 ? 'positive' : 'negative'}`}>
24h: {priceChange24h >= 0 ? '+' : ''}{priceChange24h?.toFixed(2)}%
</div>
</div>
) : (
<div>价格加载中...</div>
)}
</div>
</div>
);
}
性能优化技巧:
How to design frontend architecture for cross-chain DApp?
How to design frontend architecture for cross-chain DApp?
考察点:跨链应用设计。
How should caching strategy be designed in DApp? Balance performance and data consistency.
How should caching strategy be designed in DApp? Balance performance and data consistency.
考察点:缓存设计策略。
How to implement efficient batch operations in DApp?
How to implement efficient batch operations in DApp?
考察点:批量操作优化。
答案:
DApp中的批量操作主要通过Multicall合约、交易打包和并发处理实现。可以显著减少交易数量和Gas消耗,提高操作效率。
批量操作策略:
实现示例:
class BatchOperationManager {
constructor(multicallContract) {
this.multicall = multicallContract;
this.batchQueue = [];
this.processing = false;
}
addToBatch(target, calldata, options = {}) {
this.batchQueue.push({
target,
calldata,
options,
timestamp: Date.now()
});
if (this.batchQueue.length >= 10 || options.urgent) {
this.processBatch();
}
}
async processBatch() {
if (this.processing || this.batchQueue.length === 0) return;
this.processing = true;
const calls = this.batchQueue.splice(0, 50); // 最多50个调用
try {
const multicallData = calls.map(call => ({
target: call.target,
callData: call.calldata
}));
const tx = await this.multicall.aggregate(multicallData);
const receipt = await tx.wait();
return { success: true, receipt, callCount: calls.length };
} catch (error) {
console.error('批量操作失败:', error);
throw error;
} finally {
this.processing = false;
}
}
}
What is MEV protection? How to implement transaction protection in DApp frontend?
What is MEV protection? How to implement transaction protection in DApp frontend?
考察点:交易保护机制。
How should testing strategy be designed for DApp? How to simulate blockchain environment?
How should testing strategy be designed for DApp? How to simulate blockchain environment?
考察点:测试策略设计。
答案:
DApp测试策略需要覆盖前端组件、合约交互、网络层和端到端流程。由于涉及区块链交互,需要设计多层次的测试环境,包括本地模拟、测试网络和主网分叉测试。
测试策略层次:
区块链环境模拟:
// 区块链环境模拟器
class BlockchainTestEnvironment {
constructor() {
this.providers = new Map();
this.contracts = new Map();
this.accounts = [];
this.snapshots = new Map();
}
// 设置本地测试网络
async setupLocalNetwork() {
// 使用Hardhat本地网络
const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
// 获取测试账户
const accounts = await provider.listAccounts();
this.accounts = accounts.map(address => ({
address,
signer: provider.getSigner(address)
}));
this.providers.set('local', provider);
return provider;
}
// 设置主网分叉
async setupMainnetFork(blockNumber = 'latest') {
const provider = new ethers.providers.JsonRpcProvider({
url: 'http://localhost:8545',
timeout: 60000
});
// 分叉主网到指定区块
await provider.send('hardhat_reset', [{
forking: {
jsonRpcUrl: process.env.MAINNET_RPC_URL,
blockNumber: blockNumber === 'latest' ? undefined : blockNumber
}
}]);
this.providers.set('mainnet-fork', provider);
return provider;
}
// 部署测试合约
async deployTestContracts(contractConfigs, provider) {
const deployedContracts = {};
for (const config of contractConfigs) {
const { name, contractFactory, constructorArgs = [] } = config;
try {
const signer = provider.getSigner();
const contract = await contractFactory.connect(signer).deploy(...constructorArgs);
await contract.deployed();
deployedContracts[name] = {
contract,
address: contract.address,
abi: contract.interface.fragments
};
this.contracts.set(name, deployedContracts[name]);
} catch (error) {
console.error(`部署合约 ${name} 失败:`, error);
throw error;
}
}
return deployedContracts;
}
// 创建快照
async createSnapshot(provider, snapshotId = null) {
const id = snapshotId || `snapshot_${Date.now()}`;
try {
const snapshotResult = await provider.send('evm_snapshot', []);
this.snapshots.set(id, snapshotResult);
return { id, snapshotId: snapshotResult };
} catch (error) {
console.error('创建快照失败:', error);
throw error;
}
}
// 恢复快照
async restoreSnapshot(provider, snapshotId) {
const snapshot = this.snapshots.get(snapshotId);
if (!snapshot) {
throw new Error(`快照 ${snapshotId} 不存在`);
}
try {
await provider.send('evm_revert', [snapshot]);
// 创建新快照以便后续使用
const newSnapshot = await provider.send('evm_snapshot', []);
this.snapshots.set(snapshotId, newSnapshot);
} catch (error) {
console.error('恢复快照失败:', error);
throw error;
}
}
// 时间操作
async increaseTime(provider, seconds) {
await provider.send('evm_increaseTime', [seconds]);
await provider.send('evm_mine', []);
}
async mineBlocks(provider, blocks) {
for (let i = 0; i < blocks; i++) {
await provider.send('evm_mine', []);
}
}
// 设置账户余额
async setBalance(provider, address, balance) {
await provider.send('hardhat_setBalance', [
address,
ethers.utils.hexValue(balance)
]);
}
// 模拟账户
async impersonateAccount(provider, address) {
await provider.send('hardhat_impersonateAccount', [address]);
return provider.getSigner(address);
}
// 清理环境
async cleanup() {
this.providers.clear();
this.contracts.clear();
this.snapshots.clear();
this.accounts = [];
}
}
Jest测试配置和工具:
// Jest测试配置 (jest.config.js)
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/tests/setup.js'],
moduleNameMapping: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@tests/(.*)$': '<rootDir>/src/tests/$1'
},
testMatch: [
'<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}',
'<rootDir>/src/**/*.{test,spec}.{js,jsx,ts,tsx}'
],
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/tests/**/*'
],
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70
}
}
};
// 测试工具函数 (src/tests/utils.js)
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { ethers } from 'ethers';
import { BlockchainTestEnvironment } from './BlockchainTestEnvironment';
// 区块链测试工具
export class DAppTestUtils {
constructor() {
this.testEnv = new BlockchainTestEnvironment();
this.mockProvider = null;
this.mockSigner = null;
}
// 设置测试环境
async setupTestEnvironment() {
this.mockProvider = await this.testEnv.setupLocalNetwork();
this.mockSigner = this.mockProvider.getSigner();
return {
provider: this.mockProvider,
signer: this.mockSigner,
accounts: this.testEnv.accounts
};
}
// 创建Mock Provider
createMockProvider(overrides = {}) {
const defaultMethods = {
getNetwork: jest.fn().mockResolvedValue({ chainId: 1, name: 'homestead' }),
getBalance: jest.fn().mockResolvedValue(ethers.utils.parseEther('10')),
getBlockNumber: jest.fn().mockResolvedValue(12345),
estimateGas: jest.fn().mockResolvedValue(ethers.BigNumber.from('21000')),
getFeeData: jest.fn().mockResolvedValue({
maxFeePerGas: ethers.utils.parseUnits('20', 'gwei'),
maxPriorityFeePerGas: ethers.utils.parseUnits('2', 'gwei')
}),
sendTransaction: jest.fn().mockImplementation(() => ({
hash: '0x' + '0'.repeat(64),
wait: jest.fn().mockResolvedValue({
status: 1,
blockNumber: 12346,
gasUsed: ethers.BigNumber.from('21000')
})
}))
};
return { ...defaultMethods, ...overrides };
}
// 创建Mock合约
createMockContract(abi, address, overrides = {}) {
const mockContract = {
address,
interface: new ethers.utils.Interface(abi),
provider: this.mockProvider,
...overrides
};
// 自动创建方法Mock
abi.forEach(fragment => {
if (fragment.type === 'function') {
mockContract[fragment.name] = jest.fn();
}
});
return mockContract;
}
// 等待交易确认
async waitForTransaction(txHash, confirmations = 1) {
return new Promise(resolve => {
setTimeout(() => {
resolve({
status: 1,
blockNumber: 12346,
gasUsed: ethers.BigNumber.from('21000'),
transactionHash: txHash
});
}, 100);
});
}
}
// React组件测试辅助函数
export const renderWithProviders = (component, {
web3Provider = null,
userAddress = '0x123...',
chainId = 1
} = {}) => {
const TestWrapper = ({ children }) => {
const mockWeb3Context = {
provider: web3Provider,
userAddress,
chainId,
isConnected: !!userAddress,
connect: jest.fn(),
disconnect: jest.fn()
};
return (
<Web3Context.Provider value={mockWeb3Context}>
{children}
</Web3Context.Provider>
);
};
return render(component, { wrapper: TestWrapper });
};
实际测试用例示例:
// 代币转账组件测试
describe('TokenTransferForm', () => {
let testUtils;
let mockContract;
let mockProvider;
beforeEach(async () => {
testUtils = new DAppTestUtils();
const { provider } = await testUtils.setupTestEnvironment();
mockProvider = testUtils.createMockProvider();
mockContract = testUtils.createMockContract(ERC20_ABI, '0x123...', {
balanceOf: jest.fn().mockResolvedValue(ethers.utils.parseEther('100')),
transfer: jest.fn().mockResolvedValue({
hash: '0xabc123...',
wait: jest.fn().mockResolvedValue({ status: 1 })
})
});
});
afterEach(async () => {
await testUtils.testEnv.cleanup();
});
test('应该正确显示用户余额', async () => {
renderWithProviders(
<TokenTransferForm contract={mockContract} />,
{ web3Provider: mockProvider }
);
await waitFor(() => {
expect(screen.getByText(/余额: 100/)).toBeInTheDocument();
});
expect(mockContract.balanceOf).toHaveBeenCalledWith(
expect.any(String) // 用户地址
);
});
test('应该成功提交转账交易', async () => {
renderWithProviders(
<TokenTransferForm contract={mockContract} />,
{ web3Provider: mockProvider }
);
// 填写表单
const recipientInput = screen.getByPlaceholderText('接收地址');
const amountInput = screen.getByPlaceholderText('转账数量');
const submitButton = screen.getByText('转账');
fireEvent.change(recipientInput, { target: { value: '0x456...' } });
fireEvent.change(amountInput, { target: { value: '10' } });
fireEvent.click(submitButton);
// 验证交易调用
await waitFor(() => {
expect(mockContract.transfer).toHaveBeenCalledWith(
'0x456...',
ethers.utils.parseEther('10')
);
});
});
test('余额不足时应该显示错误', async () => {
// 模拟余额不足
mockContract.balanceOf.mockResolvedValue(ethers.utils.parseEther('5'));
renderWithProviders(
<TokenTransferForm contract={mockContract} />,
{ web3Provider: mockProvider }
);
const amountInput = screen.getByPlaceholderText('转账数量');
fireEvent.change(amountInput, { target: { value: '10' } });
await waitFor(() => {
expect(screen.getByText(/余额不足/)).toBeInTheDocument();
});
});
});
// 端到端测试示例 (使用Playwright)
describe('DApp E2E Tests', () => {
let testEnv;
let page;
beforeAll(async () => {
testEnv = new BlockchainTestEnvironment();
await testEnv.setupLocalNetwork();
});
beforeEach(async () => {
page = await browser.newPage();
// 注入测试环境
await page.addInitScript(() => {
window.ethereum = {
request: async ({ method, params }) => {
// 模拟MetaMask行为
switch (method) {
case 'eth_requestAccounts':
return ['0x123...'];
case 'eth_chainId':
return '0x1';
default:
return null;
}
},
on: () => {},
removeListener: () => {}
};
});
});
afterEach(async () => {
await page.close();
});
afterAll(async () => {
await testEnv.cleanup();
});
test('完整的代币交换流程', async () => {
await page.goto('http://localhost:3000');
// 连接钱包
await page.click('[data-testid="connect-wallet"]');
await page.waitForSelector('[data-testid="wallet-connected"]');
// 选择代币
await page.click('[data-testid="select-token-from"]');
await page.click('[data-testid="token-ETH"]');
await page.click('[data-testid="select-token-to"]');
await page.click('[data-testid="token-USDC"]');
// 输入交换数量
await page.fill('[data-testid="amount-input"]', '1');
// 点击交换按钮
await page.click('[data-testid="swap-button"]');
// 等待交易确认
await page.waitForSelector('[data-testid="transaction-success"]', {
timeout: 30000
});
// 验证余额更新
const newBalance = await page.textContent('[data-testid="eth-balance"]');
expect(parseFloat(newBalance)).toBeLessThan(10); // 假设初始余额是10 ETH
});
});
测试最佳实践:
How to design deployment and release process for DApp? Handle smart contract upgrades.
How to design deployment and release process for DApp? Handle smart contract upgrades.
考察点:部署流程设计。
How to implement user data privacy protection in DApp?
How to implement user data privacy protection in DApp?
考察点:隐私保护机制。
答案:
DApp隐私保护涉及链上数据匿名化、链下数据加密、零知识证明和隐私计算等技术。需要在透明性和隐私性之间找到平衡。
隐私保护策略:
实现示例:
class PrivacyManager {
constructor() {
this.encryptionKey = null;
this.zkProofs = new Map();
}
// 客户端数据加密
async encryptSensitiveData(data, userKey) {
const encoder = new TextEncoder();
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
encoder.encode(userKey),
{ name: 'PBKDF2' },
false,
['deriveBits', 'deriveKey']
);
const key = await window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: encoder.encode('dapp-salt'),
iterations: 100000,
hash: 'SHA-256'
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encrypted = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
encoder.encode(JSON.stringify(data))
);
return {
encrypted: Array.from(new Uint8Array(encrypted)),
iv: Array.from(iv)
};
}
// 生成零知识证明
async generateZKProof(secret, publicInput) {
// 这里是简化示例,实际需要使用专门的ZK库
const commitment = await this.hash(secret + publicInput);
return {
commitment,
proof: 'zk-proof-data', // 实际的ZK证明
publicInputs: [publicInput]
};
}
// 匿名化地址
generatePseudoAddress(realAddress, salt) {
return ethers.utils.keccak256(
ethers.utils.defaultAbiCoder.encode(
['address', 'bytes32'],
[realAddress, salt]
)
);
}
}
隐私保护最佳实践:
What is DeFi? What are the main differences from traditional finance?
What is DeFi? What are the main differences from traditional finance?
考察点:DeFi基本概念理解。
答案:
DeFi(Decentralized Finance,去中心化金融)是基于区块链技术的金融服务体系,通过智能合约实现传统金融功能,无需中介机构。DeFi的核心理念是去除传统金融中的中心化机构,让用户直接控制自己的资产并参与金融活动。
主要特征:
与传统金融的主要区别:
| 维度 | 传统金融 | DeFi |
|---|---|---|
| 准入门槛 | 需要KYC认证,地理限制 | 无需许可,全球开放 |
| 中介机构 | 依赖银行、券商等中介 | 智能合约自动执行 |
| 透明度 | 信息不透明,依赖信任 | 代码开源,链上透明 |
| 运营时间 | 工作日营业时间 | 7×24小时运行 |
| 资产控制 | 机构托管用户资产 | 用户完全控制私钥 |
适用场景:
What is DEX (Decentralized Exchange)? How does it differ from CEX?
What is DEX (Decentralized Exchange)? How does it differ from CEX?
考察点:去中心化交易所概念。
答案:
DEX(去中心化交易所)是基于区块链和智能合约的交易平台,用户可以直接进行加密货币交易,无需将资产托管给第三方。DEX通过自动做市商(AMM)机制或订单簿模式实现交易撮合。
DEX工作原理:
与CEX的主要区别:
| 特征 | CEX(中心化交易所) | DEX(去中心化交易所) |
|---|---|---|
| 资产控制 | 交易所托管用户资产 | 用户完全控制私钥 |
| KYC要求 | 需要身份验证 | 匿名交易 |
| 交易速度 | 快速撮合 | 受区块链确认时间影响 |
| 流动性 | 集中流动性,深度好 | 分散流动性,可能存在滑点 |
| 手续费 | 交易手续费较低 | Gas费+协议费 |
| 监管风险 | 受政府监管 | 代码即法律 |
常见DEX协议:
What is a Liquidity Pool? How does it work?
What is a Liquidity Pool? How does it work?
考察点:流动性池基本原理。
答案:
流动性池是DeFi中的核心概念,是由两种或多种代币组成的智能合约资金池,为去中心化交易提供流动性。用户可以向池中存入代币成为流动性提供者(LP),获得交易手续费分成和流动性挖矿奖励。
工作机制:
前端实现示例:
// 使用ethers.js查询流动性池信息
const getPoolInfo = async (poolAddress) => {
const poolContract = new ethers.Contract(poolAddress, POOL_ABI, provider);
const reserves = await poolContract.getReserves();
const totalSupply = await poolContract.totalSupply();
return {
reserve0: reserves[0],
reserve1: reserves[1],
totalSupply: totalSupply,
token0Price: reserves[1] / reserves[0],
token1Price: reserves[0] / reserves[1]
};
};
// 添加流动性
const addLiquidity = async (token0Amount, token1Amount) => {
const router = new ethers.Contract(ROUTER_ADDRESS, ROUTER_ABI, signer);
const tx = await router.addLiquidity(
token0Address,
token1Address,
token0Amount,
token1Amount,
0, // 最小token0数量
0, // 最小token1数量
userAddress,
deadline
);
return await tx.wait();
};
关键特点:
考察点:流动性池基本原理。
What is AMM (Automated Market Maker)? What are the common AMM models?
What is AMM (Automated Market Maker)? What are the common AMM models?
考察点:AMM机制理解。
答案:
AMM(自动做市商)是一种算法机制,通过预先设定的数学公式自动确定资产价格和执行交易,替代传统的订单簿模式。AMM使用流动性池和价格算法实现连续的流动性提供。
核心工作原理:
常见AMM模型:
恒定乘积模型(CPMM)
// Uniswap V2使用的公式:x * y = k
const getOutputAmount = (inputAmount, inputReserve, outputReserve) => {
const inputAmountWithFee = inputAmount * 997; // 0.3%手续费
const numerator = inputAmountWithFee * outputReserve;
const denominator = inputReserve * 1000 + inputAmountWithFee;
return numerator / denominator;
};
恒定和模型(CSMM)
// 适用于稳定币交易:x + y = k
const constantSum = (reserve0, reserve1) => {
return reserve0 + reserve1; // 价格始终为1:1
};
恒定平均模型(CMMM)
// Balancer使用:(x^w0 * y^w1)^(1/(w0+w1)) = k
const weightedGeometricMean = (reserves, weights) => {
let product = 1;
for (let i = 0; i < reserves.length; i++) {
product *= Math.pow(reserves[i], weights[i]);
}
return product;
};
AMM模型对比:
| 模型类型 | 代表协议 | 优势 | 适用场景 |
|---|---|---|---|
| CPMM | Uniswap V2/V3 | 通用性强,适合大多数代币 | 主流代币交易 |
| CSMM | mStable | 低滑点 | 稳定币交易 |
| CMMM | Balancer | 支持多资产池 | 投资组合管理 |
| Hybrid | Curve | 稳定币优化+通用性 | 稳定资产和相关资产 |
What are token standards? What are the differences between ERC-20 and ERC-721?
What are token standards? What are the differences between ERC-20 and ERC-721?
考察点:代币标准基础。
答案:
代币标准是定义智能合约接口的技术规范,确保不同代币在以太坊生态系统中的兼容性和互操作性。标准化接口让钱包、交易所和DeFi协议能够无缝支持各种代币。
主要代币标准对比:
| 标准 | 类型 | 特点 | 应用场景 |
|---|---|---|---|
| ERC-20 | 同质化代币 | 每个代币相同,可分割 | 货币、治理代币、积分 |
| ERC-721 | 非同质化代币 | 每个代币独一无二 | NFT艺术品、域名、游戏道具 |
| ERC-1155 | 多代币标准 | 同时支持FT和NFT | 游戏、批量操作 |
ERC-20标准实现:
// ERC-20基础接口
const ERC20_ABI = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)",
"function totalSupply() view returns (uint256)",
"function balanceOf(address owner) view returns (uint256)",
"function transfer(address to, uint256 amount) returns (bool)",
"function approve(address spender, uint256 amount) returns (bool)",
"function allowance(address owner, address spender) view returns (uint256)"
];
// 查询代币信息
const getTokenInfo = async (tokenAddress) => {
const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
const [name, symbol, decimals, totalSupply] = await Promise.all([
contract.name(),
contract.symbol(),
contract.decimals(),
contract.totalSupply()
]);
return { name, symbol, decimals, totalSupply };
};
ERC-721标准实现:
// ERC-721基础接口
const ERC721_ABI = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function tokenURI(uint256 tokenId) view returns (string)",
"function ownerOf(uint256 tokenId) view returns (address)",
"function balanceOf(address owner) view returns (uint256)",
"function transferFrom(address from, address to, uint256 tokenId)",
"function approve(address to, uint256 tokenId)",
"function getApproved(uint256 tokenId) view returns (address)"
];
// 查询NFT信息
const getNFTInfo = async (contractAddress, tokenId) => {
const contract = new ethers.Contract(contractAddress, ERC721_ABI, provider);
const [owner, tokenURI, approved] = await Promise.all([
contract.ownerOf(tokenId),
contract.tokenURI(tokenId),
contract.getApproved(tokenId)
]);
return { owner, tokenURI, approved };
};
核心区别:
考察点:代币标准基础。
How to query user's token balance on the frontend?
How to query user’s token balance on the frontend?
考察点:代币余额查询基础。
答案:
在前端查询用户代币余额是DeFi应用的基础功能,需要连接区块链网络并调用智能合约方法。主要涉及ETH原生余额和ERC-20代币余额的查询。
实现步骤:
完整实现代码:
// 1. 查询ETH余额
const getETHBalance = async (userAddress) => {
try {
const balance = await provider.getBalance(userAddress);
return ethers.utils.formatEther(balance); // 转换为ETH单位
} catch (error) {
console.error('获取ETH余额失败:', error);
return '0';
}
};
// 2. 查询ERC-20代币余额
const getTokenBalance = async (tokenAddress, userAddress) => {
try {
const tokenContract = new ethers.Contract(
tokenAddress,
['function balanceOf(address) view returns (uint256)',
'function decimals() view returns (uint8)'],
provider
);
const [balance, decimals] = await Promise.all([
tokenContract.balanceOf(userAddress),
tokenContract.decimals()
]);
return ethers.utils.formatUnits(balance, decimals);
} catch (error) {
console.error('获取代币余额失败:', error);
return '0';
}
};
// 3. 批量查询多个代币余额
const getBatchTokenBalances = async (tokenAddresses, userAddress) => {
const balancePromises = tokenAddresses.map(async (tokenAddress) => {
const balance = await getTokenBalance(tokenAddress, userAddress);
const tokenInfo = await getTokenInfo(tokenAddress);
return {
address: tokenAddress,
balance: balance,
symbol: tokenInfo.symbol,
decimals: tokenInfo.decimals
};
});
return await Promise.all(balancePromises);
};
// 4. 实时余额监听
const watchBalance = (tokenAddress, userAddress, callback) => {
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
// 监听Transfer事件
const filter = {
address: tokenAddress,
topics: [
ethers.utils.id("Transfer(address,address,uint256)"),
null, // from
ethers.utils.hexZeroPad(userAddress, 32) // to
]
};
provider.on(filter, async () => {
const newBalance = await getTokenBalance(tokenAddress, userAddress);
callback(newBalance);
});
};
前端组件示例:
// React组件中使用
const TokenBalanceDisplay = ({ tokenAddress, userAddress }) => {
const [balance, setBalance] = useState('0');
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchBalance = async () => {
setLoading(true);
const balance = await getTokenBalance(tokenAddress, userAddress);
setBalance(balance);
setLoading(false);
};
if (tokenAddress && userAddress) {
fetchBalance();
// 设置定期更新
const interval = setInterval(fetchBalance, 10000);
return () => clearInterval(interval);
}
}, [tokenAddress, userAddress]);
if (loading) return <div>Loading...</div>;
return <div>余额: {balance}</div>;
};
注意事项:
What is Token Approval? Why is approval needed?
What is Token Approval? Why is approval needed?
考察点:代币授权机制。
答案:
代币授权(Token Approval)是ERC-20标准的安全机制,允许用户授权第三方合约或地址在指定额度内转移自己的代币。这是DeFi协议交互的基础步骤,确保用户对资产的完全控制。
授权机制原理:
为什么需要授权?
前端授权实现:
// 1. 检查当前授权额度
const getAllowance = async (tokenAddress, owner, spender) => {
const tokenContract = new ethers.Contract(
tokenAddress,
['function allowance(address owner, address spender) view returns (uint256)'],
provider
);
const allowance = await tokenContract.allowance(owner, spender);
return allowance;
};
// 2. 授权代币
const approveToken = async (tokenAddress, spenderAddress, amount) => {
try {
const tokenContract = new ethers.Contract(
tokenAddress,
['function approve(address spender, uint256 amount) returns (bool)',
'function decimals() view returns (uint8)'],
signer
);
const decimals = await tokenContract.decimals();
const approveAmount = ethers.utils.parseUnits(amount.toString(), decimals);
const tx = await tokenContract.approve(spenderAddress, approveAmount);
await tx.wait();
return { success: true, txHash: tx.hash };
} catch (error) {
return { success: false, error: error.message };
}
};
// 3. 无限授权(常用做法)
const approveMax = async (tokenAddress, spenderAddress) => {
const maxUint256 = ethers.constants.MaxUint256;
const tokenContract = new ethers.Contract(
tokenAddress,
['function approve(address spender, uint256 amount) returns (bool)'],
signer
);
const tx = await tokenContract.approve(spenderAddress, maxUint256);
return await tx.wait();
};
// 4. 撤销授权
const revokeApproval = async (tokenAddress, spenderAddress) => {
return await approveToken(tokenAddress, spenderAddress, '0');
};
// 5. 智能授权检查
const checkAndApprove = async (tokenAddress, spenderAddress, requiredAmount) => {
const currentAllowance = await getAllowance(
tokenAddress,
await signer.getAddress(),
spenderAddress
);
const required = ethers.utils.parseUnits(requiredAmount.toString(), 18);
if (currentAllowance.lt(required)) {
console.log('需要授权,当前额度不足');
return await approveToken(tokenAddress, spenderAddress, requiredAmount);
}
console.log('授权额度充足');
return { success: true, message: '无需重新授权' };
};
授权最佳实践:
// React Hook for Token Approval
const useTokenApproval = (tokenAddress, spenderAddress) => {
const [approvalStatus, setApprovalStatus] = useState({
isApproved: false,
allowance: '0',
isLoading: false
});
const checkApproval = useCallback(async (requiredAmount = '0') => {
if (!tokenAddress || !spenderAddress || !signer) return;
const userAddress = await signer.getAddress();
const allowance = await getAllowance(tokenAddress, userAddress, spenderAddress);
const required = ethers.utils.parseUnits(requiredAmount, 18);
setApprovalStatus({
isApproved: allowance.gte(required),
allowance: ethers.utils.formatUnits(allowance, 18),
isLoading: false
});
}, [tokenAddress, spenderAddress]);
const approve = useCallback(async (amount) => {
setApprovalStatus(prev => ({ ...prev, isLoading: true }));
const result = await approveToken(tokenAddress, spenderAddress, amount);
if (result.success) {
await checkApproval(amount);
}
setApprovalStatus(prev => ({ ...prev, isLoading: false }));
return result;
}, [tokenAddress, spenderAddress, checkApproval]);
return { ...approvalStatus, checkApproval, approve };
};
安全注意事项:
考察点:代币授权机制。
How to implement token swap functionality in DeFi applications?
How to implement token swap functionality in DeFi applications?
考察点:代币交换基础。
答案:
代币交换是DeFi应用的核心功能,通过DEX协议实现不同代币间的兑换。实现涉及价格查询、滑点计算、授权检查和交易执行等多个步骤。
实现流程:
完整代码实现:
// 1. Uniswap V2 Router接口
const UNISWAP_V2_ROUTER_ABI = [
"function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)",
"function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)",
"function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)",
"function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)"
];
// 2. 代币交换类
class TokenSwap {
constructor(routerAddress, provider, signer) {
this.routerAddress = routerAddress;
this.provider = provider;
this.signer = signer;
this.router = new ethers.Contract(routerAddress, UNISWAP_V2_ROUTER_ABI, signer);
}
// 获取交换价格
async getSwapPrice(tokenIn, tokenOut, amountIn) {
try {
const path = [tokenIn, tokenOut];
const amounts = await this.router.getAmountsOut(amountIn, path);
return {
amountIn: amounts[0],
amountOut: amounts[1],
price: amounts[1] / amounts[0],
path: path
};
} catch (error) {
console.error('获取价格失败:', error);
throw error;
}
}
// 计算滑点保护
calculateMinAmount(amountOut, slippagePercent = 0.5) {
const slippage = ethers.BigNumber.from(Math.floor(slippagePercent * 100));
const minAmount = amountOut.sub(amountOut.mul(slippage).div(10000));
return minAmount;
}
// 执行代币交换
async swapTokens(tokenIn, tokenOut, amountIn, slippagePercent = 0.5) {
try {
// 1. 检查和授权
const userAddress = await this.signer.getAddress();
await this.checkAndApprove(tokenIn, amountIn);
// 2. 获取价格信息
const priceInfo = await this.getSwapPrice(tokenIn, tokenOut, amountIn);
const amountOutMin = this.calculateMinAmount(priceInfo.amountOut, slippagePercent);
// 3. 设置交易参数
const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // 20分钟
const path = [tokenIn, tokenOut];
// 4. 执行交换
const tx = await this.router.swapExactTokensForTokens(
amountIn,
amountOutMin,
path,
userAddress,
deadline,
{
gasLimit: ethers.utils.hexlify(200000),
gasPrice: await this.provider.getGasPrice()
}
);
console.log('交易已提交:', tx.hash);
const receipt = await tx.wait();
return {
success: true,
txHash: tx.hash,
receipt: receipt,
amountOut: priceInfo.amountOut,
amountOutMin: amountOutMin
};
} catch (error) {
console.error('交换失败:', error);
return {
success: false,
error: error.message
};
}
}
// ETH到代币的交换
async swapETHForTokens(tokenOut, ethAmount, slippagePercent = 0.5) {
try {
const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; // Mainnet WETH
const amountIn = ethers.utils.parseEther(ethAmount);
const priceInfo = await this.getSwapPrice(WETH, tokenOut, amountIn);
const amountOutMin = this.calculateMinAmount(priceInfo.amountOut, slippagePercent);
const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
const userAddress = await this.signer.getAddress();
const tx = await this.router.swapExactETHForTokens(
amountOutMin,
[WETH, tokenOut],
userAddress,
deadline,
{ value: amountIn }
);
return await tx.wait();
} catch (error) {
console.error('ETH交换失败:', error);
throw error;
}
}
// 检查授权
async checkAndApprove(tokenAddress, amount) {
const tokenContract = new ethers.Contract(
tokenAddress,
['function approve(address spender, uint256 amount) returns (bool)',
'function allowance(address owner, address spender) view returns (uint256)'],
this.signer
);
const userAddress = await this.signer.getAddress();
const allowance = await tokenContract.allowance(userAddress, this.routerAddress);
if (allowance.lt(amount)) {
const tx = await tokenContract.approve(this.routerAddress, ethers.constants.MaxUint256);
await tx.wait();
console.log('代币授权完成');
}
}
}
// 3. React组件使用示例
const SwapInterface = () => {
const [swap] = useState(new TokenSwap(ROUTER_ADDRESS, provider, signer));
const [swapAmount, setSwapAmount] = useState('');
const [expectedOutput, setExpectedOutput] = useState('0');
const handleSwap = async () => {
const result = await swap.swapTokens(
TOKEN_A_ADDRESS,
TOKEN_B_ADDRESS,
ethers.utils.parseUnits(swapAmount, 18),
0.5 // 0.5% 滑点
);
if (result.success) {
alert(`交换成功! 交易哈希: ${result.txHash}`);
} else {
alert(`交换失败: ${result.error}`);
}
};
return (
<div>
<input
value={swapAmount}
onChange={(e) => setSwapAmount(e.target.value)}
placeholder="输入交换数量"
/>
<div>预期输出: {expectedOutput}</div>
<button onClick={handleSwap}>执行交换</button>
</div>
);
};
关键实现要点:
What is Slippage? How to set slippage protection on the frontend?
What is Slippage? How to set slippage protection on the frontend?
考察点:滑点概念与保护。
答案:
滑点(Slippage)是指交易执行时的实际价格与预期价格之间的差异,主要由市场深度不足、交易规模过大或价格波动造成。在AMM机制中,大额交易会显著影响流动性池的价格曲线。
滑点产生原因:
滑点计算公式:
// 滑点计算
const calculateSlippage = (expectedPrice, executedPrice) => {
const slippage = Math.abs(expectedPrice - executedPrice) / expectedPrice * 100;
return slippage;
};
// 价格影响计算(AMM)
const calculatePriceImpact = (inputAmount, inputReserve, outputReserve) => {
const k = inputReserve * outputReserve;
const newInputReserve = inputReserve + inputAmount;
const newOutputReserve = k / newInputReserve;
const outputAmount = outputReserve - newOutputReserve;
const originalPrice = outputReserve / inputReserve;
const newPrice = newOutputReserve / newInputReserve;
const priceImpact = (originalPrice - newPrice) / originalPrice * 100;
return {
outputAmount,
priceImpact,
newPrice,
originalPrice
};
};
前端滑点保护实现:
// 滑点保护组件
class SlippageProtection {
constructor(defaultSlippage = 0.5) {
this.defaultSlippage = defaultSlippage;
this.maxSlippage = 50; // 最大50%滑点
}
// 计算最小接收数量
calculateMinReceived(expectedAmount, slippagePercent) {
const slippage = ethers.BigNumber.from(Math.floor(slippagePercent * 100));
const minAmount = expectedAmount.sub(
expectedAmount.mul(slippage).div(10000)
);
return minAmount;
}
// 滑点验证
validateSlippage(slippagePercent) {
if (slippagePercent < 0.1) {
return { valid: false, message: '滑点过低,交易可能失败' };
}
if (slippagePercent > this.maxSlippage) {
return { valid: false, message: '滑点过高,存在风险' };
}
return { valid: true };
}
// 获取建议滑点
getSuggestedSlippage(tokenPair, tradeSize) {
// 根据代币对和交易规模建议滑点
const suggestions = {
'stable': 0.1, // 稳定币对
'major': 0.5, // 主流币对
'minor': 1.0, // 小币种
'new': 3.0 // 新币种
};
// 大额交易增加滑点
if (tradeSize > 10000) return suggestions.major * 2;
return suggestions.major;
}
}
// React滑点设置组件
const SlippageSettings = ({ onSlippageChange, currentSlippage = 0.5 }) => {
const [customSlippage, setCustomSlippage] = useState('');
const [showWarning, setShowWarning] = useState(false);
const presetSlippages = [0.1, 0.5, 1.0];
const handleSlippageSelect = (slippage) => {
onSlippageChange(slippage);
validateSlippage(slippage);
};
const handleCustomSlippage = (value) => {
const slippage = parseFloat(value);
if (isNaN(slippage)) return;
setCustomSlippage(value);
onSlippageChange(slippage);
validateSlippage(slippage);
};
const validateSlippage = (slippage) => {
if (slippage > 5) {
setShowWarning(true);
} else {
setShowWarning(false);
}
};
return (
<div className="slippage-settings">
<h4>滑点容忍度设置</h4>
<div className="preset-buttons">
{presetSlippages.map(slippage => (
<button
key={slippage}
className={currentSlippage === slippage ? 'active' : ''}
onClick={() => handleSlippageSelect(slippage)}
>
{slippage}%
</button>
))}
</div>
<div className="custom-input">
<input
type="number"
step="0.1"
min="0.1"
max="50"
placeholder="自定义"
value={customSlippage}
onChange={(e) => handleCustomSlippage(e.target.value)}
/>
<span>%</span>
</div>
{showWarning && (
<div className="warning">
⚠️ 高滑点设置可能导致不利交易
</div>
)}
</div>
);
};
// 交易确认组件
const TradeConfirmation = ({ tradeDetails, slippage }) => {
const protection = new SlippageProtection();
const minReceived = protection.calculateMinReceived(
tradeDetails.expectedOutput,
slippage
);
const priceImpact = calculatePriceImpact(
tradeDetails.inputAmount,
tradeDetails.inputReserve,
tradeDetails.outputReserve
);
return (
<div className="trade-confirmation">
<div className="trade-info">
<div>预期接收: {ethers.utils.formatUnits(tradeDetails.expectedOutput, 18)}</div>
<div>最少接收: {ethers.utils.formatUnits(minReceived, 18)}</div>
<div>价格影响: {priceImpact.priceImpact.toFixed(2)}%</div>
<div>滑点设置: {slippage}%</div>
</div>
{priceImpact.priceImpact > 3 && (
<div className="warning">
⚠️ 价格影响较大,请谨慎交易
</div>
)}
</div>
);
};
最佳实践:
考察点:滑点概念与保护。
What is Liquidity Mining? What is the basic process?
What is Liquidity Mining? What is the basic process?
考察点:流动性挖矿基础。
答案:
流动性挖矿是DeFi协议的激励机制,用户向流动性池提供资金获得LP代币,然后质押LP代币获得额外的代币奖励。这种机制旨在引导流动性,促进协议发展。
基本流程:
前端实现示例:
// 流动性挖矿合约接口
const FARMING_CONTRACT_ABI = [
"function deposit(uint256 _pid, uint256 _amount) external",
"function withdraw(uint256 _pid, uint256 _amount) external",
"function userInfo(uint256 _pid, address _user) view returns (uint256 amount, uint256 rewardDebt)",
"function pendingReward(uint256 _pid, address _user) view returns (uint256)",
"function poolInfo(uint256 _pid) view returns (address lpToken, uint256 allocPoint, uint256 lastRewardBlock, uint256 accRewardPerShare)"
];
// 流动性挖矿类
class LiquidityMining {
constructor(farmingAddress, provider, signer) {
this.farmingContract = new ethers.Contract(farmingAddress, FARMING_CONTRACT_ABI, signer);
this.provider = provider;
this.signer = signer;
}
// 获取池子信息
async getPoolInfo(pid) {
const poolInfo = await this.farmingContract.poolInfo(pid);
return {
lpToken: poolInfo.lpToken,
allocPoint: poolInfo.allocPoint,
lastRewardBlock: poolInfo.lastRewardBlock,
accRewardPerShare: poolInfo.accRewardPerShare
};
}
// 获取用户质押信息
async getUserInfo(pid, userAddress) {
const userInfo = await this.farmingContract.userInfo(pid, userAddress);
return {
amount: userInfo.amount,
rewardDebt: userInfo.rewardDebt
};
}
// 获取待领取奖励
async getPendingReward(pid, userAddress) {
return await this.farmingContract.pendingReward(pid, userAddress);
}
// 质押LP代币
async stakeLPToken(pid, amount) {
try {
const tx = await this.farmingContract.deposit(pid, amount);
await tx.wait();
return { success: true, txHash: tx.hash };
} catch (error) {
return { success: false, error: error.message };
}
}
// 提取LP代币和奖励
async unstakeLPToken(pid, amount) {
try {
const tx = await this.farmingContract.withdraw(pid, amount);
await tx.wait();
return { success: true, txHash: tx.hash };
} catch (error) {
return { success: false, error: error.message };
}
}
// 计算APY
async calculateAPY(pid) {
const poolInfo = await this.getPoolInfo(pid);
const rewardPerBlock = 100; // 假设每个区块奖励100代币
const blocksPerYear = 365 * 24 * 60 * 4; // 15秒出块
const totalRewardPerYear = rewardPerBlock * blocksPerYear;
// 获取LP代币总价值
const lpContract = new ethers.Contract(poolInfo.lpToken, [
"function totalSupply() view returns (uint256)",
"function getReserves() view returns (uint112, uint112, uint32)"
], this.provider);
const totalSupply = await lpContract.totalSupply();
const reserves = await lpContract.getReserves();
// 简化APY计算(实际需要考虑代币价格)
const tvl = reserves[0] * 2; // 假设等值
const apy = (totalRewardPerYear / tvl) * 100;
return apy;
}
}
// React挖矿组件
const LiquidityMiningInterface = ({ poolId, lpTokenAddress }) => {
const [mining] = useState(new LiquidityMining(FARMING_ADDRESS, provider, signer));
const [userInfo, setUserInfo] = useState({ amount: '0', rewards: '0' });
const [apy, setAPY] = useState(0);
useEffect(() => {
const loadData = async () => {
const userAddress = await signer.getAddress();
const info = await mining.getUserInfo(poolId, userAddress);
const rewards = await mining.getPendingReward(poolId, userAddress);
const calculatedAPY = await mining.calculateAPY(poolId);
setUserInfo({
amount: ethers.utils.formatUnits(info.amount, 18),
rewards: ethers.utils.formatUnits(rewards, 18)
});
setAPY(calculatedAPY);
};
loadData();
}, [poolId]);
return (
<div className="mining-interface">
<h3>流动性挖矿</h3>
<div>当前APY: {apy.toFixed(2)}%</div>
<div>已质押: {userInfo.amount} LP</div>
<div>待领取奖励: {userInfo.rewards}</div>
</div>
);
};
收益计算公式:
风险考虑:
What is Yield Farming? How does it differ from liquidity mining?
What is Yield Farming? How does it differ from liquidity mining?
考察点:收益农场概念。
答案:
收益农场(Yield Farming)是一种更广泛的DeFi策略,通过在多个协议间移动资产以最大化收益。它不仅包括流动性挖矿,还涉及借贷、质押、套利等多种收益获取方式。
收益农场 vs 流动性挖矿对比:
| 维度 | 流动性挖矿 | 收益农场 |
|---|---|---|
| 范围 | 单一协议LP挖矿 | 多协议综合策略 |
| 复杂度 | 相对简单 | 策略复杂,需要优化 |
| 收益来源 | 交易费+代币奖励 | 多元化收益流 |
| 风险 | 无常损失为主 | 多重协议风险叠加 |
| 管理频率 | 相对被动 | 需要主动管理 |
收益农场策略类型:
// 收益农场策略管理器
class YieldFarmingStrategy {
constructor(provider, signer) {
this.provider = provider;
this.signer = signer;
this.protocols = {
compound: '0x...',
aave: '0x...',
uniswap: '0x...',
curve: '0x...'
};
}
// 1. 基础借贷策略
async lendingStrategy(asset, amount) {
// 比较不同借贷协议的收益率
const rates = await Promise.all([
this.getCompoundAPY(asset),
this.getAaveAPY(asset),
this.getCreameAPY(asset)
]);
const bestProtocol = rates.reduce((best, current, index) =>
current.apy > best.apy ? { ...current, index } : best
);
return await this.depositToProtocol(bestProtocol.index, asset, amount);
}
// 2. 循环借贷策略
async leveragedLending(collateralAsset, borrowAsset, leverage) {
const steps = [];
let currentAmount = parseFloat(initialAmount);
for (let i = 0; i < leverage; i++) {
// 存入抵押品
steps.push({
action: 'supply',
asset: collateralAsset,
amount: currentAmount
});
// 借出资产
const borrowAmount = currentAmount * 0.75; // 75%抵押率
steps.push({
action: 'borrow',
asset: borrowAsset,
amount: borrowAmount
});
// 交换为抵押品
const swapResult = await this.swapAssets(borrowAsset, collateralAsset, borrowAmount);
currentAmount = swapResult.amountOut;
}
return await this.executeBatchOperations(steps);
}
// 3. 三池套利策略
async triArbitrageStrategy() {
// 监控Curve, Uniswap, Sushiswap的价差
const pools = [
{ name: 'Curve', price: await this.getCurvePrice('USDC/USDT') },
{ name: 'Uniswap', price: await this.getUniswapPrice('USDC/USDT') },
{ name: 'Sushiswap', price: await this.getSushiPrice('USDC/USDT') }
];
const sortedPools = pools.sort((a, b) => a.price - b.price);
const priceDiff = sortedPools[2].price - sortedPools[0].price;
if (priceDiff > 0.001) { // 0.1%以上价差
return await this.executeArbitrage(
sortedPools[0], // 买入
sortedPools[2] // 卖出
);
}
return null;
}
// 4. 自动复投策略
async autoCompoundStrategy(farmingContract, pid) {
const pendingRewards = await farmingContract.pendingReward(pid, await this.signer.getAddress());
if (pendingRewards.gt(ethers.utils.parseEther("10"))) {
// 领取奖励
await farmingContract.withdraw(pid, 0);
// 兑换一半为配对代币
const halfReward = pendingRewards.div(2);
await this.swapToken(rewardToken, token0Address, halfReward);
// 添加流动性
const lpTokens = await this.addLiquidity(token0Address, token1Address, halfReward, halfReward);
// 重新质押
await farmingContract.deposit(pid, lpTokens);
}
}
}
// React收益农场仪表盘
const YieldFarmingDashboard = () => {
const [strategies, setStrategies] = useState([]);
const [totalAPY, setTotalAPY] = useState(0);
const [riskScore, setRiskScore] = useState(0);
const activeStrategies = [
{
name: "USDC-ETH LP挖矿",
protocol: "Uniswap V3",
apy: 45.2,
tvl: 1000,
risk: "中等"
},
{
name: "稳定币借贷",
protocol: "Compound",
apy: 8.5,
tvl: 500,
risk: "低"
},
{
name: "循环借贷ETH",
protocol: "Aave",
apy: 12.8,
tvl: 2000,
risk: "高"
}
];
const calculatePortfolioMetrics = () => {
const totalValue = activeStrategies.reduce((sum, s) => sum + s.tvl, 0);
const weightedAPY = activeStrategies.reduce((sum, s) =>
sum + (s.apy * s.tvl / totalValue), 0
);
setTotalAPY(weightedAPY);
setRiskScore(calculateRiskScore(activeStrategies));
};
return (
<div className="yield-farming-dashboard">
<div className="portfolio-overview">
<h2>收益农场组合</h2>
<div>总价值: ${activeStrategies.reduce((sum, s) => sum + s.tvl, 0)}</div>
<div>加权APY: {totalAPY.toFixed(2)}%</div>
<div>风险评分: {riskScore}/10</div>
</div>
<div className="active-strategies">
{activeStrategies.map((strategy, index) => (
<div key={index} className="strategy-card">
<h3>{strategy.name}</h3>
<div>协议: {strategy.protocol}</div>
<div>APY: {strategy.apy}%</div>
<div>投入: ${strategy.tvl}</div>
<div>风险: {strategy.risk}</div>
</div>
))}
</div>
</div>
);
};
关键区别总结:
How do lending protocols work in DeFi?
How do lending protocols work in DeFi?
考察点:借贷协议基础。
答案:
DeFi借贷协议通过智能合约实现去中心化的借贷服务,用户可以存入资产赚取利息,或抵押资产借出其他代币。协议通过算法自动调节利率,确保供需平衡。
工作机制:
核心组件:
// 借贷协议交互示例
const lendingProtocol = {
// 存款赚取利息
async supply(asset, amount) {
const cToken = new ethers.Contract(cTokenAddress, CTOKEN_ABI, signer);
return await cToken.mint(amount);
},
// 抵押借贷
async borrow(asset, amount) {
const cToken = new ethers.Contract(cTokenAddress, CTOKEN_ABI, signer);
return await cToken.borrow(amount);
},
// 还款
async repay(asset, amount) {
const cToken = new ethers.Contract(cTokenAddress, CTOKEN_ABI, signer);
return await cToken.repayBorrow(amount);
}
};
What is collateral ratio? How to calculate and monitor collateral ratio?
What is collateral ratio? How to calculate and monitor collateral ratio?
考察点:抵押机制理解。
答案:
抵押率是借贷中的关键指标,表示抵押品价值与借款金额的比例。维持健康的抵押率是避免清算的关键。
计算公式:
// 抵押率计算
const calculateCollateralRatio = (collateralValue, borrowedValue) => {
return (collateralValue / borrowedValue) * 100;
};
// 健康因子计算(Aave模式)
const calculateHealthFactor = (collateralValue, borrowedValue, liquidationThreshold) => {
return (collateralValue * liquidationThreshold) / borrowedValue;
};
// 实时监控组件
const CollateralMonitor = ({ userAccount }) => {
const [ratio, setRatio] = useState(0);
const [healthFactor, setHealthFactor] = useState(0);
useEffect(() => {
const monitor = async () => {
const account = await getAccountData(userAccount);
const newRatio = calculateCollateralRatio(
account.totalCollateral,
account.totalDebt
);
setRatio(newRatio);
setHealthFactor(account.healthFactor);
};
monitor();
const interval = setInterval(monitor, 30000);
return () => clearInterval(interval);
}, [userAccount]);
return (
<div className={`collateral-status ${ratio < 150 ? 'danger' : 'safe'}`}>
<div>抵押率: {ratio.toFixed(2)}%</div>
<div>健康因子: {healthFactor.toFixed(3)}</div>
{ratio < 150 && <div>⚠️ 抵押率过低,存在清算风险</div>}
</div>
);
};
What is Liquidation? What are the conditions that trigger liquidation?
What is Liquidation? What are the conditions that trigger liquidation?
考察点:清算机制基础。
答案:
清算是DeFi借贷协议的风险控制机制,当借款人的抵押率低于最低要求时,协议会强制出售部分抵押品来偿还债务,保护出借人利益。
触发条件:
清算流程:
// 清算机制实现
class LiquidationBot {
async checkLiquidationOpportunity(borrower) {
const account = await this.getAccountData(borrower);
if (account.healthFactor < 1.0) {
return await this.executeLiquidation(borrower, account);
}
}
async executeLiquidation(borrower, account) {
// 计算可清算金额(通常是债务的50%)
const maxLiquidation = account.totalDebt * 0.5;
// 执行清算交易
const tx = await this.lendingContract.liquidateBorrow(
borrower,
repayAsset,
maxLiquidation,
collateralAsset
);
return await tx.wait();
}
}
// 清算保护策略
const LiquidationProtection = ({ userAccount }) => {
const [riskLevel, setRiskLevel] = useState('low');
const autoRepay = async () => {
if (riskLevel === 'high') {
// 自动部分还款来提高健康因子
await repayBorrow(debtAsset, repayAmount);
}
};
return (
<div>
<div>清算风险等级: {riskLevel}</div>
<button onClick={autoRepay}>紧急还款</button>
</div>
);
};
How to display APY (Annual Percentage Yield) of DeFi protocols on the frontend?
How to display APY (Annual Percentage Yield) of DeFi protocols on the frontend?
考察点:收益率展示基础。
答案:
APY展示需要从协议合约获取利率数据,考虑复利效应进行计算,并实时更新显示给用户。
实现方式:
// APY计算和显示
class APYCalculator {
// 计算存款APY
async getSupplyAPY(cTokenAddress) {
const cToken = new ethers.Contract(cTokenAddress, CTOKEN_ABI, provider);
const supplyRatePerBlock = await cToken.supplyRatePerBlock();
const blocksPerYear = 365 * 24 * 60 * 4; // 15秒出块
// 复利计算
const apy = (Math.pow((supplyRatePerBlock / 1e18 * blocksPerYear) + 1, 1) - 1) * 100;
return apy;
}
// 计算借贷APY
async getBorrowAPY(cTokenAddress) {
const cToken = new ethers.Contract(cTokenAddress, CTOKEN_ABI, provider);
const borrowRatePerBlock = await cToken.borrowRatePerBlock();
const blocksPerYear = 365 * 24 * 60 * 4;
const apy = (Math.pow((borrowRatePerBlock / 1e18 * blocksPerYear) + 1, 1) - 1) * 100;
return apy;
}
// 流动性挖矿APY
async getFarmingAPY(poolId, rewardTokenPrice, lpTokenPrice) {
const rewardPerSecond = await farmingContract.rewardPerSecond();
const totalAllocPoint = await farmingContract.totalAllocPoint();
const poolInfo = await farmingContract.poolInfo(poolId);
const poolRewardPerSecond = rewardPerSecond.mul(poolInfo.allocPoint).div(totalAllocPoint);
const yearlyReward = poolRewardPerSecond.mul(365 * 24 * 60 * 60);
const yearlyRewardValue = yearlyReward * rewardTokenPrice;
const totalStakedValue = poolInfo.totalStaked * lpTokenPrice;
return (yearlyRewardValue / totalStakedValue) * 100;
}
}
// React APY显示组件
const APYDisplay = ({ protocol, asset }) => {
const [apy, setAPY] = useState({ supply: 0, borrow: 0, farming: 0 });
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchAPY = async () => {
const calculator = new APYCalculator();
const [supplyAPY, borrowAPY, farmingAPY] = await Promise.all([
calculator.getSupplyAPY(asset.cToken),
calculator.getBorrowAPY(asset.cToken),
calculator.getFarmingAPY(asset.poolId, asset.rewardPrice, asset.price)
]);
setAPY({ supply: supplyAPY, borrow: borrowAPY, farming: farmingAPY });
setLoading(false);
};
fetchAPY();
const interval = setInterval(fetchAPY, 60000); // 每分钟更新
return () => clearInterval(interval);
}, [asset]);
if (loading) return <div>加载APY...</div>;
return (
<div className="apy-display">
<h3>{asset.symbol} 收益率</h3>
<div className="apy-item">
<span>存款APY:</span>
<span className="apy-value">{apy.supply.toFixed(2)}%</span>
</div>
<div className="apy-item">
<span>借款APY:</span>
<span className="apy-value negative">{apy.borrow.toFixed(2)}%</span>
</div>
<div className="apy-item">
<span>挖矿APY:</span>
<span className="apy-value highlight">{apy.farming.toFixed(2)}%</span>
</div>
<div className="total-apy">
总收益: {(apy.supply + apy.farming).toFixed(2)}%
</div>
</div>
);
};
注意事项:
How to design a universal DeFi protocol interaction framework?
How to design a universal DeFi protocol interaction framework?
考察点:架构设计能力。
答案:
通用DeFi协议交互框架需要抽象不同协议的共同接口,提供标准化的交互方式,支持可扩展的协议适配和统一的错误处理机制。
架构设计:
// 1. 协议抽象接口
interface IProtocolAdapter {
protocol: string;
version: string;
// 基础交互方法
getBalance(asset: string, user: string): Promise<BigNumber>;
deposit(asset: string, amount: BigNumber): Promise<Transaction>;
withdraw(asset: string, amount: BigNumber): Promise<Transaction>;
getAPY(asset: string): Promise<number>;
}
// 2. 协议适配器实现
class UniswapV2Adapter implements IProtocolAdapter {
protocol = 'Uniswap';
version = 'v2';
constructor(private router: Contract, private factory: Contract) {}
async getBalance(asset: string, user: string): Promise<BigNumber> {
// 实现Uniswap特定逻辑
}
async deposit(asset: string, amount: BigNumber): Promise<Transaction> {
// 添加流动性逻辑
}
}
class CompoundAdapter implements IProtocolAdapter {
protocol = 'Compound';
version = 'v2';
constructor(private comptroller: Contract) {}
async deposit(asset: string, amount: BigNumber): Promise<Transaction> {
// Compound存款逻辑
}
}
// 3. 协议注册中心
class ProtocolRegistry {
private adapters: Map<string, IProtocolAdapter> = new Map();
register(key: string, adapter: IProtocolAdapter) {
this.adapters.set(key, adapter);
}
get(protocol: string): IProtocolAdapter {
return this.adapters.get(protocol);
}
getAllAdapters(): IProtocolAdapter[] {
return Array.from(this.adapters.values());
}
}
// 4. 统一交互层
class DeFiFramework {
constructor(
private registry: ProtocolRegistry,
private provider: ethers.Provider,
private signer: ethers.Signer
) {}
// 跨协议余额查询
async getBalances(user: string): Promise<ProtocolBalance[]> {
const adapters = this.registry.getAllAdapters();
const balancePromises = adapters.map(async adapter => {
const assets = await this.getSupportedAssets(adapter.protocol);
const assetBalances = await Promise.all(
assets.map(asset => adapter.getBalance(asset, user))
);
return {
protocol: adapter.protocol,
balances: assets.map((asset, index) => ({
asset,
balance: assetBalances[index]
}))
};
});
return Promise.all(balancePromises);
}
// 最优收益率查询
async getBestAPY(asset: string): Promise<ProtocolAPY[]> {
const adapters = this.registry.getAllAdapters();
const apyPromises = adapters.map(async adapter => ({
protocol: adapter.protocol,
apy: await adapter.getAPY(asset)
}));
const apys = await Promise.all(apyPromises);
return apys.sort((a, b) => b.apy - a.apy);
}
// 批量操作执行
async executeBatch(operations: Operation[]): Promise<BatchResult> {
const results = [];
for (const op of operations) {
try {
const adapter = this.registry.get(op.protocol);
const result = await this.executeOperation(adapter, op);
results.push({ success: true, result });
} catch (error) {
results.push({ success: false, error: error.message });
}
}
return { operations: results, success: results.every(r => r.success) };
}
}
// 5. 使用示例
const setupFramework = () => {
const registry = new ProtocolRegistry();
// 注册协议适配器
registry.register('uniswap-v2', new UniswapV2Adapter(router, factory));
registry.register('compound', new CompoundAdapter(comptroller));
registry.register('aave-v2', new AaveV2Adapter(lendingPool));
return new DeFiFramework(registry, provider, signer);
};
// React Hook使用
const useDeFiFramework = () => {
const [framework] = useState(() => setupFramework());
const getOptimalStrategy = useCallback(async (asset: string, amount: BigNumber) => {
const apys = await framework.getBestAPY(asset);
return apys[0]; // 返回最高收益协议
}, [framework]);
return { framework, getOptimalStrategy };
};
框架特点:
What is Flash Loan? How to integrate flash loan functionality on the frontend?
What is Flash Loan? How to integrate flash loan functionality on the frontend?
考察点:高级DeFi概念。
答案:
闪电贷是DeFi的创新机制,允许在单个交易中借入大量资金而无需抵押,但必须在同一交易中归还。主要用于套利、债务重组、清算等场景。
工作原理:
前端集成实现:
// 闪电贷合约接口
const FLASHLOAN_ABI = [
"function flashLoan(address receiverAddress, address[] assets, uint256[] amounts, uint256[] modes, address onBehalfOf, bytes params, uint16 referralCode)",
"function executeOperation(address[] assets, uint256[] amounts, uint256[] premiums, address initiator, bytes params) external returns (bool)"
];
// 闪电贷执行器
class FlashLoanExecutor {
constructor(provider, signer, aavePool) {
this.provider = provider;
this.signer = signer;
this.aavePool = aavePool;
}
// 部署闪电贷合约
async deployFlashLoanContract(strategy) {
const contractFactory = new ethers.ContractFactory(
FLASHLOAN_CONTRACT_ABI,
FLASHLOAN_CONTRACT_BYTECODE,
this.signer
);
const contract = await contractFactory.deploy(
this.aavePool.address,
strategy.targetProtocols
);
return await contract.deployed();
}
// 执行闪电贷套利
async executeArbitrage(params) {
const {
asset,
amount,
exchanges,
expectedProfit
} = params;
// 编码执行参数
const executeParams = ethers.utils.defaultAbiCoder.encode(
['address[]', 'uint256[]', 'uint256'],
[exchanges, [amount], expectedProfit]
);
try {
const tx = await this.aavePool.flashLoan(
this.flashLoanContract.address,
[asset],
[amount],
[0], // 模式:0=无债务,1=稳定利率,2=浮动利率
this.signer.address,
executeParams,
0 // 推荐码
);
return await tx.wait();
} catch (error) {
throw new Error(`闪电贷执行失败: ${error.message}`);
}
}
// 计算套利机会
async findArbitrageOpportunity() {
const exchanges = ['uniswap', 'sushiswap', 'curve'];
const assets = ['USDC', 'DAI', 'USDT'];
const opportunities = [];
for (const asset of assets) {
const prices = await Promise.all(
exchanges.map(ex => this.getPrice(ex, asset))
);
const maxPrice = Math.max(...prices);
const minPrice = Math.min(...prices);
const priceDiff = ((maxPrice - minPrice) / minPrice) * 100;
if (priceDiff > 0.5) { // 0.5%以上价差
opportunities.push({
asset,
buyExchange: exchanges[prices.indexOf(minPrice)],
sellExchange: exchanges[prices.indexOf(maxPrice)],
priceDiff,
estimatedProfit: this.calculateProfit(priceDiff, 100000) // 10万美元规模
});
}
}
return opportunities.sort((a, b) => b.estimatedProfit - a.estimatedProfit);
}
}
// React闪电贷界面
const FlashLoanInterface = () => {
const [executor] = useState(new FlashLoanExecutor(provider, signer, aavePool));
const [opportunities, setOpportunities] = useState([]);
const [executing, setExecuting] = useState(false);
const [results, setResults] = useState([]);
useEffect(() => {
const scanOpportunities = async () => {
const opps = await executor.findArbitrageOpportunity();
setOpportunities(opps);
};
scanOpportunities();
const interval = setInterval(scanOpportunities, 30000);
return () => clearInterval(interval);
}, [executor]);
const executeFlashLoan = async (opportunity) => {
setExecuting(true);
try {
const result = await executor.executeArbitrage({
asset: opportunity.asset,
amount: ethers.utils.parseUnits("100000", 6), // 10万USDC
exchanges: [opportunity.buyExchange, opportunity.sellExchange],
expectedProfit: opportunity.estimatedProfit
});
setResults(prev => [...prev, {
...opportunity,
txHash: result.transactionHash,
success: true,
timestamp: Date.now()
}]);
alert(`套利成功!交易哈希: ${result.transactionHash}`);
} catch (error) {
alert(`套利失败: ${error.message}`);
setResults(prev => [...prev, {
...opportunity,
success: false,
error: error.message,
timestamp: Date.now()
}]);
} finally {
setExecuting(false);
}
};
return (
<div className="flashloan-interface">
<h2>闪电贷套利</h2>
<div className="opportunities">
<h3>套利机会</h3>
{opportunities.map((opp, index) => (
<div key={index} className="opportunity-card">
<div>资产: {opp.asset}</div>
<div>价差: {opp.priceDiff.toFixed(3)}%</div>
<div>买入: {opp.buyExchange}</div>
<div>卖出: {opp.sellExchange}</div>
<div>预期利润: ${opp.estimatedProfit.toFixed(2)}</div>
<button
onClick={() => executeFlashLoan(opp)}
disabled={executing}
>
{executing ? '执行中...' : '执行套利'}
</button>
</div>
))}
</div>
<div className="results">
<h3>执行历史</h3>
{results.map((result, index) => (
<div key={index} className={`result ${result.success ? 'success' : 'error'}`}>
<div>{new Date(result.timestamp).toLocaleString()}</div>
<div>{result.asset} - {result.success ? '成功' : '失败'}</div>
{result.txHash && <div>交易: {result.txHash}</div>}
{result.error && <div>错误: {result.error}</div>}
</div>
))}
</div>
</div>
);
};
// 闪电贷策略合约示例
const FLASHLOAN_STRATEGY_CONTRACT = `
pragma solidity ^0.8.0;
contract ArbitrageBot {
address private aavePool;
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool) {
// 解析参数
(address[] memory exchanges, uint256[] memory tradeAmounts, uint256 expectedProfit) =
abi.decode(params, (address[], uint256[], uint256));
// 执行套利逻辑
uint256 profit = performArbitrage(assets[0], amounts[0], exchanges);
// 确保有足够资金还款
require(profit >= premiums[0], "Insufficient profit for repayment");
// 归还闪电贷
IERC20(assets[0]).transfer(msg.sender, amounts[0] + premiums[0]);
return true;
}
function performArbitrage(address asset, uint256 amount, address[] memory exchanges)
private returns (uint256 profit) {
// 具体套利实现
}
}
`;
使用场景:
How to implement yield comparison and optimization strategies across multiple protocols?
How to implement yield comparison and optimization strategies across multiple protocols?
考察点:收益优化策略。
答案:
多协议收益比较需要实时获取各协议的收益数据,考虑风险因素,自动推荐最优策略组合。
class YieldOptimizer {
async compareProtocols(asset, amount) {
const protocols = [
{ name: 'Compound', apy: await getCompoundAPY(asset), risk: 'low' },
{ name: 'Aave', apy: await getAaveAPY(asset), risk: 'low' },
{ name: 'Yearn', apy: await getYearnAPY(asset), risk: 'medium' }
];
return protocols.map(p => ({
...p,
riskAdjustedReturn: p.apy * this.getRiskWeight(p.risk),
estimatedYield: amount * p.apy / 100
})).sort((a, b) => b.riskAdjustedReturn - a.riskAdjustedReturn);
}
async autoRebalance(portfolio) {
const opportunities = await this.findRebalanceOpportunities(portfolio);
return await this.executeRebalancing(opportunities);
}
}
What is Impermanent Loss? How to calculate and display it on the frontend?
What is Impermanent Loss? How to calculate and display it on the frontend?
考察点:流动性风险理解。
答案:
无常损失是流动性提供者因代币价格变化而产生的潜在损失,相对于简单持有代币的策略。
const calculateImpermanentLoss = (price0Initial, price1Initial, price0Current, price1Current) => {
const priceRatio = (price1Current / price0Current) / (price1Initial / price0Initial);
const impermanentLoss = 2 * Math.sqrt(priceRatio) / (1 + priceRatio) - 1;
return impermanentLoss * 100;
};
const ImpermanentLossCalculator = ({ token0, token1, initialPrice, currentPrice }) => {
const loss = calculateImpermanentLoss(1, initialPrice, 1, currentPrice);
return (
<div className={`il-display ${loss < 0 ? 'loss' : 'gain'}`}>
<div>无常损失: {loss.toFixed(2)}%</div>
<div className="explanation">
{loss < -5 && "⚠️ 价格偏离较大,建议谨慎"}
</div>
</div>
);
};
How to implement arbitrage strategies across protocols? What factors need to be considered?
How to implement arbitrage strategies across protocols? What factors need to be considered?
考察点:套利策略设计。
答案:
跨协议套利需要实时监控价差,考虑交易成本、滑点、时间风险等因素。
class ArbitrageBot {
async scanOpportunities() {
const exchanges = ['Uniswap', 'Sushiswap', 'Curve'];
const prices = await Promise.all(exchanges.map(ex => this.getPrice(ex, 'ETH/USDC')));
const opportunities = [];
for (let i = 0; i < prices.length; i++) {
for (let j = i + 1; j < prices.length; j++) {
const priceDiff = Math.abs(prices[i] - prices[j]) / Math.min(prices[i], prices[j]);
if (priceDiff > 0.005) { // 0.5%阈值
opportunities.push({
buyExchange: prices[i] < prices[j] ? exchanges[i] : exchanges[j],
sellExchange: prices[i] > prices[j] ? exchanges[i] : exchanges[j],
profitPercent: priceDiff * 100
});
}
}
}
return opportunities;
}
async executeArbitrage(opportunity, amount) {
// 考虑因素:Gas费用、滑点、时间延迟、MEV风险
const gasCost = await this.estimateGasCost();
const slippage = this.calculateSlippage(amount);
const expectedProfit = amount * opportunity.profitPercent / 100;
if (expectedProfit > gasCost + slippage) {
return await this.performTrade(opportunity, amount);
}
return null;
}
}
What is MEV (Maximum Extractable Value)? How to prevent MEV attacks in DeFi interactions?
What is MEV (Maximum Extractable Value)? How to prevent MEV attacks in DeFi interactions?
考察点:MEV防护机制。
答案:
MEV是矿工或验证者通过重排、插入或审查交易获得的最大价值。防范需要使用提交-揭示方案、私有内存池等技术。
class MEVProtection {
// 使用Flashbots保护交易
async submitPrivateTransaction(tx) {
const flashbotsRelay = new FlashbotsRelay();
return await flashbotsRelay.sendBundle([tx]);
}
// 提交-揭示方案
async commitRevealTransaction(params) {
// 第一步:提交哈希
const commitment = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(
['address', 'uint256', 'uint256'],
[params.token, params.amount, params.nonce]
));
await this.commitHash(commitment);
// 等待确认后揭示
setTimeout(async () => {
await this.revealTransaction(params);
}, 60000); // 1分钟后揭示
}
// 时间锁定保护
async useTimelock(operation, delay = 3600) {
const timelockTx = await this.timelockContract.schedule(
operation.target,
operation.value,
operation.data,
ethers.constants.HashZero,
delay
);
return timelockTx;
}
}
How to design a risk management system for DeFi applications?
How to design a risk management system for DeFi applications?
考察点:风险管理设计。
答案:
DeFi风险管理需要多维度监控,包括价格风险、流动性风险、智能合约风险和系统性风险。
class RiskManagementSystem {
constructor() {
this.riskThresholds = {
priceVolatility: 0.1, // 10%
liquidityRatio: 0.8, // 80%
concentrationRisk: 0.3 // 30%
};
}
async assessPortfolioRisk(portfolio) {
const risks = {
market: await this.calculateMarketRisk(portfolio),
liquidity: await this.calculateLiquidityRisk(portfolio),
contract: await this.assessContractRisk(portfolio),
concentration: this.calculateConcentrationRisk(portfolio)
};
const overallRisk = this.aggregateRiskScore(risks);
return {
score: overallRisk,
details: risks,
recommendations: this.generateRecommendations(risks)
};
}
generateRecommendations(risks) {
const recommendations = [];
if (risks.concentration > 0.3) {
recommendations.push("建议分散投资,降低集中度风险");
}
if (risks.liquidity < 0.2) {
recommendations.push("流动性不足,考虑增加现金比例");
}
if (risks.market > 0.8) {
recommendations.push("市场风险较高,建议降低杠杆");
}
return recommendations;
}
}
What are governance tokens? How to implement DAO governance voting on the frontend?
What are governance tokens? How to implement DAO governance voting on the frontend?
考察点:DAO治理机制。
答案:
治理代币赋予持有者参与协议治理的权利,包括提案、投票和执行决策。
class DAOGovernance {
async createProposal(title, description, actions) {
const proposal = await this.governanceContract.propose(
actions.map(a => a.target),
actions.map(a => a.value),
actions.map(a => a.signature),
actions.map(a => a.calldata),
description
);
return proposal;
}
async vote(proposalId, support) {
// support: 0=反对, 1=支持, 2=弃权
return await this.governanceContract.castVote(proposalId, support);
}
async executeProposal(proposalId) {
return await this.governanceContract.execute(proposalId);
}
}
const VotingInterface = ({ proposalId }) => {
const [proposal, setProposal] = useState(null);
const [votingPower, setVotingPower] = useState('0');
const handleVote = async (support) => {
const governance = new DAOGovernance();
await governance.vote(proposalId, support);
};
return (
<div className="voting-interface">
<h3>{proposal?.title}</h3>
<p>{proposal?.description}</p>
<div>投票权重: {votingPower}</div>
<div className="vote-buttons">
<button onClick={() => handleVote(1)}>支持</button>
<button onClick={() => handleVote(0)}>反对</button>
<button onClick={() => handleVote(2)}>弃权</button>
</div>
</div>
);
};
How to optimize Gas costs for DeFi transactions? Implement smart Gas management.
How to optimize Gas costs for DeFi transactions? Implement smart Gas management.
考察点:Gas优化策略。
答案:
Gas优化包括批量操作、时机选择、Layer2使用和智能合约优化。
class GasOptimizer {
async getOptimalGasPrice() {
const network = await this.provider.getNetwork();
const gasPrice = await this.provider.getGasPrice();
const gasStation = await fetch(`https://gasstation-mainnet.matic.network/v2`).then(r => r.json());
return {
slow: ethers.utils.parseUnits(gasStation.safeLow.toString(), 'gwei'),
standard: ethers.utils.parseUnits(gasStation.standard.toString(), 'gwei'),
fast: ethers.utils.parseUnits(gasStation.fast.toString(), 'gwei'),
current: gasPrice
};
}
async batchTransactions(transactions) {
// 使用Multicall合约批量执行
const multicall = new ethers.Contract(MULTICALL_ADDRESS, MULTICALL_ABI, this.signer);
const calls = transactions.map(tx => ({
target: tx.to,
callData: tx.data
}));
return await multicall.aggregate(calls);
}
async scheduleTransaction(tx, targetGasPrice) {
// 等待Gas价格降到目标价格
return new Promise((resolve) => {
const checkGasPrice = async () => {
const currentPrice = await this.provider.getGasPrice();
if (currentPrice.lte(targetGasPrice)) {
const result = await this.signer.sendTransaction(tx);
resolve(result);
} else {
setTimeout(checkGasPrice, 60000); // 1分钟后重试
}
};
checkGasPrice();
});
}
}
const GasTracker = () => {
const [gasData, setGasData] = useState(null);
const [selectedSpeed, setSelectedSpeed] = useState('standard');
useEffect(() => {
const optimizer = new GasOptimizer();
const updateGasData = async () => {
const data = await optimizer.getOptimalGasPrice();
setGasData(data);
};
updateGasData();
const interval = setInterval(updateGasData, 30000);
return () => clearInterval(interval);
}, []);
if (!gasData) return <div>加载Gas数据...</div>;
return (
<div className="gas-tracker">
<h3>Gas费用追踪</h3>
<div className="gas-options">
{Object.entries(gasData).map(([speed, price]) => (
<button
key={speed}
className={selectedSpeed === speed ? 'selected' : ''}
onClick={() => setSelectedSpeed(speed)}
>
{speed}: {ethers.utils.formatUnits(price, 'gwei')} Gwei
</button>
))}
</div>
</div>
);
};
What is cross-chain DeFi? How to implement cross-chain asset management?
What is cross-chain DeFi? How to implement cross-chain asset management?
考察点:跨链DeFi实现。
答案:
跨链DeFi允许用户在不同区块链网络间转移和使用资产,通过桥接协议、跨链通信和多链部署实现资产的互操作性。
跨链技术方案:
// 跨链桥接管理器
class CrossChainBridge {
constructor() {
this.supportedChains = {
ethereum: { chainId: 1, rpc: 'https://mainnet.infura.io' },
polygon: { chainId: 137, rpc: 'https://polygon-rpc.com' },
arbitrum: { chainId: 42161, rpc: 'https://arb1.arbitrum.io/rpc' },
optimism: { chainId: 10, rpc: 'https://mainnet.optimism.io' }
};
}
// 使用LayerZero进行跨链转移
async bridgeAssets(fromChain, toChain, asset, amount) {
const layerZeroEndpoint = this.getLayerZeroEndpoint(fromChain);
const bridgeTx = await layerZeroEndpoint.send(
this.getChainId(toChain),
ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [asset, amount]),
{ value: await this.estimateFees(fromChain, toChain, amount) }
);
return await bridgeTx.wait();
}
// 跨链资产查询
async getMultiChainBalance(userAddress, asset) {
const balances = {};
for (const [chainName, chainConfig] of Object.entries(this.supportedChains)) {
const provider = new ethers.providers.JsonRpcProvider(chainConfig.rpc);
const tokenContract = new ethers.Contract(asset, ERC20_ABI, provider);
try {
const balance = await tokenContract.balanceOf(userAddress);
balances[chainName] = ethers.utils.formatUnits(balance, 18);
} catch (error) {
balances[chainName] = '0';
}
}
return balances;
}
}
// React跨链资产管理组件
const CrossChainAssetManager = ({ userAddress }) => {
const [balances, setBalances] = useState({});
const [bridgeStatus, setBridgeStatus] = useState('idle');
const [selectedFromChain, setSelectedFromChain] = useState('ethereum');
const [selectedToChain, setSelectedToChain] = useState('polygon');
const bridge = new CrossChainBridge();
const handleBridge = async (asset, amount) => {
setBridgeStatus('bridging');
try {
const result = await bridge.bridgeAssets(
selectedFromChain,
selectedToChain,
asset,
ethers.utils.parseUnits(amount, 18)
);
setBridgeStatus('success');
// 刷新余额
await loadBalances();
} catch (error) {
setBridgeStatus('failed');
console.error('跨链转移失败:', error);
}
};
return (
<div className="cross-chain-manager">
<h3>跨链资产管理</h3>
<div className="chain-balances">
{Object.entries(balances).map(([chain, balance]) => (
<div key={chain} className="chain-balance">
<span>{chain}:</span>
<span>{balance} USDC</span>
</div>
))}
</div>
<div className="bridge-interface">
<select value={selectedFromChain} onChange={(e) => setSelectedFromChain(e.target.value)}>
<option value="ethereum">Ethereum</option>
<option value="polygon">Polygon</option>
<option value="arbitrum">Arbitrum</option>
</select>
<span>→</span>
<select value={selectedToChain} onChange={(e) => setSelectedToChain(e.target.value)}>
<option value="polygon">Polygon</option>
<option value="arbitrum">Arbitrum</option>
<option value="optimism">Optimism</option>
</select>
<button
onClick={() => handleBridge('USDC', '100')}
disabled={bridgeStatus === 'bridging'}
>
{bridgeStatus === 'bridging' ? '转移中...' : '跨链转移'}
</button>
</div>
</div>
);
};
How to implement DeFi portfolio investment strategies? Automated asset allocation.
How to implement DeFi portfolio investment strategies? Automated asset allocation.
考察点:投资策略自动化。
答案:
DeFi投资组合策略基于现代投资组合理论,通过算法自动分配资产权重,实现风险分散和收益优化。
策略实现:
// 投资组合管理器
class PortfolioManager {
constructor(totalAmount, riskTolerance = 0.5) {
this.totalAmount = totalAmount;
this.riskTolerance = riskTolerance; // 0-1,风险承受度
this.strategies = {
conservative: { stocks: 0.3, bonds: 0.6, defi: 0.1 },
balanced: { stocks: 0.4, bonds: 0.3, defi: 0.3 },
aggressive: { stocks: 0.2, bonds: 0.1, defi: 0.7 }
};
}
// 计算最优资产配置
async calculateOptimalAllocation(assets) {
const returns = await this.getHistoricalReturns(assets);
const covariance = this.calculateCovarianceMatrix(returns);
// 使用均值方差优化
const weights = this.meanVarianceOptimization(returns, covariance);
return assets.map((asset, index) => ({
asset: asset.symbol,
weight: weights[index],
amount: this.totalAmount * weights[index],
protocol: asset.protocol,
expectedReturn: returns[index],
risk: Math.sqrt(covariance[index][index])
}));
}
// 自动再平衡
async rebalancePortfolio(currentPortfolio, targetAllocation) {
const rebalanceActions = [];
for (const target of targetAllocation) {
const current = currentPortfolio.find(p => p.asset === target.asset);
const currentAmount = current ? current.amount : 0;
const targetAmount = target.amount;
const difference = targetAmount - currentAmount;
if (Math.abs(difference) > targetAmount * 0.05) { // 5%阈值
rebalanceActions.push({
action: difference > 0 ? 'buy' : 'sell',
asset: target.asset,
amount: Math.abs(difference),
protocol: target.protocol
});
}
}
return await this.executeBatchRebalance(rebalanceActions);
}
// 智能收益农场分配
async smartYieldAllocation(availableProtocols) {
const riskAdjustedReturns = await Promise.all(
availableProtocols.map(async protocol => {
const apy = await protocol.getAPY();
const tvl = await protocol.getTVL();
const riskScore = this.assessProtocolRisk(protocol);
return {
protocol: protocol.name,
apy: apy,
riskAdjustedReturn: apy * (1 - riskScore),
tvl: tvl,
allocation: 0
};
})
);
// 按风险调整收益排序分配
const sortedProtocols = riskAdjustedReturns.sort((a, b) =>
b.riskAdjustedReturn - a.riskAdjustedReturn
);
// 分配逻辑:高收益协议分配更多资金,但考虑风险分散
let remainingAmount = this.totalAmount;
const allocations = [];
for (let i = 0; i < Math.min(3, sortedProtocols.length); i++) {
const protocol = sortedProtocols[i];
const weight = this.calculateAllocationWeight(i, sortedProtocols.length);
const allocation = remainingAmount * weight;
allocations.push({
...protocol,
allocation: allocation,
weight: weight
});
remainingAmount -= allocation;
}
return allocations;
}
}
// React投资组合仪表板
const PortfolioDashboard = ({ userAddress, totalInvestment }) => {
const [portfolio, setPortfolio] = useState([]);
const [performance, setPerformance] = useState({ totalReturn: 0, sharpeRatio: 0 });
const [autoRebalance, setAutoRebalance] = useState(false);
const portfolioManager = new PortfolioManager(totalInvestment, 0.6);
useEffect(() => {
const initializePortfolio = async () => {
const availableAssets = [
{ symbol: 'ETH', protocol: 'Compound' },
{ symbol: 'USDC', protocol: 'Aave' },
{ symbol: 'UNI-ETH/USDC', protocol: 'Uniswap' }
];
const allocation = await portfolioManager.calculateOptimalAllocation(availableAssets);
setPortfolio(allocation);
};
initializePortfolio();
}, []);
const handleRebalance = async () => {
const currentPortfolio = await getCurrentPortfolio(userAddress);
const targetAllocation = await portfolioManager.calculateOptimalAllocation(
portfolio.map(p => ({ symbol: p.asset, protocol: p.protocol }))
);
await portfolioManager.rebalancePortfolio(currentPortfolio, targetAllocation);
};
return (
<div className="portfolio-dashboard">
<div className="portfolio-overview">
<h2>投资组合概览</h2>
<div>总投资: ${totalInvestment.toLocaleString()}</div>
<div>总收益: ${performance.totalReturn.toFixed(2)}</div>
<div>夏普比率: {performance.sharpeRatio.toFixed(3)}</div>
</div>
<div className="asset-allocation">
{portfolio.map((asset, index) => (
<div key={index} className="asset-card">
<h4>{asset.asset}</h4>
<div>权重: {(asset.weight * 100).toFixed(1)}%</div>
<div>金额: ${asset.amount.toFixed(2)}</div>
<div>协议: {asset.protocol}</div>
<div>预期收益: {(asset.expectedReturn * 100).toFixed(2)}%</div>
</div>
))}
</div>
<div className="portfolio-controls">
<label>
<input
type="checkbox"
checked={autoRebalance}
onChange={(e) => setAutoRebalance(e.target.checked)}
/>
自动再平衡
</label>
<button onClick={handleRebalance}>手动再平衡</button>
</div>
</div>
);
};
What are the key points of DeFi protocol security audits? How to perform security checks on the frontend?
What are the key points of DeFi protocol security audits? How to perform security checks on the frontend?
考察点:安全审计意识。
答案:
DeFi安全审计需要检查智能合约漏洞、权限控制、价格操纵等风险点,前端也需要实现相应的安全验证机制。
安全检查实现:
// 安全审计检查器
class SecurityAuditor {
constructor(provider) {
this.provider = provider;
this.riskThresholds = {
maxSlippage: 0.03, // 3%
minLiquidity: ethers.utils.parseEther("100000"), // 10万美元
maxPriceImpact: 0.05 // 5%
};
}
// 检查合约安全性
async auditContract(contractAddress) {
const auditResults = {
ownership: await this.checkOwnership(contractAddress),
upgradeable: await this.checkUpgradeability(contractAddress),
timelock: await this.checkTimelock(contractAddress),
pausable: await this.checkPauseMechanism(contractAddress),
reentrancy: await this.checkReentrancyProtection(contractAddress),
priceOracle: await this.checkPriceOracle(contractAddress)
};
const riskScore = this.calculateRiskScore(auditResults);
return {
...auditResults,
overallRisk: riskScore,
recommendation: this.generateSecurityRecommendation(riskScore)
};
}
// 检查价格操纵风险
async checkPriceManipulation(tokenAddress, amount) {
const priceImpact = await this.calculatePriceImpact(tokenAddress, amount);
const liquidityDepth = await this.getLiquidityDepth(tokenAddress);
const volatility = await this.getHistoricalVolatility(tokenAddress);
return {
priceImpact: priceImpact,
liquidityRisk: liquidityDepth < this.riskThresholds.minLiquidity,
volatilityRisk: volatility > 0.3,
manipulationRisk: priceImpact > this.riskThresholds.maxPriceImpact
};
}
// 实时风险监控
async monitorTransaction(tx) {
const risks = [];
// 检查交易金额是否异常
if (tx.value > ethers.utils.parseEther("1000")) {
risks.push({ type: 'high_value', severity: 'medium' });
}
// 检查Gas价格是否异常
const avgGasPrice = await this.getAverageGasPrice();
if (tx.gasPrice > avgGasPrice.mul(3)) {
risks.push({ type: 'high_gas', severity: 'low' });
}
// 检查合约交互
if (tx.to && await this.isContract(tx.to)) {
const contractRisk = await this.assessContractRisk(tx.to);
if (contractRisk.score > 0.7) {
risks.push({ type: 'risky_contract', severity: 'high' });
}
}
return risks;
}
}
// React安全检查组件
const SecurityChecker = ({ contractAddress, transactionData }) => {
const [auditResult, setAuditResult] = useState(null);
const [riskLevel, setRiskLevel] = useState('unknown');
const [securityWarnings, setSecurityWarnings] = useState([]);
const auditor = new SecurityAuditor(provider);
useEffect(() => {
const performSecurityCheck = async () => {
if (!contractAddress) return;
try {
const result = await auditor.auditContract(contractAddress);
setAuditResult(result);
if (result.overallRisk > 0.7) {
setRiskLevel('high');
setSecurityWarnings(['⚠️ 高风险合约,建议谨慎操作']);
} else if (result.overallRisk > 0.4) {
setRiskLevel('medium');
setSecurityWarnings(['⚠️ 中等风险,请仔细检查']);
} else {
setRiskLevel('low');
setSecurityWarnings([]);
}
} catch (error) {
setRiskLevel('unknown');
setSecurityWarnings(['❌ 无法验证合约安全性']);
}
};
performSecurityCheck();
}, [contractAddress]);
const getRiskColor = () => {
switch (riskLevel) {
case 'high': return '#ff4757';
case 'medium': return '#ffa502';
case 'low': return '#2ed573';
default: return '#747d8c';
}
};
return (
<div className="security-checker">
<div className="risk-indicator" style={{ borderColor: getRiskColor() }}>
<h4>安全评估</h4>
<div className="risk-level">
风险等级: <span style={{ color: getRiskColor() }}>{riskLevel.toUpperCase()}</span>
</div>
{auditResult && (
<div className="audit-details">
<div>所有权检查: {auditResult.ownership ? '✅' : '❌'}</div>
<div>可升级性: {auditResult.upgradeable ? '⚠️' : '✅'}</div>
<div>时间锁: {auditResult.timelock ? '✅' : '❌'}</div>
<div>暂停机制: {auditResult.pausable ? '✅' : '❌'}</div>
<div>重入保护: {auditResult.reentrancy ? '✅' : '❌'}</div>
</div>
)}
{securityWarnings.map((warning, index) => (
<div key={index} className="security-warning">
{warning}
</div>
))}
</div>
</div>
);
};
How to handle DeFi protocol upgrades and migrations? Ensure user asset security.
How to handle DeFi protocol upgrades and migrations? Ensure user asset security.
考察点:协议升级处理。
答案:
协议升级需要使用代理模式、时间锁定、多重签名等机制,确保升级过程透明且用户资产安全。
升级管理实现:
// 协议升级管理器
class ProtocolUpgradeManager {
constructor(provider, signer) {
this.provider = provider;
this.signer = signer;
this.upgradeDelay = 48 * 3600; // 48小时延迟
}
// 提议协议升级
async proposeUpgrade(proxyAddress, newImplementation, upgradeData) {
const timelock = new ethers.Contract(TIMELOCK_ADDRESS, TIMELOCK_ABI, this.signer);
const proposalTx = await timelock.schedule(
proxyAddress,
0,
upgradeData,
ethers.constants.HashZero,
this.upgradeDelay
);
return {
proposalId: proposalTx.hash,
executionTime: Date.now() + this.upgradeDelay * 1000,
newImplementation: newImplementation
};
}
// 执行升级
async executeUpgrade(proposalId, targetAddress, upgradeData) {
const timelock = new ethers.Contract(TIMELOCK_ADDRESS, TIMELOCK_ABI, this.signer);
// 检查时间锁是否到期
const isReady = await timelock.isOperationReady(proposalId);
if (!isReady) {
throw new Error('升级提案尚未到执行时间');
}
// 执行升级
const executeTx = await timelock.execute(
targetAddress,
0,
upgradeData,
ethers.constants.HashZero
);
return await executeTx.wait();
}
// 紧急暂停机制
async emergencyPause(contractAddress) {
const pausableContract = new ethers.Contract(
contractAddress,
['function pause() external'],
this.signer
);
const pauseTx = await pausableContract.pause();
return await pauseTx.wait();
}
// 用户资产迁移
async migrateUserAssets(oldContract, newContract, userAddress) {
const migrationSteps = [];
// 1. 获取用户在旧合约的资产
const userBalance = await oldContract.balanceOf(userAddress);
if (userBalance.eq(0)) return [];
// 2. 从旧合约提取资产
migrationSteps.push({
contract: oldContract,
method: 'withdraw',
params: [userBalance],
description: '从旧合约提取资产'
});
// 3. 存入新合约
migrationSteps.push({
contract: newContract,
method: 'deposit',
params: [userBalance],
description: '存入新合约'
});
return migrationSteps;
}
}
// React升级通知组件
const UpgradeNotification = ({ protocolName, upgradeInfo }) => {
const [userChoice, setUserChoice] = useState('pending');
const [migrationStatus, setMigrationStatus] = useState('idle');
const upgradeManager = new ProtocolUpgradeManager(provider, signer);
const handleMigration = async () => {
setMigrationStatus('migrating');
try {
const userAddress = await signer.getAddress();
const migrationSteps = await upgradeManager.migrateUserAssets(
upgradeInfo.oldContract,
upgradeInfo.newContract,
userAddress
);
// 执行迁移步骤
for (const step of migrationSteps) {
await step.contract[step.method](...step.params);
}
setMigrationStatus('completed');
setUserChoice('migrated');
} catch (error) {
setMigrationStatus('failed');
console.error('迁移失败:', error);
}
};
return (
<div className="upgrade-notification">
<div className="notification-header">
<h3>🔄 协议升级通知</h3>
<span className="protocol-name">{protocolName}</span>
</div>
<div className="upgrade-details">
<p>新版本将在 <strong>{upgradeInfo.executionTime}</strong> 激活</p>
<div className="changes-list">
<h4>主要变更:</h4>
<ul>
{upgradeInfo.changes?.map((change, index) => (
<li key={index}>{change}</li>
))}
</ul>
</div>
</div>
<div className="migration-options">
{userChoice === 'pending' && (
<>
<button
className="migrate-btn"
onClick={handleMigration}
disabled={migrationStatus === 'migrating'}
>
{migrationStatus === 'migrating' ? '迁移中...' : '立即迁移'}
</button>
<button
className="wait-btn"
onClick={() => setUserChoice('wait')}
>
稍后迁移
</button>
</>
)}
{migrationStatus === 'completed' && (
<div className="success-message">
✅ 迁移完成!您的资产已安全转移到新合约
</div>
)}
{migrationStatus === 'failed' && (
<div className="error-message">
❌ 迁移失败,请稍后重试或联系技术支持
</div>
)}
</div>
</div>
);
};
What is Layer 2 DeFi? How to adapt to different Layer 2 solutions?
What is Layer 2 DeFi? How to adapt to different Layer 2 solutions?
考察点:Layer 2适配策略。
答案:
Layer 2 DeFi在以太坊二层网络上运行,提供更低的交易费用和更快的确认速度,需要适配不同的Layer 2解决方案。
Layer 2适配实现:
// Layer 2 适配管理器
class Layer2Adapter {
constructor() {
this.networks = {
arbitrum: {
chainId: 42161,
rpc: 'https://arb1.arbitrum.io/rpc',
bridge: '0x8315177aB297bA92A06054cE80a67Ed4DBd7ed3a',
type: 'optimistic'
},
optimism: {
chainId: 10,
rpc: 'https://mainnet.optimism.io',
bridge: '0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1',
type: 'optimistic'
},
polygon: {
chainId: 137,
rpc: 'https://polygon-rpc.com',
bridge: '0xA0c68C638235ee32657e8f720a23ceC1bFc77C77',
type: 'sidechain'
}
};
}
// 自动检测和切换网络
async switchToLayer2(networkName) {
const network = this.networks[networkName];
if (!network) throw new Error('不支持的网络');
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${network.chainId.toString(16)}` }]
});
} catch (error) {
if (error.code === 4902) {
// 网络不存在,添加网络
await this.addNetwork(networkName, network);
} else {
throw error;
}
}
}
// 添加Layer 2网络
async addNetwork(networkName, networkConfig) {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: `0x${networkConfig.chainId.toString(16)}`,
chainName: this.getNetworkDisplayName(networkName),
nativeCurrency: this.getNativeCurrency(networkName),
rpcUrls: [networkConfig.rpc],
blockExplorerUrls: [this.getExplorerUrl(networkName)]
}]
});
}
// 跨Layer 2桥接
async bridgeToLayer2(fromNetwork, toNetwork, asset, amount) {
const fromConfig = this.networks[fromNetwork];
const toConfig = this.networks[toNetwork];
if (fromConfig.type === 'optimistic' && toConfig.type === 'optimistic') {
// 使用通用桥接协议
return await this.useHopProtocol(fromNetwork, toNetwork, asset, amount);
} else {
// 使用原生桥接
return await this.useNativeBridge(fromNetwork, toNetwork, asset, amount);
}
}
// Gas费用比较
async compareGasCosts(operations) {
const costs = {};
for (const [networkName, config] of Object.entries(this.networks)) {
const provider = new ethers.providers.JsonRpcProvider(config.rpc);
const gasPrice = await provider.getGasPrice();
costs[networkName] = {
gasPrice: ethers.utils.formatUnits(gasPrice, 'gwei'),
estimatedCost: await this.estimateOperationCost(provider, operations)
};
}
// 添加主网对比
const mainnetProvider = new ethers.providers.JsonRpcProvider(MAINNET_RPC);
const mainnetGasPrice = await mainnetProvider.getGasPrice();
costs.mainnet = {
gasPrice: ethers.utils.formatUnits(mainnetGasPrice, 'gwei'),
estimatedCost: await this.estimateOperationCost(mainnetProvider, operations)
};
return costs;
}
}
// React Layer 2选择器组件
const Layer2Selector = ({ onNetworkChange, currentNetwork }) => {
const [gasCosts, setGasCosts] = useState({});
const [switching, setSwitching] = useState(false);
const adapter = new Layer2Adapter();
useEffect(() => {
const loadGasCosts = async () => {
const operations = ['swap', 'deposit', 'withdraw'];
const costs = await adapter.compareGasCosts(operations);
setGasCosts(costs);
};
loadGasCosts();
const interval = setInterval(loadGasCosts, 60000); // 每分钟更新
return () => clearInterval(interval);
}, []);
const handleNetworkSwitch = async (networkName) => {
setSwitching(true);
try {
await adapter.switchToLayer2(networkName);
onNetworkChange(networkName);
} catch (error) {
alert(`切换网络失败: ${error.message}`);
} finally {
setSwitching(false);
}
};
return (
<div className="layer2-selector">
<h3>选择Layer 2网络</h3>
<div className="network-options">
{Object.entries(adapter.networks).map(([name, config]) => (
<div
key={name}
className={`network-option ${currentNetwork === name ? 'active' : ''}`}
onClick={() => handleNetworkSwitch(name)}
>
<div className="network-info">
<h4>{this.getNetworkDisplayName(name)}</h4>
<div>类型: {config.type}</div>
{gasCosts[name] && (
<div>Gas: {gasCosts[name].gasPrice} Gwei</div>
)}
</div>
{gasCosts[name] && (
<div className="cost-comparison">
<div>预估费用: ${gasCosts[name].estimatedCost}</div>
<div className="savings">
相比主网节省: {
gasCosts.mainnet ?
(((gasCosts.mainnet.estimatedCost - gasCosts[name].estimatedCost) / gasCosts.mainnet.estimatedCost) * 100).toFixed(1)
: '计算中'
}%
</div>
</div>
)}
</div>
))}
</div>
{switching && (
<div className="switching-indicator">
🔄 正在切换网络,请在钱包中确认...
</div>
)}
</div>
);
};
How to design user experience for DeFi applications? Balance complexity and usability.
How to design user experience for DeFi applications? Balance complexity and usability.
考察点:DeFi UX设计。
答案:
DeFi应用需要在保持功能完整性的同时简化用户操作流程,提供清晰的信息展示和风险提示,优化用户体验。
UX设计实现:
// DeFi UX优化组件库
const DeFiUXComponents = {
// 简化的交易流程
SimplifiedSwapInterface: ({ onSwap }) => {
const [fromToken, setFromToken] = useState('ETH');
const [toToken, setToToken] = useState('USDC');
const [amount, setAmount] = useState('');
const [outputAmount, setOutputAmount] = useState('0');
const [priceImpact, setPriceImpact] = useState(0);
const handleAmountChange = async (value) => {
setAmount(value);
if (value && parseFloat(value) > 0) {
const quote = await getSwapQuote(fromToken, toToken, value);
setOutputAmount(quote.outputAmount);
setPriceImpact(quote.priceImpact);
}
};
return (
<div className="simplified-swap">
<div className="swap-card">
<div className="token-input">
<span>从</span>
<input
type="number"
value={amount}
onChange={(e) => handleAmountChange(e.target.value)}
placeholder="0.0"
/>
<TokenSelector
selected={fromToken}
onChange={setFromToken}
/>
</div>
<div className="swap-arrow" onClick={() => {
setFromToken(toToken);
setToToken(fromToken);
}}>
⇅
</div>
<div className="token-output">
<span>到</span>
<div className="output-amount">{outputAmount}</div>
<TokenSelector
selected={toToken}
onChange={setToToken}
/>
</div>
{priceImpact > 0 && (
<div className="price-impact-warning">
<span>价格影响: {priceImpact.toFixed(2)}%</span>
{priceImpact > 3 && <span>⚠️ 影响较大</span>}
</div>
)}
<button
className="swap-button"
onClick={() => onSwap({ fromToken, toToken, amount })}
disabled={!amount || parseFloat(amount) <= 0}
>
交换
</button>
</div>
</div>
);
},
// 智能风险提示
RiskIndicator: ({ riskLevel, details }) => {
const getRiskConfig = () => {
switch (riskLevel) {
case 'low':
return { color: '#2ed573', icon: '✅', message: '风险较低' };
case 'medium':
return { color: '#ffa502', icon: '⚠️', message: '中等风险' };
case 'high':
return { color: '#ff4757', icon: '🚨', message: '高风险' };
default:
return { color: '#747d8c', icon: '❓', message: '未知风险' };
}
};
const config = getRiskConfig();
return (
<div className="risk-indicator" style={{ borderColor: config.color }}>
<div className="risk-header">
<span className="risk-icon">{config.icon}</span>
<span className="risk-message" style={{ color: config.color }}>
{config.message}
</span>
</div>
{details && (
<div className="risk-details">
{details.map((detail, index) => (
<div key={index} className="risk-item">
<span className="risk-type">{detail.type}:</span>
<span className="risk-value">{detail.value}</span>
</div>
))}
</div>
)}
</div>
);
},
// 一键操作按钮
OneClickAction: ({ action, loading, disabled, onClick, children }) => {
return (
<button
className={`one-click-action ${action} ${loading ? 'loading' : ''}`}
disabled={disabled || loading}
onClick={onClick}
>
{loading ? (
<div className="loading-spinner">⏳ 处理中...</div>
) : (
children
)}
</button>
);
},
// 进度跟踪器
TransactionTracker: ({ transactions }) => {
return (
<div className="transaction-tracker">
<h4>交易进度</h4>
{transactions.map((tx, index) => (
<div key={index} className={`tx-step ${tx.status}`}>
<div className="step-indicator">
{tx.status === 'pending' && '⏳'}
{tx.status === 'confirmed' && '✅'}
{tx.status === 'failed' && '❌'}
</div>
<div className="step-content">
<div className="step-title">{tx.title}</div>
{tx.hash && (
<div className="tx-hash">
<a href={`https://etherscan.io/tx/${tx.hash}`} target="_blank" rel="noopener noreferrer">
查看交易
</a>
</div>
)}
</div>
</div>
))}
</div>
);
}
};
// 主要的DeFi应用界面
const DeFiAppInterface = () => {
const [activeTab, setActiveTab] = useState('swap');
const [userEducationMode, setUserEducationMode] = useState(false);
const [transactionHistory, setTransactionHistory] = useState([]);
const handleSwap = async (swapData) => {
const txId = Date.now().toString();
// 添加到交易跟踪
setTransactionHistory(prev => [...prev, {
id: txId,
title: `交换 ${swapData.amount} ${swapData.fromToken} → ${swapData.toToken}`,
status: 'pending',
timestamp: new Date()
}]);
try {
// 执行交换逻辑
const result = await executeSwap(swapData);
// 更新交易状态
setTransactionHistory(prev =>
prev.map(tx =>
tx.id === txId
? { ...tx, status: 'confirmed', hash: result.hash }
: tx
)
);
} catch (error) {
setTransactionHistory(prev =>
prev.map(tx =>
tx.id === txId
? { ...tx, status: 'failed', error: error.message }
: tx
)
);
}
};
return (
<div className="defi-app">
<header className="app-header">
<h1>DeFi Hub</h1>
<div className="header-controls">
<label>
<input
type="checkbox"
checked={userEducationMode}
onChange={(e) => setUserEducationMode(e.target.checked)}
/>
新手模式
</label>
</div>
</header>
<nav className="app-navigation">
<button
className={activeTab === 'swap' ? 'active' : ''}
onClick={() => setActiveTab('swap')}
>
交换
</button>
<button
className={activeTab === 'pool' ? 'active' : ''}
onClick={() => setActiveTab('pool')}
>
流动性
</button>
<button
className={activeTab === 'farm' ? 'active' : ''}
onClick={() => setActiveTab('farm')}
>
挖矿
</button>
</nav>
<main className="app-content">
{activeTab === 'swap' && (
<DeFiUXComponents.SimplifiedSwapInterface onSwap={handleSwap} />
)}
{userEducationMode && (
<div className="education-panel">
<h3>💡 提示</h3>
<p>交换代币时请注意滑点和价格影响。建议在交易前了解相关风险。</p>
</div>
)}
{transactionHistory.length > 0 && (
<DeFiUXComponents.TransactionTracker transactions={transactionHistory} />
)}
</main>
</div>
);
};
UX设计原则:
What is a smart contract? How does it differ from traditional programs?
What is a smart contract? How does it differ from traditional programs?
考察点:智能合约基本概念。
答案:
智能合约是运行在区块链网络上的自动执行程序,其中合约条款直接写入代码。它是一种去中心化的、不可篡改的程序,一旦部署到区块链上,就会按照预设的规则自动执行,无需中介机构参与。
智能合约具有透明性、不可篡改性和去信任化的特点,所有交易和执行过程都记录在区块链上,任何人都可以验证。
主要区别:
执行环境:
信任机制:
可修改性:
执行成本:
常见应用场景:
What are the main differences between Web3.js and Ethers.js? What are their respective advantages?
What are the main differences between Web3.js and Ethers.js? What are their respective advantages?
考察点:Web3库选择与对比。
答案:
Web3.js 和 Ethers.js 都是用于与以太坊区块链交互的 JavaScript 库,但在设计理念和使用方式上有显著区别。Web3.js 是较早出现的库,而 Ethers.js 是后来开发的现代化替代方案。
主要区别:
架构设计:
API设计:
类型安全:
Web3.js 优势:
Ethers.js 优势:
代码示例对比:
// Web3.js
const Web3 = require('web3');
const web3 = new Web3('https://mainnet.infura.io/v3/YOUR-PROJECT-ID');
// Ethers.js
const { ethers } = require('ethers');
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR-PROJECT-ID');
What is ABI (Application Binary Interface)? What role does it play in smart contract interaction?
What is ABI (Application Binary Interface)? What role does it play in smart contract interaction?
考察点:ABI基础概念与作用。
答案:
ABI(Application Binary Interface,应用二进制接口)是一个JSON格式的描述文件,定义了智能合约中函数和事件的接口规范。它描述了如何与智能合约的字节码进行交互,包括函数名、参数类型、返回值类型等信息。
ABI 充当了前端应用程序与区块链上智能合约之间的桥梁,使得外部应用能够正确地调用合约函数和解析合约事件。
主要作用:
函数调用编码:
返回值解码:
事件解析:
类型验证:
ABI 结构示例:
[
{
"inputs": [
{"name": "_to", "type": "address"},
{"name": "_value", "type": "uint256"}
],
"name": "transfer",
"outputs": [{"name": "", "type": "bool"}],
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{"indexed": true, "name": "from", "type": "address"},
{"indexed": true, "name": "to", "type": "address"},
{"indexed": false, "name": "value", "type": "uint256"}
],
"name": "Transfer",
"type": "event"
}
]
使用示例:
// 使用 ABI 创建合约实例
const contract = new ethers.Contract(contractAddress, abi, provider);
// 调用合约函数
const result = await contract.transfer("0x...", ethers.utils.parseEther("1.0"));
How to connect to the Ethereum network? What are the differences between mainnet and testnets?
How to connect to the Ethereum network? What are the differences between mainnet and testnets?
考察点:网络连接基础。
答案:
连接以太坊网络需要通过 RPC 节点提供商或本地节点。常见的连接方式包括使用 Infura、Alchemy、QuickNode 等服务提供商,或者连接到本地运行的以太坊客户端。
连接方法:
// 使用 Ethers.js
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR-PROJECT-ID');
// 使用 Web3.js
const web3 = new Web3('https://mainnet.infura.io/v3/YOUR-PROJECT-ID');
// 连接到用户的钱包
const provider = new ethers.providers.Web3Provider(window.ethereum);
const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
主网和测试网区别:
| 特征 | 主网 (Mainnet) | 测试网 (Testnet) |
|---|---|---|
| 真实价值 | ETH 具有真实经济价值 | 测试 ETH 无经济价值 |
| 用途 | 生产环境,真实交易 | 开发测试,实验功能 |
| Gas 费用 | 需要真实 ETH 支付 | 免费获取测试 ETH |
| 数据持久性 | 永久保存 | 可能定期重置 |
| 网络稳定性 | 高度稳定 | 可能不稳定 |
常见测试网:
网络配置示例:
const networks = {
mainnet: {
name: 'Ethereum Mainnet',
rpcUrl: 'https://mainnet.infura.io/v3/YOUR-PROJECT-ID',
chainId: 1
},
goerli: {
name: 'Goerli Testnet',
rpcUrl: 'https://goerli.infura.io/v3/YOUR-PROJECT-ID',
chainId: 5
},
sepolia: {
name: 'Sepolia Testnet',
rpcUrl: 'https://sepolia.infura.io/v3/YOUR-PROJECT-ID',
chainId: 11155111
}
};
最佳实践:
What is a contract address? How to obtain and verify contract addresses?
What is a contract address? How to obtain and verify contract addresses?
考察点:合约地址概念与验证。
答案:
合约地址是智能合约在区块链上的唯一标识符,是一个42字符的十六进制字符串(包含0x前缀)。与普通账户地址不同,合约地址是在部署合约时由区块链网络自动生成的,基于部署者地址和其nonce值计算得出。
合约地址是与智能合约交互的必要条件,所有对合约的调用都需要指定正确的合约地址。
地址生成规则:
合约地址 = keccak256(rlp.encode([部署者地址, nonce]))[12:]
获取合约地址的方法:
// 部署合约时获取地址
const contractFactory = new ethers.ContractFactory(abi, bytecode, signer);
const contract = await contractFactory.deploy();
await contract.deployed();
console.log("合约地址:", contract.address);
// 从部署交易收据中获取
const receipt = await provider.getTransactionReceipt(deployTxHash);
console.log("合约地址:", receipt.contractAddress);
验证合约地址的方法:
function isValidAddress(address) {
return ethers.utils.isAddress(address);
}
async function isContract(address, provider) {
const code = await provider.getCode(address);
return code !== '0x';
}
// 获取合约字节码
const bytecode = await provider.getCode(contractAddress);
if (bytecode === '0x') {
console.log("该地址不是合约地址");
} else {
console.log("合约字节码:", bytecode);
}
安全注意事项:
常见验证工具:
How to read public state variables of smart contracts?
How to read public state variables of smart contracts?
考察点:合约状态读取基础。
答案:
读取智能合约的公共状态变量是与区块链交互的基础操作。公共状态变量会自动生成对应的 getter 函数,可以通过这些函数读取变量的当前值。读取操作不需要消耗 Gas,因为它们不会改变区块链状态。
读取方法:
// 创建合约实例
const contract = new ethers.Contract(contractAddress, abi, provider);
// 读取公共变量
const totalSupply = await contract.totalSupply();
const owner = await contract.owner();
const balance = await contract.balanceOf(userAddress);
console.log("总供应量:", totalSupply.toString());
console.log("合约所有者:", owner);
console.log("用户余额:", ethers.utils.formatEther(balance));
const contract = new web3.eth.Contract(abi, contractAddress);
// 读取状态变量
const totalSupply = await contract.methods.totalSupply().call();
const owner = await contract.methods.owner().call();
const balance = await contract.methods.balanceOf(userAddress).call();
// 使用 eth_call 方法
const data = contract.interface.encodeFunctionData("totalSupply", []);
const result = await provider.call({
to: contractAddress,
data: data
});
const decoded = contract.interface.decodeFunctionResult("totalSupply", result);
Solidity 合约示例:
contract MyToken {
string public name = "MyToken"; // 自动生成 name() 函数
uint256 public totalSupply = 1000000; // 自动生成 totalSupply() 函数
address public owner; // 自动生成 owner() 函数
mapping(address => uint256) public balanceOf; // 自动生成 balanceOf(address) 函数
constructor() {
owner = msg.sender;
}
}
批量读取示例:
// 批量读取多个状态变量
async function getContractInfo(contract) {
const [name, symbol, totalSupply, owner] = await Promise.all([
contract.name(),
contract.symbol(),
contract.totalSupply(),
contract.owner()
]);
return {
name,
symbol,
totalSupply: totalSupply.toString(),
owner
};
}
注意事项:
最佳实践:
What are the differences between calling view and pure functions in contracts?
What are the differences between calling view and pure functions in contracts?
考察点:合约函数类型理解。
答案:
view 函数和 pure 函数是 Solidity 中两种不同的只读函数修饰符,它们都不会修改区块链状态,因此调用时不需要消耗 Gas 费用。但它们在对状态数据的访问权限上有重要区别。
主要区别:
状态访问权限:
允许的操作:
view 函数可以:
pure 函数只能:
代码示例:
contract Example {
uint256 public balance = 1000;
string public name = "MyContract";
// view 函数 - 可以读取状态变量
function getBalance() public view returns (uint256) {
return balance; // 读取状态变量
}
function getInfo() public view returns (string memory, uint256, address) {
return (name, balance, msg.sender); // 读取状态和区块信息
}
// pure 函数 - 只能处理传入参数
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b; // 纯数学计算
}
function calculateHash(string memory text) public pure returns (bytes32) {
return keccak256(abi.encodePacked(text)); // 纯函数计算
}
}
JavaScript 调用示例:
// 调用 view 函数
const balance = await contract.getBalance();
const info = await contract.getInfo();
// 调用 pure 函数
const sum = await contract.add(10, 20);
const hash = await contract.calculateHash("hello");
console.log("余额:", balance.toString());
console.log("计算结果:", sum.toString());
性能特点:
使用场景:
view 函数适用于:
pure 函数适用于:
注意事项:
What are Gas limit and Gas price? How to set them reasonably?
What are Gas limit and Gas price? How to set them reasonably?
考察点:Gas机制基础理解。
答案:
Gas limit 和 Gas price 是以太坊网络中控制交易执行成本和优先级的两个重要参数。它们共同决定了交易的最终费用和处理速度。
Gas Limit:
Gas limit 是用户愿意为一笔交易支付的最大计算单位数量。它设定了交易可以消耗的计算资源上限,防止无限循环或恶意代码消耗过多资源。
Gas Price:
Gas price 是用户愿意为每单位 Gas 支付的价格,通常以 Gwei(1 Gwei = 10^-9 ETH)为单位。它决定了交易的优先级,矿工/验证者倾向于优先处理 Gas price 更高的交易。
总费用计算:
交易费用 = Gas Used × Gas Price
设置策略:
// 简单转账
const gasLimit = 21000;
// 合约交互 - 使用估算
const estimatedGas = await contract.estimateGas.transfer(to, amount);
const gasLimit = estimatedGas.mul(120).div(100); // 增加20%缓冲
// 复杂操作
const gasLimit = 500000; // 根据具体合约功能设定
// 获取网络建议价格
const gasPrice = await provider.getGasPrice();
// 手动设置(快速确认)
const gasPrice = ethers.utils.parseUnits('20', 'gwei');
// 分级设置
const gasPrices = {
slow: ethers.utils.parseUnits('10', 'gwei'), // 慢速
standard: ethers.utils.parseUnits('20', 'gwei'), // 标准
fast: ethers.utils.parseUnits('40', 'gwei') // 快速
};
实际应用示例:
// 发送交易时设置 Gas 参数
const tx = {
to: recipientAddress,
value: ethers.utils.parseEther("1.0"),
gasLimit: 21000,
gasPrice: ethers.utils.parseUnits('20', 'gwei')
};
const transaction = await signer.sendTransaction(tx);
EIP-1559 后的变化:
// 使用 maxFeePerGas 和 maxPriorityFeePerGas
const tx = {
to: recipientAddress,
value: ethers.utils.parseEther("1.0"),
gasLimit: 21000,
maxFeePerGas: ethers.utils.parseUnits('30', 'gwei'),
maxPriorityFeePerGas: ethers.utils.parseUnits('2', 'gwei')
};
合理设置原则:
Gas Limit:
Gas Price:
常用工具:
注意事项:
How to estimate the Gas cost required for transactions?
How to estimate the Gas cost required for transactions?
考察点:Gas估算方法。
答案:
Gas 费用估算是区块链交易中的重要环节,准确的估算可以避免交易失败,同时防止支付过高的手续费。估算方法包括使用内置函数、查询网络状态和参考历史数据等。
主要估算方法:
// Ethers.js 估算
const contract = new ethers.Contract(contractAddress, abi, signer);
// 估算合约函数调用
const estimatedGas = await contract.estimateGas.transfer(
recipientAddress,
ethers.utils.parseEther("1.0")
);
console.log("估算 Gas:", estimatedGas.toString());
// 估算普通转账
const estimatedGas = await signer.estimateGas({
to: recipientAddress,
value: ethers.utils.parseEther("1.0")
});
// 估算合约调用
const gasEstimate = await contract.methods.transfer(to, amount).estimateGas({
from: userAddress
});
// 估算转账
const gasEstimate = await web3.eth.estimateGas({
to: recipientAddress,
from: userAddress,
value: web3.utils.toWei('1', 'ether')
});
// 增加 20% 安全缓冲
const gasLimit = estimatedGas.mul(120).div(100);
// 或使用固定增量
const gasLimit = estimatedGas.add(50000);
费用计算:
// 获取当前 Gas 价格
const gasPrice = await provider.getGasPrice();
// 计算总费用
const totalCost = gasLimit.mul(gasPrice);
const costInEther = ethers.utils.formatEther(totalCost);
console.log(`预估费用: ${costInEther} ETH`);
不同交易类型的典型 Gas 消耗:
| 交易类型 | 典型 Gas 消耗 |
|---|---|
| ETH 转账 | 21,000 |
| ERC-20 转账 | 60,000 - 80,000 |
| Uniswap 交换 | 150,000 - 300,000 |
| NFT 铸造 | 100,000 - 200,000 |
| 合约部署 | 500,000 - 2,000,000+ |
动态价格获取:
// 获取网络建议的 Gas 价格
async function getGasPrices(provider) {
const gasPrice = await provider.getGasPrice();
return {
slow: gasPrice.mul(80).div(100), // 慢速 (80%)
standard: gasPrice, // 标准 (100%)
fast: gasPrice.mul(120).div(100) // 快速 (120%)
};
}
// EIP-1559 网络的费用估算
async function estimateEIP1559Fees(provider) {
const feeData = await provider.getFeeData();
return {
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
};
}
完整估算示例:
async function estimateTransactionCost(contract, method, params) {
try {
// 估算 Gas limit
const gasLimit = await contract.estimateGas[method](...params);
const safeGasLimit = gasLimit.mul(110).div(100); // +10% 缓冲
// 获取 Gas 价格
const gasPrice = await contract.provider.getGasPrice();
// 计算费用
const cost = safeGasLimit.mul(gasPrice);
return {
gasLimit: safeGasLimit,
gasPrice: gasPrice,
cost: cost,
costInEther: ethers.utils.formatEther(cost)
};
} catch (error) {
console.error("估算失败:", error);
throw error;
}
}
第三方 API 服务:
// 使用 ETH Gas Station API
async function getGasStationData() {
const response = await fetch('https://ethgasstation.info/api/ethgasAPI.json');
const data = await response.json();
return {
slow: data.safeLow / 10, // Gwei
standard: data.average / 10, // Gwei
fast: data.fast / 10 // Gwei
};
}
注意事项:
最佳实践:
What is a transaction hash? How to query transaction status through transaction hash?
What is a transaction hash? How to query transaction status through transaction hash?
考察点:交易跟踪基础。
答案:
交易哈希(Transaction Hash)是每笔区块链交易的唯一标识符,是一个64字符的十六进制字符串。它是通过对交易数据进行 Keccak-256 哈希算法计算得出的,具有唯一性和不可篡改性,用于在区块链网络中追踪和验证交易。
交易哈希在交易提交到网络时即刻生成,无论交易最终成功还是失败,都会有对应的哈希值。
交易哈希的获取:
// 发送交易时获取哈希
const tx = await signer.sendTransaction({
to: recipientAddress,
value: ethers.utils.parseEther("1.0")
});
console.log("交易哈希:", tx.hash);
// 示例: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
查询交易状态的方法:
// 获取交易详细信息
const transaction = await provider.getTransaction(txHash);
console.log("交易信息:", transaction);
// 获取交易收据(仅已确认的交易)
const receipt = await provider.getTransactionReceipt(txHash);
console.log("交易收据:", receipt);
// 等待交易确认
const receipt = await provider.waitForTransaction(txHash);
console.log("交易已确认:", receipt);
async function getTransactionStatus(txHash, provider) {
try {
const receipt = await provider.getTransactionReceipt(txHash);
if (!receipt) {
return "pending"; // 交易待确认
}
if (receipt.status === 1) {
return "success"; // 交易成功
} else {
return "failed"; // 交易失败
}
} catch (error) {
return "not_found"; // 交易不存在
}
}
// 等待指定确认数
async function waitForConfirmations(txHash, confirmations = 6) {
const receipt = await provider.waitForTransaction(txHash, confirmations);
console.log(`交易已获得 ${confirmations} 个确认`);
return receipt;
}
// 实时监听交易状态
function watchTransaction(txHash) {
provider.on(txHash, (transaction) => {
console.log("交易已确认:", transaction);
});
}
交易收据信息解析:
const receipt = await provider.getTransactionReceipt(txHash);
console.log({
transactionHash: receipt.transactionHash, // 交易哈希
blockNumber: receipt.blockNumber, // 区块号
blockHash: receipt.blockHash, // 区块哈希
status: receipt.status, // 状态 (1=成功, 0=失败)
from: receipt.from, // 发送方地址
to: receipt.to, // 接收方地址
gasUsed: receipt.gasUsed.toString(), // 实际消耗的 Gas
effectiveGasPrice: receipt.effectiveGasPrice, // 有效 Gas 价格
logs: receipt.logs // 事件日志
});
错误处理和重试机制:
async function queryTransactionWithRetry(txHash, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const receipt = await provider.getTransactionReceipt(txHash);
return receipt;
} catch (error) {
if (i === maxRetries - 1) throw error;
// 等待后重试
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
使用区块浏览器 API:
// Etherscan API 查询示例
async function queryEtherscan(txHash, apiKey) {
const url = `https://api.etherscan.io/api?module=transaction&action=gettxreceiptstatus&txhash=${txHash}&apikey=${apiKey}`;
const response = await fetch(url);
const data = await response.json();
return data.result;
}
交易状态的应用场景:
最佳实践:
What is the role of Events in smart contracts? How to listen to events?
What is the role of Events in smart contracts? How to listen to events?
考察点:事件机制基础。
答案:
智能合约事件是一种特殊的日志机制,用于记录合约执行过程中的重要信息。事件在区块链上永久存储,但不占用合约存储空间,提供了一种高效的数据记录和通信方式。前端应用可以通过监听事件来获取合约状态变化的实时通知。
事件的主要作用:
Solidity 事件定义示例:
contract MyToken {
// 定义事件
event Transfer(
address indexed from, // indexed 参数可以被过滤
address indexed to,
uint256 value // 非 indexed 参数存储在日志数据中
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
function transfer(address to, uint256 amount) public {
// 执行转账逻辑...
// 触发事件
emit Transfer(msg.sender, to, amount);
}
}
监听事件的方法:
// 监听所有 Transfer 事件
contract.on("Transfer", (from, to, value, event) => {
console.log({
from: from,
to: to,
value: ethers.utils.formatEther(value),
blockNumber: event.blockNumber,
transactionHash: event.transactionHash
});
});
// 监听特定地址的转入事件
const filter = contract.filters.Transfer(null, userAddress);
contract.on(filter, (from, to, value, event) => {
console.log(`${userAddress} 收到转账:`, ethers.utils.formatEther(value));
});
// 查询指定区块范围的事件
const filter = contract.filters.Transfer();
const events = await contract.queryFilter(filter, fromBlock, toBlock);
events.forEach(event => {
console.log({
from: event.args.from,
to: event.args.to,
value: event.args.value.toString(),
blockNumber: event.blockNumber
});
});
// 监听所有新区块中的事件
provider.on("block", async (blockNumber) => {
const events = await contract.queryFilter("Transfer", blockNumber);
events.forEach(event => {
console.log("新的转账事件:", event.args);
});
});
高级事件监听:
// 复合条件过滤
const multiFilter = {
address: contractAddress,
topics: [
ethers.utils.id("Transfer(address,address,uint256)"),
null, // from (任意)
ethers.utils.hexZeroPad(userAddress, 32) // to (特定用户)
]
};
provider.on(multiFilter, (log) => {
const parsed = contract.interface.parseLog(log);
console.log("解析后的事件:", parsed);
});
事件数据解析:
// 手动解析事件日志
function parseTransferEvent(log) {
const iface = new ethers.utils.Interface([
"event Transfer(address indexed from, address indexed to, uint256 value)"
]);
try {
const parsed = iface.parseLog(log);
return {
eventName: parsed.name,
from: parsed.args.from,
to: parsed.args.to,
value: parsed.args.value
};
} catch (error) {
console.error("事件解析失败:", error);
return null;
}
}
实际应用场景:
// DeFi 应用中监听流动性变化
contract.on("LiquidityAdded", (provider, tokenA, tokenB, amount, event) => {
updateLiquidityUI(tokenA, tokenB, amount);
showNotification("流动性已增加");
});
// NFT 市场监听交易
nftContract.on("Transfer", (from, to, tokenId) => {
if (from === "0x0000000000000000000000000000000000000000") {
console.log(`NFT ${tokenId} 被铸造给 ${to}`);
} else {
console.log(`NFT ${tokenId} 从 ${from} 转移给 ${to}`);
}
});
性能优化建议:
注意事项:
How to send Ether to a contract address?
How to send Ether to a contract address?
考察点:基础转账操作。
答案:
向智能合约发送以太币有多种方式,主要取决于合约是否实现了接收以太币的特殊函数。合约可以通过 receive() 函数、fallback() 函数或带有 payable 修饰符的普通函数来接收以太币。
发送以太币的方法:
// 使用 Ethers.js 发送以太币
const tx = await signer.sendTransaction({
to: contractAddress,
value: ethers.utils.parseEther("1.0") // 发送 1 ETH
});
console.log("交易哈希:", tx.hash);
await tx.wait(); // 等待确认
// 调用带有 payable 修饰符的函数并发送以太币
const contract = new ethers.Contract(contractAddress, abi, signer);
const tx = await contract.deposit({
value: ethers.utils.parseEther("0.5")
});
await tx.wait();
// Web3.js 发送以太币到合约
const tx = await web3.eth.sendTransaction({
from: userAddress,
to: contractAddress,
value: web3.utils.toWei('1', 'ether'),
gas: 21000
});
合约端接收以太币的实现:
contract MyContract {
mapping(address => uint256) public balances;
// receive 函数 - 接收纯以太币转账
receive() external payable {
balances[msg.sender] += msg.value;
emit Received(msg.sender, msg.value);
}
// fallback 函数 - 接收带有数据的转账
fallback() external payable {
balances[msg.sender] += msg.value;
emit FallbackReceived(msg.sender, msg.value, msg.data);
}
// 显式的存款函数
function deposit() public payable {
require(msg.value > 0, "必须发送一些以太币");
balances[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
// 提取以太币
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "余额不足");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
}
// 获取合约余额
function getContractBalance() public view returns (uint256) {
return address(this).balance;
}
event Received(address sender, uint256 amount);
event FallbackReceived(address sender, uint256 amount, bytes data);
event Deposited(address sender, uint256 amount);
}
发送以太币的完整示例:
async function sendEtherToContract() {
try {
// 检查合约是否能接收以太币
const code = await provider.getCode(contractAddress);
if (code === '0x') {
throw new Error("目标地址不是合约");
}
// 获取当前 Gas 价格
const gasPrice = await provider.getGasPrice();
// 发送交易
const tx = await signer.sendTransaction({
to: contractAddress,
value: ethers.utils.parseEther("1.0"),
gasLimit: 100000, // 适当设置 Gas limit
gasPrice: gasPrice
});
console.log(`交易已发送: ${tx.hash}`);
// 等待确认
const receipt = await tx.wait();
console.log(`交易已确认,区块号: ${receipt.blockNumber}`);
return receipt;
} catch (error) {
console.error("发送失败:", error);
throw error;
}
}
批量发送示例:
// 向多个合约发送以太币
async function batchSendEther(recipients, amount) {
const txPromises = recipients.map(address =>
signer.sendTransaction({
to: address,
value: ethers.utils.parseEther(amount.toString())
})
);
const transactions = await Promise.all(txPromises);
// 等待所有交易确认
const receipts = await Promise.all(
transactions.map(tx => tx.wait())
);
return receipts;
}
安全检查和错误处理:
async function safeSendEther(contractAddress, amount) {
// 1. 验证地址格式
if (!ethers.utils.isAddress(contractAddress)) {
throw new Error("无效的合约地址");
}
// 2. 检查账户余额
const balance = await signer.getBalance();
if (balance.lt(amount)) {
throw new Error("账户余额不足");
}
// 3. 检查合约是否存在
const code = await provider.getCode(contractAddress);
if (code === '0x') {
throw new Error("目标地址不是合约");
}
// 4. 估算 Gas 费用
const gasEstimate = await provider.estimateGas({
to: contractAddress,
value: amount
});
// 5. 发送交易
const tx = await signer.sendTransaction({
to: contractAddress,
value: amount,
gasLimit: gasEstimate.mul(110).div(100) // +10% 缓冲
});
return tx;
}
注意事项:
receive() 或 fallback() 函数才能接收直接转账payable 函数时可以同时发送以太币常见错误:
What is nonce? What role does it play in transactions?
What is nonce? What role does it play in transactions?
考察点:交易nonce理解。
答案:
nonce 是"number only used once"的缩写,在以太坊中指账户发送交易的序列号。每个账户都有一个 nonce 值,从0开始,每发送一笔交易就递增1。nonce 确保交易的唯一性和顺序性,防止重放攻击和双重支付问题。
nonce 是交易安全机制的重要组成部分,它确保了每笔交易只能被执行一次,并且必须按照正确的顺序执行。
nonce 的主要作用:
nonce 的获取和使用:
// 获取账户当前的 nonce
const currentNonce = await provider.getTransactionCount(userAddress);
console.log("当前 nonce:", currentNonce);
// 获取包含待确认交易的 nonce
const pendingNonce = await provider.getTransactionCount(userAddress, "pending");
console.log("待确认 nonce:", pendingNonce);
手动设置 nonce:
// 发送交易时指定 nonce
const tx = await signer.sendTransaction({
to: recipientAddress,
value: ethers.utils.parseEther("1.0"),
nonce: currentNonce // 手动指定 nonce
});
nonce 管理策略:
// Ethers.js 自动管理 nonce
const tx = await signer.sendTransaction({
to: recipientAddress,
value: ethers.utils.parseEther("1.0")
// 不指定 nonce,由库自动处理
});
async function sendMultipleTransactions(transactions) {
let nonce = await provider.getTransactionCount(userAddress);
const txPromises = transactions.map((txData, index) => {
return signer.sendTransaction({
...txData,
nonce: nonce + index // 依次递增 nonce
});
});
return Promise.all(txPromises);
}
class NonceManager {
constructor(provider, address) {
this.provider = provider;
this.address = address;
this.currentNonce = null;
}
async init() {
this.currentNonce = await this.provider.getTransactionCount(this.address);
}
getNextNonce() {
return this.currentNonce++;
}
async reset() {
this.currentNonce = await this.provider.getTransactionCount(this.address);
}
}
// 使用示例
const nonceManager = new NonceManager(provider, userAddress);
await nonceManager.init();
const tx1 = await signer.sendTransaction({
to: address1,
value: amount1,
nonce: nonceManager.getNextNonce()
});
const tx2 = await signer.sendTransaction({
to: address2,
value: amount2,
nonce: nonceManager.getNextNonce()
});
nonce 相关问题和解决方案:
// 错误:nonce too low
// 解决:获取最新的 nonce
try {
const tx = await signer.sendTransaction(txData);
} catch (error) {
if (error.code === 'NONCE_EXPIRED') {
// 重新获取 nonce 并重试
const newNonce = await provider.getTransactionCount(userAddress);
const retryTx = await signer.sendTransaction({
...txData,
nonce: newNonce
});
}
}
// 如果有 nonce 间隙,后续交易会被卡住
async function checkNonceGap(address) {
const confirmedNonce = await provider.getTransactionCount(address);
const pendingNonce = await provider.getTransactionCount(address, "pending");
if (pendingNonce > confirmedNonce) {
console.log(`存在 ${pendingNonce - confirmedNonce} 个待确认交易`);
}
}
// 使用相同 nonce 但更高 Gas 价格替换交易
async function replaceTransaction(originalTx, newGasPrice) {
const replaceTx = await signer.sendTransaction({
to: originalTx.to,
value: originalTx.value,
nonce: originalTx.nonce, // 使用相同 nonce
gasPrice: newGasPrice, // 提高 Gas 价格
gasLimit: originalTx.gasLimit
});
return replaceTx;
}
nonce 监控和调试:
// 监控账户 nonce 变化
async function monitorNonce(address) {
let lastNonce = await provider.getTransactionCount(address);
setInterval(async () => {
const currentNonce = await provider.getTransactionCount(address);
if (currentNonce !== lastNonce) {
console.log(`Nonce 更新: ${lastNonce} -> ${currentNonce}`);
lastNonce = currentNonce;
}
}, 10000); // 每10秒检查一次
}
最佳实践:
注意事项:
What are the common reasons for contract call failures?
What are the common reasons for contract call failures?
考察点:错误处理基础。
答案:
智能合约调用失败是开发过程中的常见问题,了解失败原因有助于快速定位和解决问题。合约调用失败主要分为交易级别失败和合约逻辑失败两大类。
常见失败原因分类:
1. Gas 相关问题:
// Gas limit 过低
try {
const tx = await contract.complexFunction({
gasLimit: 50000 // 可能过低
});
} catch (error) {
// Error: out of gas
console.log("Gas 不足,需要增加 gasLimit");
}
// Gas price 过低导致交易长时间不被确认
const tx = await contract.someFunction({
gasPrice: ethers.utils.parseUnits('1', 'gwei') // 过低的 Gas 价格
});
2. 权限和访问控制:
// 合约中的权限检查
contract MyContract {
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "只有所有者可以调用");
_;
}
function adminFunction() public onlyOwner {
// 只有 owner 可以调用
}
}
// 调用时权限不足
try {
await contract.adminFunction(); // 非 owner 调用
} catch (error) {
console.log("权限不足:", error.message);
}
3. 参数验证失败:
contract TokenContract {
function transfer(address to, uint256 amount) public {
require(to != address(0), "接收地址不能为零地址");
require(amount > 0, "转账金额必须大于零");
require(balanceOf[msg.sender] >= amount, "余额不足");
// ...
}
}
4. 状态条件不满足:
// 合约状态检查失败
try {
await contract.withdraw(ethers.utils.parseEther("100"));
} catch (error) {
// 可能的错误:"余额不足"、"合约已暂停"等
console.log("状态检查失败:", error.reason);
}
5. 重入攻击保护:
contract SecureContract {
bool private locked;
modifier noReentrancy() {
require(!locked, "重入攻击检测");
locked = true;
_;
locked = false;
}
function withdraw() external noReentrancy {
// 防重入的提取逻辑
}
}
6. 网络和连接问题:
// 网络超时或连接问题
async function robustContractCall() {
const maxRetries = 3;
let retries = 0;
while (retries < maxRetries) {
try {
const result = await contract.someFunction();
return result;
} catch (error) {
if (error.code === 'NETWORK_ERROR' && retries < maxRetries - 1) {
retries++;
await new Promise(resolve => setTimeout(resolve, 1000 * retries));
} else {
throw error;
}
}
}
}
7. ABI 不匹配:
// ABI 与合约实际接口不匹配
try {
await contract.nonExistentFunction(); // 函数不存在
} catch (error) {
console.log("函数不存在或 ABI 不匹配");
}
8. 合约地址错误:
// 检查合约地址有效性
async function validateContract(address) {
if (!ethers.utils.isAddress(address)) {
throw new Error("无效的合约地址格式");
}
const code = await provider.getCode(address);
if (code === '0x') {
throw new Error("地址不是合约或合约已被销毁");
}
return true;
}
错误处理最佳实践:
async function safeContractCall(contract, functionName, params = []) {
try {
// 1. 预检查
await validateContract(contract.address);
// 2. 估算 Gas
const gasEstimate = await contract.estimateGas[functionName](...params);
// 3. 执行调用
const tx = await contract[functionName](...params, {
gasLimit: gasEstimate.mul(110).div(100) // +10% 缓冲
});
// 4. 等待确认
const receipt = await tx.wait();
return receipt;
} catch (error) {
// 错误分类处理
if (error.code === 'UNPREDICTABLE_GAS_LIMIT') {
console.error("交易可能会失败,检查合约条件");
} else if (error.code === 'INSUFFICIENT_FUNDS') {
console.error("账户余额不足");
} else if (error.reason) {
console.error("合约拒绝:", error.reason);
} else {
console.error("未知错误:", error.message);
}
throw error;
}
}
调试工具和方法:
// 使用 callStatic 进行无状态调用测试
try {
const result = await contract.callStatic.someFunction(params);
console.log("模拟调用成功:", result);
} catch (error) {
console.log("模拟调用失败:", error.reason);
// 不会消耗 Gas,可以安全测试
}
// 检查交易失败原因
async function analyzeFailedTx(txHash) {
try {
const tx = await provider.getTransaction(txHash);
const receipt = await provider.getTransactionReceipt(txHash);
if (receipt.status === 0) {
console.log("交易失败");
// 尝试重新执行以获取错误信息
try {
await provider.call(tx, tx.blockNumber);
} catch (error) {
console.log("失败原因:", error.reason || error.message);
}
}
} catch (error) {
console.log("分析失败:", error);
}
}
预防措施:
callStatic 进行预调用验证What is a contract constructor? How to pass parameters during deployment?
What is a contract constructor? How to pass parameters during deployment?
考察点:合约部署基础。
答案:
构造函数是智能合约中的一个特殊函数,在合约部署时自动执行一次且仅执行一次。它用于初始化合约的状态变量、设置初始配置、分配权限等。构造函数使用 constructor 关键字定义,可以接收参数来灵活配置合约的初始状态。
构造函数在合约部署完成后就不能再被调用,这使得它成为设置不可变配置和一次性初始化操作的理想位置。
Solidity 构造函数示例:
contract MyToken {
string public name;
string public symbol;
uint256 public totalSupply;
address public owner;
mapping(address => uint256) public balanceOf;
// 构造函数 - 部署时执行
constructor(
string memory _name,
string memory _symbol,
uint256 _totalSupply
) {
name = _name;
symbol = _symbol;
totalSupply = _totalSupply;
owner = msg.sender; // 部署者成为所有者
balanceOf[msg.sender] = _totalSupply; // 将所有代币分配给部署者
emit TokenCreated(_name, _symbol, _totalSupply, msg.sender);
}
event TokenCreated(
string name,
string symbol,
uint256 totalSupply,
address owner
);
}
使用 Ethers.js 部署带参数的合约:
// 1. 准备合约工厂
const contractFactory = new ethers.ContractFactory(abi, bytecode, signer);
// 2. 部署合约并传入构造函数参数
const contract = await contractFactory.deploy(
"MyToken", // name 参数
"MTK", // symbol 参数
ethers.utils.parseEther("1000000") // totalSupply 参数
);
// 3. 等待部署完成
await contract.deployed();
console.log("合约地址:", contract.address);
console.log("部署交易:", contract.deployTransaction.hash);
使用 Web3.js 部署:
// 创建合约实例
const contract = new web3.eth.Contract(abi);
// 部署合约
const deployedContract = await contract.deploy({
data: bytecode,
arguments: [
"MyToken", // name
"MTK", // symbol
web3.utils.toWei('1000000', 'ether') // totalSupply
]
}).send({
from: deployerAddress,
gas: 2000000,
gasPrice: web3.utils.toWei('20', 'gwei')
});
console.log("合约地址:", deployedContract.options.address);
复杂构造函数参数示例:
contract DeFiProtocol {
struct Config {
uint256 fee;
uint256 minAmount;
bool paused;
}
Config public config;
address[] public supportedTokens;
mapping(address => bool) public admins;
constructor(
Config memory _config,
address[] memory _supportedTokens,
address[] memory _admins
) {
// 设置配置
config = _config;
// 添加支持的代币
for (uint i = 0; i < _supportedTokens.length; i++) {
supportedTokens.push(_supportedTokens[i]);
}
// 设置管理员
for (uint i = 0; i < _admins.length; i++) {
admins[_admins[i]] = true;
}
// 部署者也是管理员
admins[msg.sender] = true;
}
}
// 部署复杂参数合约
const config = {
fee: 100, // 1% fee (basis points)
minAmount: ethers.utils.parseEther("0.1"),
paused: false
};
const supportedTokens = [
"0xA0b86a33E6441e2b000E85C8b60E54E48e48B2Bb", // USDT
"0xdAC17F958D2ee523a2206206994597C13D831ec7" // USDC
];
const admins = [
"0x1234567890123456789012345678901234567890",
"0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef"
];
const contract = await contractFactory.deploy(
config, // struct 参数
supportedTokens, // array 参数
admins // array 参数
);
参数编码和验证:
// 验证构造函数参数
function validateConstructorParams(name, symbol, totalSupply) {
if (!name || name.length === 0) {
throw new Error("代币名称不能为空");
}
if (!symbol || symbol.length === 0) {
throw new Error("代币符号不能为空");
}
if (totalSupply.lte(0)) {
throw new Error("总供应量必须大于0");
}
return true;
}
// 安全部署函数
async function deployTokenContract(params) {
const { name, symbol, totalSupply } = params;
// 参数验证
validateConstructorParams(name, symbol, totalSupply);
try {
// 估算部署 Gas
const deployTx = contractFactory.getDeployTransaction(
name, symbol, totalSupply
);
const gasEstimate = await signer.estimateGas(deployTx);
// 部署合约
const contract = await contractFactory.deploy(
name,
symbol,
totalSupply,
{
gasLimit: gasEstimate.mul(110).div(100) // +10% 缓冲
}
);
// 等待确认
await contract.deployed();
console.log(`${name} 合约部署成功:`);
console.log(`地址: ${contract.address}`);
console.log(`交易: ${contract.deployTransaction.hash}`);
return contract;
} catch (error) {
console.error("部署失败:", error);
throw error;
}
}
获取构造函数参数(合约验证):
// 从部署交易中提取构造函数参数
async function getConstructorParams(contractAddress) {
// 获取合约创建交易
const contract = new ethers.Contract(contractAddress, abi, provider);
const deployTx = await provider.getTransaction(contract.deployTransaction.hash);
// 解码构造函数参数
const iface = new ethers.utils.Interface(abi);
const constructorFragment = iface.deploy;
// 从 input data 中解析参数
const params = iface.decodeFunctionData(constructorFragment, deployTx.data);
return params;
}
构造函数最佳实践:
注意事项:
msg.sender 是部署者地址How to implement batch contract calls to improve efficiency?
How to implement batch contract calls to improve efficiency?
考察点:批量操作优化。
答案:
批量合约调用是提高 DApp 性能的重要技术,通过减少网络往返次数、降低 Gas 消耗和提升用户体验来优化应用效率。主要实现方式包括使用 Multicall 合约、Promise.all() 并发调用和自定义批量处理逻辑。
主要实现方式:
// Multicall2 合约接口
const multicallABI = [
"function aggregate(tuple(address target, bytes callData)[] calls) returns (uint256 blockNumber, bytes[] returnData)",
"function tryAggregate(bool requireSuccess, tuple(address target, bytes callData)[] calls) returns (tuple(bool success, bytes returnData)[] returnData)"
];
const multicallAddress = "0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696"; // Ethereum mainnet
async function batchContractCalls(calls) {
const multicall = new ethers.Contract(multicallAddress, multicallABI, provider);
// 准备批量调用数据
const callData = calls.map(call => ({
target: call.contract.address,
callData: call.contract.interface.encodeFunctionData(call.method, call.params)
}));
try {
const [blockNumber, returnData] = await multicall.aggregate(callData);
// 解码返回数据
const results = returnData.map((data, index) => {
const call = calls[index];
return call.contract.interface.decodeFunctionResult(call.method, data);
});
return results;
} catch (error) {
console.error("批量调用失败:", error);
throw error;
}
}
// 使用示例
const batchCalls = [
{ contract: tokenContract, method: "balanceOf", params: [userAddress] },
{ contract: tokenContract, method: "totalSupply", params: [] },
{ contract: tokenContract, method: "allowance", params: [userAddress, spenderAddress] }
];
const results = await batchContractCalls(batchCalls);
// 并发执行多个只读调用
async function parallelReadCalls(userAddress) {
const promises = [
tokenContract.balanceOf(userAddress),
tokenContract.totalSupply(),
tokenContract.name(),
tokenContract.symbol(),
tokenContract.decimals()
];
try {
const [balance, totalSupply, name, symbol, decimals] = await Promise.all(promises);
return {
balance: ethers.utils.formatUnits(balance, decimals),
totalSupply: ethers.utils.formatUnits(totalSupply, decimals),
name,
symbol,
decimals
};
} catch (error) {
console.error("并发调用失败:", error);
throw error;
}
}
// 自定义批量处理合约
contract BatchProcessor {
struct Call {
address target;
bytes data;
}
struct Result {
bool success;
bytes data;
}
function batchCall(Call[] calldata calls)
external
returns (Result[] memory results)
{
results = new Result[](calls.length);
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory data) = calls[i].target.call(calls[i].data);
results[i] = Result(success, data);
}
}
function batchStaticCall(Call[] calldata calls)
external
view
returns (Result[] memory results)
{
results = new Result[](calls.length);
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory data) = calls[i].target.staticcall(calls[i].data);
results[i] = Result(success, data);
}
}
}
// 批量发送交易(按序执行)
async function batchTransactions(transactions) {
const results = [];
let nonce = await provider.getTransactionCount(signer.getAddress());
for (let i = 0; i < transactions.length; i++) {
try {
const tx = await signer.sendTransaction({
...transactions[i],
nonce: nonce + i
});
results.push(tx);
console.log(`交易 ${i + 1} 已发送: ${tx.hash}`);
} catch (error) {
console.error(`交易 ${i + 1} 失败:`, error);
results.push({ error });
}
}
// 等待所有交易确认
const receipts = await Promise.all(
results.filter(result => result.hash).map(tx => tx.wait())
);
return receipts;
}
高级批量调用实现:
class BatchCallManager {
constructor(provider, multicallAddress) {
this.provider = provider;
this.multicall = new ethers.Contract(multicallAddress, multicallABI, provider);
this.calls = [];
}
// 添加调用
addCall(contract, method, params = []) {
this.calls.push({
contract,
method,
params,
callData: contract.interface.encodeFunctionData(method, params)
});
return this;
}
// 执行批量调用
async execute() {
if (this.calls.length === 0) {
return [];
}
const callData = this.calls.map(call => ({
target: call.contract.address,
callData: call.callData
}));
try {
const [blockNumber, returnData] = await this.multicall.aggregate(callData);
const results = returnData.map((data, index) => {
const call = this.calls[index];
try {
const decoded = call.contract.interface.decodeFunctionResult(call.method, data);
return { success: true, data: decoded };
} catch (error) {
return { success: false, error: error.message };
}
});
this.calls = []; // 清空调用队列
return results;
} catch (error) {
console.error("批量调用执行失败:", error);
throw error;
}
}
// 清空调用队列
clear() {
this.calls = [];
return this;
}
}
// 使用示例
const batchManager = new BatchCallManager(provider, multicallAddress);
const results = await batchManager
.addCall(tokenContract, "balanceOf", [userAddress])
.addCall(tokenContract, "totalSupply")
.addCall(uniswapContract, "getAmountsOut", [amount, [tokenA, tokenB]])
.execute();
性能优化技巧:
// 批量数据获取优化
async function optimizedDataFetch(userAddresses) {
const batchSize = 50; // 控制批次大小
const results = [];
for (let i = 0; i < userAddresses.length; i += batchSize) {
const batch = userAddresses.slice(i, i + batchSize);
const batchCalls = batch.map(address => ({
contract: tokenContract,
method: "balanceOf",
params: [address]
}));
const batchResults = await batchContractCalls(batchCalls);
results.push(...batchResults);
// 添加延迟避免速率限制
if (i + batchSize < userAddresses.length) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return results;
}
适用场景:
最佳实践:
What is the multicall pattern? How does it reduce the number of network requests?
What is the multicall pattern? How does it reduce the number of network requests?
考察点:高效调用模式。
答案:
Multicall 模式是一种将多个合约调用聚合到单个交易中执行的设计模式。它通过一个中介合约来批量执行多个合约调用,将原本需要多次网络请求的操作合并为一次请求,显著提高了效率并降低了网络延迟。
Multicall 特别适用于只读查询操作,可以在一个区块的状态下获取多个合约的数据,确保数据的一致性。
Multicall 工作原理:
Multicall 合约实现:
pragma solidity ^0.8.0;
contract Multicall {
struct Call {
address target;
bytes callData;
}
struct Result {
bool success;
bytes returnData;
}
// 标准聚合调用(任一失败则全部失败)
function aggregate(Call[] calldata calls)
public
returns (uint256 blockNumber, bytes[] memory returnData)
{
blockNumber = block.number;
returnData = new bytes[](calls.length);
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
require(success, "Multicall aggregate: call failed");
returnData[i] = ret;
}
}
// 尝试聚合调用(允许部分失败)
function tryAggregate(bool requireSuccess, Call[] calldata calls)
public
returns (Result[] memory returnData)
{
returnData = new Result[](calls.length);
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
if (requireSuccess) {
require(success, "Multicall tryAggregate: call failed");
}
returnData[i] = Result(success, ret);
}
}
// 静态聚合调用(只读)
function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls)
external
returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData)
{
blockNumber = block.number;
blockHash = blockhash(block.number);
returnData = tryAggregate(requireSuccess, calls);
}
}
JavaScript 实现示例:
class MulticallManager {
constructor(provider, multicallAddress) {
this.provider = provider;
this.multicallAddress = multicallAddress;
this.multicall = new ethers.Contract(
multicallAddress,
multicallABI,
provider
);
this.calls = [];
}
// 添加合约调用
call(contract, method, params = []) {
const callData = contract.interface.encodeFunctionData(method, params);
this.calls.push({
target: contract.address,
callData: callData,
contract: contract,
method: method,
decode: (data) => contract.interface.decodeFunctionResult(method, data)
});
return this;
}
// 执行聚合调用
async execute() {
if (this.calls.length === 0) {
return [];
}
const callRequests = this.calls.map(call => ({
target: call.target,
callData: call.callData
}));
try {
// 使用 tryAggregate 允许部分失败
const results = await this.multicall.tryAggregate(false, callRequests);
return results.map((result, index) => {
const call = this.calls[index];
if (result.success) {
try {
return {
success: true,
data: call.decode(result.returnData)
};
} catch (error) {
return {
success: false,
error: `解码失败: ${error.message}`
};
}
} else {
return {
success: false,
error: "调用失败"
};
}
});
} finally {
this.calls = []; // 清空调用列表
}
}
}
实际应用示例:
// DeFi 协议数据聚合
async function getDeFiPortfolio(userAddress) {
const multicaller = new MulticallManager(provider, MULTICALL_ADDRESS);
// 添加多个协议的查询
multicaller
.call(usdcContract, "balanceOf", [userAddress])
.call(daiContract, "balanceOf", [userAddress])
.call(uniswapV3Position, "balanceOf", [userAddress])
.call(aaveContract, "getUserAccountData", [userAddress])
.call(compoundContract, "getAccountLiquidity", [userAddress]);
const results = await multicaller.execute();
return {
usdcBalance: results[0].success ? ethers.utils.formatUnits(results[0].data[0], 6) : "0",
daiBalance: results[1].success ? ethers.utils.formatUnits(results[1].data[0], 18) : "0",
nftCount: results[2].success ? results[2].data[0].toString() : "0",
aaveData: results[3].success ? results[3].data : null,
compoundLiquidity: results[4].success ? results[4].data : null
};
}
效率对比分析:
// 传统方式(多次网络请求)
async function traditionalApproach(userAddress) {
const start = Date.now();
const balance1 = await token1.balanceOf(userAddress);
const balance2 = await token2.balanceOf(userAddress);
const balance3 = await token3.balanceOf(userAddress);
const allowance1 = await token1.allowance(userAddress, spenderAddress);
const allowance2 = await token2.allowance(userAddress, spenderAddress);
const end = Date.now();
console.log(`传统方式耗时: ${end - start}ms`);
return { balance1, balance2, balance3, allowance1, allowance2 };
}
// Multicall 方式(单次网络请求)
async function multicallApproach(userAddress) {
const start = Date.now();
const multicaller = new MulticallManager(provider, MULTICALL_ADDRESS);
const results = await multicaller
.call(token1, "balanceOf", [userAddress])
.call(token2, "balanceOf", [userAddress])
.call(token3, "balanceOf", [userAddress])
.call(token1, "allowance", [userAddress, spenderAddress])
.call(token2, "allowance", [userAddress, spenderAddress])
.execute();
const end = Date.now();
console.log(`Multicall 方式耗时: ${end - start}ms`);
return results;
}
Multicall 的优势:
网络效率:
数据一致性:
成本优化:
使用场景:
注意事项:
How to handle reentrancy attack risks in contract calls?
How to handle reentrancy attack risks in contract calls?
考察点:安全防护意识。
答案:
重入攻击是智能合约安全中的一个重要威胁,发生在合约调用外部合约时,外部合约反过来调用原合约的函数,可能导致状态不一致和资产损失。处理重入攻击需要在合约设计和前端交互中采用多层防护措施。
重入攻击的工作原理:
// 存在重入风险的合约
contract Vulnerable {
mapping(address => uint256) public balances;
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "余额不足");
// 危险:在更新状态前进行外部调用
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "转账失败");
// 状态更新在外部调用之后(太晚了)
balances[msg.sender] -= amount;
}
}
// 攻击者合约
contract Attacker {
Vulnerable vulnerable;
uint256 public attackCount;
constructor(address _vulnerable) {
vulnerable = Vulnerable(_vulnerable);
}
function attack() external payable {
vulnerable.withdraw(msg.value);
}
// 重入攻击的核心:在 receive 函数中再次调用 withdraw
receive() external payable {
if (attackCount < 3) { // 限制攻击次数避免 gas 耗尽
attackCount++;
vulnerable.withdraw(msg.value);
}
}
}
合约端防护措施:
contract Secure {
mapping(address => uint256) public balances;
function withdraw(uint256 amount) public {
// 1. 检查 (Checks)
require(balances[msg.sender] >= amount, "余额不足");
// 2. 效果 (Effects) - 先更新状态
balances[msg.sender] -= amount;
// 3. 交互 (Interactions) - 最后进行外部调用
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "转账失败");
}
}
contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "重入攻击检测");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
contract SecureWithGuard is ReentrancyGuard {
mapping(address => uint256) public balances;
function withdraw(uint256 amount) public nonReentrant {
require(balances[msg.sender] >= amount, "余额不足");
balances[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "转账失败");
}
}
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract MySecureContract is ReentrancyGuard {
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) public nonReentrant {
require(balances[msg.sender] >= amount, "余额不足");
balances[msg.sender] -= amount;
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "提取失败");
}
}
前端防护措施:
class ReentrancyProtection {
constructor() {
this.pendingTransactions = new Set();
}
async safeWithdraw(contract, amount) {
const txKey = `withdraw_${amount}_${Date.now()}`;
// 检查是否有相同操作在进行
if (this.pendingTransactions.has(txKey)) {
throw new Error("已有相同操作在进行中");
}
try {
this.pendingTransactions.add(txKey);
// 执行提取操作
const tx = await contract.withdraw(amount);
await tx.wait();
return tx;
} finally {
this.pendingTransactions.delete(txKey);
}
}
}
class WithdrawManager {
constructor(contract) {
this.contract = contract;
this.isWithdrawing = false;
}
async withdraw(amount) {
if (this.isWithdrawing) {
throw new Error("提取操作进行中,请等待完成");
}
try {
this.isWithdrawing = true;
// 禁用提取按钮
this.updateUI({ withdrawing: true });
// 执行提取
const tx = await this.contract.withdraw(amount, {
gasLimit: 100000 // 限制 Gas 使用
});
console.log("提取交易发送:", tx.hash);
// 等待确认
const receipt = await tx.wait();
console.log("提取完成:", receipt);
return receipt;
} finally {
this.isWithdrawing = false;
this.updateUI({ withdrawing: false });
}
}
updateUI(state) {
// 更新用户界面状态
const withdrawButton = document.getElementById('withdrawButton');
withdrawButton.disabled = state.withdrawing;
withdrawButton.textContent = state.withdrawing ? '提取中...' : '提取';
}
}
async function secureContractCall(contract, method, params, options = {}) {
try {
// 估算 Gas 使用量
const gasEstimate = await contract.estimateGas[method](...params);
// 设置合理的 Gas 限制(防止无限循环)
const gasLimit = gasEstimate.mul(110).div(100); // +10% 缓冲
const maxGasLimit = 500000; // 设置绝对上限
const finalGasLimit = gasLimit.gt(maxGasLimit) ? maxGasLimit : gasLimit;
// 执行调用
const tx = await contract[method](...params, {
gasLimit: finalGasLimit,
...options
});
return tx;
} catch (error) {
if (error.code === 'UNPREDICTABLE_GAS_LIMIT') {
throw new Error("交易可能失败,请检查合约状态");
}
throw error;
}
}
检测和监控:
// 监控重入攻击迹象
class ReentrancyMonitor {
constructor(contract, provider) {
this.contract = contract;
this.provider = provider;
this.suspiciousPatterns = new Map();
}
async monitorTransactions() {
this.contract.on("*", (event) => {
this.analyzeEvent(event);
});
}
analyzeEvent(event) {
const { transactionHash, blockNumber } = event;
// 检测短时间内的多次调用
const key = `${event.args?.user || event.address}_${blockNumber}`;
if (this.suspiciousPatterns.has(key)) {
const count = this.suspiciousPatterns.get(key) + 1;
this.suspiciousPatterns.set(key, count);
if (count > 3) {
console.warn(`检测到可疑的重入模式: ${transactionHash}`);
this.alertSecurityTeam(event);
}
} else {
this.suspiciousPatterns.set(key, 1);
}
// 清理旧数据
setTimeout(() => {
this.suspiciousPatterns.delete(key);
}, 60000); // 1分钟后清理
}
alertSecurityTeam(event) {
// 发送安全警报
console.error("安全警报:检测到潜在的重入攻击", event);
}
}
最佳实践:
合约设计:
前端开发:
安全审计:
注意事项:
transfer() 和 send() 虽然有 Gas 限制,但不是完美的解决方案What is the proxy contract pattern? How to interact with upgradeable contracts?
What is the proxy contract pattern? How to interact with upgradeable contracts?
考察点:合约升级机制。
答案:
代理合约模式是一种允许智能合约升级的设计模式,通过将合约逻辑与存储分离来实现。代理合约负责存储数据和接收调用,而将实际的业务逻辑委托给可替换的实现合约。这种模式使得在不改变合约地址的情况下升级业务逻辑成为可能。
代理合约工作原理:
// 简单代理合约示例
contract SimpleProxy {
address public implementation;
address public admin;
modifier onlyAdmin() {
require(msg.sender == admin, "只有管理员可以操作");
_;
}
constructor(address _implementation) {
implementation = _implementation;
admin = msg.sender;
}
// 升级实现合约
function upgrade(address newImplementation) external onlyAdmin {
implementation = newImplementation;
}
// 委托调用
fallback() external payable {
address impl = implementation;
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}
OpenZeppelin 透明代理模式:
// 使用 OpenZeppelin 的透明代理
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
// 实现合约 V1
contract ImplementationV1 {
uint256 public value;
function initialize(uint256 _value) public {
value = _value;
}
function setValue(uint256 _value) public {
value = _value;
}
function getValue() public view returns (uint256) {
return value;
}
}
// 实现合约 V2 (升级版本)
contract ImplementationV2 {
uint256 public value;
uint256 public newFeature; // 新增功能
function initialize(uint256 _value) public {
value = _value;
}
function setValue(uint256 _value) public {
value = _value;
}
function getValue() public view returns (uint256) {
return value;
}
// 新增功能
function setNewFeature(uint256 _newFeature) public {
newFeature = _newFeature;
}
}
部署和升级流程:
// 1. 部署实现合约
const ImplementationV1 = await ethers.getContractFactory("ImplementationV1");
const implementationV1 = await ImplementationV1.deploy();
// 2. 部署代理管理员
const ProxyAdmin = await ethers.getContractFactory("ProxyAdmin");
const proxyAdmin = await ProxyAdmin.deploy();
// 3. 准备初始化数据
const initData = implementationV1.interface.encodeFunctionData("initialize", [100]);
// 4. 部署透明代理
const TransparentUpgradeableProxy = await ethers.getContractFactory("TransparentUpgradeableProxy");
const proxy = await TransparentUpgradeableProxy.deploy(
implementationV1.address,
proxyAdmin.address,
initData
);
console.log("代理合约地址:", proxy.address);
console.log("实现合约地址:", implementationV1.address);
前端与代理合约交互:
class UpgradeableContractManager {
constructor(proxyAddress, currentABI, provider, signer) {
this.proxyAddress = proxyAddress;
this.provider = provider;
this.signer = signer;
this.contract = new ethers.Contract(proxyAddress, currentABI, signer);
}
// 获取当前实现合约地址
async getImplementation() {
// 透明代理的实现地址存储在特定槽位
const IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
const implementationHex = await this.provider.getStorageAt(
this.proxyAddress,
IMPLEMENTATION_SLOT
);
return ethers.utils.getAddress(implementationHex.slice(-40));
}
// 检查合约版本
async getVersion() {
try {
// 如果实现合约有版本函数
return await this.contract.version();
} catch (error) {
console.log("合约没有版本函数");
return null;
}
}
// 安全调用合约函数
async safeCall(methodName, params = []) {
try {
// 检查函数是否存在
if (!this.contract.interface.getFunction(methodName)) {
throw new Error(`函数 ${methodName} 不存在`);
}
const result = await this.contract[methodName](...params);
return result;
} catch (error) {
console.error(`调用 ${methodName} 失败:`, error);
// 检查是否因为升级导致 ABI 不匹配
await this.checkForUpgrade();
throw error;
}
}
// 检查合约是否已升级
async checkForUpgrade() {
const currentImpl = await this.getImplementation();
if (this.lastKnownImplementation && this.lastKnownImplementation !== currentImpl) {
console.log("检测到合约升级:");
console.log("旧实现:", this.lastKnownImplementation);
console.log("新实现:", currentImpl);
// 触发升级处理逻辑
await this.handleUpgrade(currentImpl);
}
this.lastKnownImplementation = currentImpl;
}
// 处理合约升级
async handleUpgrade(newImplementation) {
// 通知用户合约已升级
console.log("合约已升级,可能需要刷新 ABI");
// 这里可以实现自动获取新 ABI 的逻辑
// 或者提示用户手动更新
}
// 更新 ABI(升级后)
updateABI(newABI) {
this.contract = new ethers.Contract(this.proxyAddress, newABI, this.signer);
}
}
升级流程示例:
async function upgradeContract() {
try {
// 1. 部署新的实现合约
const ImplementationV2 = await ethers.getContractFactory("ImplementationV2");
const implementationV2 = await ImplementationV2.deploy();
await implementationV2.deployed();
console.log("新实现合约地址:", implementationV2.address);
// 2. 通过 ProxyAdmin 执行升级
const proxyAdmin = new ethers.Contract(proxyAdminAddress, proxyAdminABI, signer);
const upgradeTx = await proxyAdmin.upgrade(
proxyAddress, // 代理合约地址
implementationV2.address // 新实现合约地址
);
await upgradeTx.wait();
console.log("升级完成,交易哈希:", upgradeTx.hash);
// 3. 验证升级
const contractManager = new UpgradeableContractManager(
proxyAddress,
implementationV2ABI,
provider,
signer
);
const newImplementation = await contractManager.getImplementation();
console.log("当前实现地址:", newImplementation);
// 4. 测试新功能
await contractManager.safeCall("setNewFeature", [200]);
const newFeatureValue = await contractManager.safeCall("newFeature");
console.log("新功能值:", newFeatureValue.toString());
} catch (error) {
console.error("升级失败:", error);
}
}
升级兼容性检查:
class UpgradeCompatibilityChecker {
constructor(oldABI, newABI) {
this.oldABI = oldABI;
this.newABI = newABI;
}
checkCompatibility() {
const issues = [];
// 检查现有函数是否保持兼容
const oldFunctions = this.getFunctions(this.oldABI);
const newFunctions = this.getFunctions(this.newABI);
oldFunctions.forEach(oldFunc => {
const newFunc = newFunctions.find(f => f.name === oldFunc.name);
if (!newFunc) {
issues.push(`函数 ${oldFunc.name} 在新版本中被移除`);
} else if (!this.isSignatureCompatible(oldFunc, newFunc)) {
issues.push(`函数 ${oldFunc.name} 的签名发生了不兼容的变化`);
}
});
return {
compatible: issues.length === 0,
issues: issues
};
}
getFunctions(abi) {
return abi.filter(item => item.type === 'function');
}
isSignatureCompatible(oldFunc, newFunc) {
// 检查参数数量和类型
if (oldFunc.inputs.length !== newFunc.inputs.length) {
return false;
}
for (let i = 0; i < oldFunc.inputs.length; i++) {
if (oldFunc.inputs[i].type !== newFunc.inputs[i].type) {
return false;
}
}
return true;
}
}
监听升级事件:
// 监听合约升级事件
async function monitorUpgrades(proxyAddress) {
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
// 监听 Upgraded 事件
const upgradeEventSignature = "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b";
const filter = {
address: proxyAddress,
topics: [upgradeEventSignature]
};
provider.on(filter, (log) => {
console.log("检测到合约升级:");
// 解析事件数据
const newImplementation = ethers.utils.getAddress("0x" + log.topics[1].slice(-40));
console.log("新实现地址:", newImplementation);
// 通知应用处理升级
handleContractUpgrade(newImplementation);
});
}
function handleContractUpgrade(newImplementation) {
// 更新应用中的合约实例
// 可能需要重新加载 ABI 或刷新界面
console.log("处理合约升级...");
}
最佳实践:
升级前准备:
前端适配:
安全考虑:
注意事项:
How to gracefully handle transaction failures and rollbacks?
How to gracefully handle transaction failures and rollbacks?
考察点:错误处理最佳实践。
答案:
优雅地处理交易失败和回滚是构建可靠 DApp 的关键技能。需要在合约设计、前端交互和用户体验等多个层面实施完善的错误处理机制,确保用户资金安全和操作的可预测性。
交易失败的分类:
合约层面的错误处理:
contract SafeTransactionHandler {
event TransactionAttempted(address indexed user, string operation, bool success);
event TransactionReverted(address indexed user, string reason);
// 自定义错误(Gas 效率更高)
error InsufficientBalance(uint256 required, uint256 available);
error InvalidOperation(string reason);
error OperationTimeout();
mapping(address => uint256) public balances;
mapping(bytes32 => bool) public processedTransactions;
modifier validAmount(uint256 amount) {
if (amount == 0) revert InvalidOperation("Amount cannot be zero");
if (balances[msg.sender] < amount) {
revert InsufficientBalance(amount, balances[msg.sender]);
}
_;
}
modifier nonceProtection(bytes32 txHash) {
require(!processedTransactions[txHash], "Transaction already processed");
processedTransactions[txHash] = true;
_;
}
// 安全的提取函数
function safeWithdraw(uint256 amount, bytes32 nonce)
external
validAmount(amount)
nonceProtection(nonce)
{
uint256 oldBalance = balances[msg.sender];
try this.performWithdraw(msg.sender, amount) {
emit TransactionAttempted(msg.sender, "withdraw", true);
} catch Error(string memory reason) {
// 处理 require 失败
balances[msg.sender] = oldBalance; // 恢复状态
emit TransactionReverted(msg.sender, reason);
revert(reason);
} catch (bytes memory lowLevelData) {
// 处理低级别错误
balances[msg.sender] = oldBalance;
emit TransactionReverted(msg.sender, "Low level error");
revert("Transaction failed");
}
}
function performWithdraw(address user, uint256 amount) external {
require(msg.sender == address(this), "Internal only");
balances[user] -= amount;
(bool success, ) = user.call{value: amount}("");
if (!success) {
revert("Transfer failed");
}
}
}
前端错误处理框架:
class TransactionManager {
constructor(provider, signer) {
this.provider = provider;
this.signer = signer;
this.pendingTransactions = new Map();
this.errorHandlers = new Map();
}
// 注册错误处理器
registerErrorHandler(errorType, handler) {
this.errorHandlers.set(errorType, handler);
}
// 安全执行交易
async safeExecute(contract, method, params = [], options = {}) {
const txId = this.generateTxId();
try {
// 1. 预检查
await this.preflightCheck(contract, method, params, options);
// 2. 估算 Gas
const gasEstimate = await this.estimateGas(contract, method, params);
const finalOptions = {
...options,
gasLimit: gasEstimate.mul(110).div(100) // +10% 缓冲
};
// 3. 执行交易
this.pendingTransactions.set(txId, { status: 'pending' });
const tx = await contract[method](...params, finalOptions);
this.pendingTransactions.set(txId, {
status: 'submitted',
hash: tx.hash,
transaction: tx
});
// 4. 等待确认
const receipt = await this.waitForConfirmation(tx, txId);
this.pendingTransactions.set(txId, {
status: 'confirmed',
receipt: receipt
});
return receipt;
} catch (error) {
await this.handleError(error, txId, { contract, method, params, options });
throw error;
} finally {
// 清理
setTimeout(() => this.pendingTransactions.delete(txId), 300000); // 5分钟后清理
}
}
// 预检查
async preflightCheck(contract, method, params, options) {
// 检查网络连接
try {
await this.provider.getBlockNumber();
} catch (error) {
throw new Error("网络连接失败");
}
// 检查账户余额
const balance = await this.signer.getBalance();
const requiredBalance = options.value || 0;
if (balance.lt(requiredBalance)) {
throw new Error("账户余额不足");
}
// 模拟执行
try {
await contract.callStatic[method](...params, options);
} catch (error) {
throw new Error(`模拟执行失败: ${error.reason || error.message}`);
}
}
// Gas 估算
async estimateGas(contract, method, params, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await contract.estimateGas[method](...params);
} catch (error) {
if (i === retries - 1) {
throw new Error(`Gas 估算失败: ${error.reason || error.message}`);
}
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
// 等待确认
async waitForConfirmation(tx, txId, confirmations = 1) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error("交易确认超时")), 300000); // 5分钟超时
});
const confirmationPromise = tx.wait(confirmations);
try {
const receipt = await Promise.race([confirmationPromise, timeoutPromise]);
if (receipt.status === 0) {
throw new Error("交易执行失败");
}
return receipt;
} catch (error) {
this.pendingTransactions.set(txId, { status: 'failed', error });
throw error;
}
}
// 错误处理
async handleError(error, txId, context) {
const errorType = this.classifyError(error);
this.pendingTransactions.set(txId, {
status: 'failed',
error: error,
errorType: errorType,
context: context
});
// 调用注册的错误处理器
const handler = this.errorHandlers.get(errorType);
if (handler) {
await handler(error, context);
}
// 记录错误
console.error(`交易失败 [${errorType}]:`, error);
}
// 错误分类
classifyError(error) {
if (error.code === 'INSUFFICIENT_FUNDS') return 'INSUFFICIENT_FUNDS';
if (error.code === 'UNPREDICTABLE_GAS_LIMIT') return 'GAS_ESTIMATION_FAILED';
if (error.code === 'NETWORK_ERROR') return 'NETWORK_ERROR';
if (error.code === 4001) return 'USER_REJECTED';
if (error.reason) return 'CONTRACT_ERROR';
return 'UNKNOWN_ERROR';
}
generateTxId() {
return `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
重试机制实现:
class RetryableTransaction {
constructor(txManager, maxRetries = 3) {
this.txManager = txManager;
this.maxRetries = maxRetries;
}
async executeWithRetry(contract, method, params, options = {}) {
let lastError;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
console.log(`交易尝试 ${attempt}/${this.maxRetries}`);
const result = await this.txManager.safeExecute(
contract,
method,
params,
this.adjustOptionsForRetry(options, attempt)
);
return result;
} catch (error) {
lastError = error;
if (!this.shouldRetry(error, attempt)) {
throw error;
}
// 等待后重试
const delay = this.calculateDelay(attempt);
console.log(`${delay}ms 后重试...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
shouldRetry(error, attempt) {
if (attempt >= this.maxRetries) return false;
// 不重试的错误类型
const nonRetryableErrors = [
'USER_REJECTED',
'INSUFFICIENT_FUNDS',
'CONTRACT_ERROR'
];
const errorType = this.classifyError(error);
return !nonRetryableErrors.includes(errorType);
}
adjustOptionsForRetry(options, attempt) {
// 逐步增加 Gas 价格
if (options.gasPrice) {
const multiplier = 1 + (attempt - 1) * 0.2; // 每次增加20%
return {
...options,
gasPrice: options.gasPrice.mul(Math.floor(multiplier * 100)).div(100)
};
}
return options;
}
calculateDelay(attempt) {
return Math.min(1000 * Math.pow(2, attempt - 1), 30000); // 指数退避,最大30秒
}
}
用户界面状态管理:
class TransactionStateManager {
constructor() {
this.transactions = new Map();
this.listeners = new Set();
}
// 添加交易
addTransaction(txId, txData) {
this.transactions.set(txId, {
...txData,
timestamp: Date.now(),
status: 'pending'
});
this.notifyListeners('added', txId);
}
// 更新交易状态
updateTransaction(txId, updates) {
const existing = this.transactions.get(txId);
if (existing) {
this.transactions.set(txId, { ...existing, ...updates });
this.notifyListeners('updated', txId);
}
}
// 获取交易状态
getTransaction(txId) {
return this.transactions.get(txId);
}
// 获取所有待处理交易
getPendingTransactions() {
return Array.from(this.transactions.entries())
.filter(([_, tx]) => tx.status === 'pending')
.map(([id, tx]) => ({ id, ...tx }));
}
// 添加监听器
addListener(callback) {
this.listeners.add(callback);
return () => this.listeners.delete(callback);
}
notifyListeners(event, txId) {
this.listeners.forEach(listener => {
try {
listener(event, txId, this.getTransaction(txId));
} catch (error) {
console.error("监听器错误:", error);
}
});
}
}
// React Hook 示例
function useTransactionState() {
const [transactions, setTransactions] = useState(new Map());
const stateManager = useRef(new TransactionStateManager());
useEffect(() => {
const unsubscribe = stateManager.current.addListener((event, txId, txData) => {
setTransactions(prev => new Map(prev.set(txId, txData)));
});
return unsubscribe;
}, []);
const submitTransaction = useCallback(async (contract, method, params, options) => {
const txId = generateTxId();
try {
stateManager.current.addTransaction(txId, {
contract: contract.address,
method,
params,
status: 'pending'
});
const result = await txManager.safeExecute(contract, method, params, options);
stateManager.current.updateTransaction(txId, {
status: 'confirmed',
result: result
});
return result;
} catch (error) {
stateManager.current.updateTransaction(txId, {
status: 'failed',
error: error.message
});
throw error;
}
}, []);
return {
transactions: Array.from(transactions.values()),
submitTransaction
};
}
最佳实践:
注意事项:
What is event filtering? How to efficiently query historical events?
What is event filtering? How to efficiently query historical events?
考察点:事件查询优化。
答案:
事件过滤是通过指定条件来筛选智能合约事件的技术,允许高效地从大量区块链数据中提取特定信息。通过合理的过滤策略和查询优化,可以显著提高历史数据检索的性能和准确性。
事件过滤基础概念:
// 示例合约事件
contract TokenContract {
event Transfer(
address indexed from, // indexed 参数可以被过滤
address indexed to, // indexed 参数可以被过滤
uint256 value // 非 indexed 参数不能直接过滤
);
event Approval(
address indexed owner, // indexed 参数
address indexed spender, // indexed 参数
uint256 value
);
event CustomEvent(
address indexed user,
uint256 indexed eventType,
string data // 非 indexed 数据
);
}
基本事件过滤:
// 1. 查询所有 Transfer 事件
const transferFilter = contract.filters.Transfer();
const allTransfers = await contract.queryFilter(transferFilter, fromBlock, toBlock);
// 2. 查询特定用户的转入事件
const userReceiveFilter = contract.filters.Transfer(null, userAddress);
const userReceives = await contract.queryFilter(userReceiveFilter, fromBlock, toBlock);
// 3. 查询特定用户的转出事件
const userSendFilter = contract.filters.Transfer(userAddress, null);
const userSends = await contract.queryFilter(userSendFilter, fromBlock, toBlock);
// 4. 查询两个地址之间的转账
const specificTransferFilter = contract.filters.Transfer(senderAddress, receiverAddress);
const specificTransfers = await contract.queryFilter(specificTransferFilter, fromBlock, toBlock);
高级过滤技术:
class EventQueryManager {
constructor(contract, provider) {
this.contract = contract;
this.provider = provider;
this.cache = new Map();
}
// 高效的历史事件查询
async queryEventsOptimized(eventName, filters = {}, options = {}) {
const {
fromBlock = 0,
toBlock = 'latest',
batchSize = 10000,
useCache = true
} = options;
const cacheKey = this.getCacheKey(eventName, filters, fromBlock, toBlock);
if (useCache && this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
try {
const events = await this.batchQueryEvents(
eventName,
filters,
fromBlock,
toBlock,
batchSize
);
if (useCache) {
this.cache.set(cacheKey, events);
}
return events;
} catch (error) {
console.error("事件查询失败:", error);
throw error;
}
}
// 批量查询事件(分页处理)
async batchQueryEvents(eventName, filters, fromBlock, toBlock, batchSize) {
const allEvents = [];
let currentBlock = typeof fromBlock === 'number' ? fromBlock : 0;
const endBlock = toBlock === 'latest' ? await this.provider.getBlockNumber() : toBlock;
while (currentBlock <= endBlock) {
const batchEndBlock = Math.min(currentBlock + batchSize - 1, endBlock);
try {
const batchEvents = await this.querySingleBatch(
eventName,
filters,
currentBlock,
batchEndBlock
);
allEvents.push(...batchEvents);
console.log(`查询区块 ${currentBlock} - ${batchEndBlock}: ${batchEvents.length} 个事件`);
currentBlock = batchEndBlock + 1;
// 添加延迟避免速率限制
await this.rateLimitDelay();
} catch (error) {
if (error.code === -32005) { // 查询范围过大
batchSize = Math.floor(batchSize / 2);
if (batchSize < 100) {
throw new Error("查询范围过小仍然失败");
}
continue;
}
throw error;
}
}
return allEvents;
}
// 单批次查询
async querySingleBatch(eventName, filters, fromBlock, toBlock) {
const filter = this.contract.filters[eventName](...Object.values(filters));
return await this.contract.queryFilter(filter, fromBlock, toBlock);
}
// 速率限制延迟
async rateLimitDelay() {
await new Promise(resolve => setTimeout(resolve, 100)); // 100ms 延迟
}
// 生成缓存键
getCacheKey(eventName, filters, fromBlock, toBlock) {
return `${eventName}_${JSON.stringify(filters)}_${fromBlock}_${toBlock}`;
}
// 清除缓存
clearCache() {
this.cache.clear();
}
}
复杂事件查询示例:
// 用户交易历史查询
async function getUserTransactionHistory(userAddress, tokenContract) {
const queryManager = new EventQueryManager(tokenContract, provider);
// 查询用户相关的所有转账事件
const [sentTransfers, receivedTransfers] = await Promise.all([
queryManager.queryEventsOptimized('Transfer', { from: userAddress }),
queryManager.queryEventsOptimized('Transfer', { to: userAddress })
]);
// 合并和排序事件
const allTransfers = [...sentTransfers, ...receivedTransfers]
.sort((a, b) => a.blockNumber - b.blockNumber);
// 处理事件数据
const transactionHistory = allTransfers.map(event => ({
hash: event.transactionHash,
blockNumber: event.blockNumber,
from: event.args.from,
to: event.args.to,
amount: ethers.utils.formatEther(event.args.value),
type: event.args.from.toLowerCase() === userAddress.toLowerCase() ? 'sent' : 'received',
timestamp: null // 需要获取区块时间戳
}));
// 批量获取时间戳
const timestamps = await getBatchTimestamps(
transactionHistory.map(tx => tx.blockNumber)
);
transactionHistory.forEach((tx, index) => {
tx.timestamp = timestamps[tx.blockNumber];
});
return transactionHistory;
}
// 批量获取区块时间戳
async function getBatchTimestamps(blockNumbers) {
const uniqueBlocks = [...new Set(blockNumbers)];
const timestampMap = new Map();
// 并发获取区块信息
const blockPromises = uniqueBlocks.map(async (blockNumber) => {
try {
const block = await provider.getBlock(blockNumber);
return { blockNumber, timestamp: block.timestamp };
} catch (error) {
console.error(`获取区块 ${blockNumber} 失败:`, error);
return { blockNumber, timestamp: null };
}
});
const blocks = await Promise.all(blockPromises);
blocks.forEach(({ blockNumber, timestamp }) => {
timestampMap.set(blockNumber, timestamp);
});
return timestampMap;
}
实时事件监听优化:
class RealTimeEventMonitor {
constructor(contract, provider) {
this.contract = contract;
this.provider = provider;
this.listeners = new Map();
this.isListening = false;
}
// 开始监听特定事件
startListening(eventName, filters = {}, callback) {
const filterKey = this.getFilterKey(eventName, filters);
if (this.listeners.has(filterKey)) {
console.warn("该过滤器已在监听中");
return;
}
const filter = this.contract.filters[eventName](...Object.values(filters));
const wrappedCallback = (log) => {
try {
const parsedLog = this.contract.interface.parseLog(log);
callback(parsedLog, log);
} catch (error) {
console.error("事件解析失败:", error);
}
};
this.provider.on(filter, wrappedCallback);
this.listeners.set(filterKey, { filter, callback: wrappedCallback });
console.log(`开始监听事件: ${eventName}`);
this.isListening = true;
}
// 停止监听特定事件
stopListening(eventName, filters = {}) {
const filterKey = this.getFilterKey(eventName, filters);
const listener = this.listeners.get(filterKey);
if (listener) {
this.provider.off(listener.filter, listener.callback);
this.listeners.delete(filterKey);
console.log(`停止监听事件: ${eventName}`);
}
if (this.listeners.size === 0) {
this.isListening = false;
}
}
// 停止所有监听
stopAllListening() {
this.listeners.forEach((listener) => {
this.provider.off(listener.filter, listener.callback);
});
this.listeners.clear();
this.isListening = false;
console.log("停止所有事件监听");
}
getFilterKey(eventName, filters) {
return `${eventName}_${JSON.stringify(filters)}`;
}
}
事件查询性能优化:
// 智能区块范围优化
class SmartBlockRangeOptimizer {
constructor(provider) {
this.provider = provider;
this.maxBatchSize = 50000;
this.minBatchSize = 1000;
}
// 自适应批次大小
async getOptimalBatchSize(contract, eventName, startBlock) {
let batchSize = this.maxBatchSize;
while (batchSize >= this.minBatchSize) {
try {
const filter = contract.filters[eventName]();
const testEndBlock = startBlock + batchSize - 1;
// 测试查询
await contract.queryFilter(filter, startBlock, testEndBlock);
return batchSize;
} catch (error) {
if (error.code === -32005) { // 范围过大
batchSize = Math.floor(batchSize / 2);
} else {
throw error;
}
}
}
return this.minBatchSize;
}
// 基于事件密度调整批次大小
adjustBatchSizeByDensity(eventCount, batchSize) {
const density = eventCount / batchSize;
if (density > 0.1) { // 事件密度高,减小批次
return Math.max(Math.floor(batchSize * 0.7), this.minBatchSize);
} else if (density < 0.01) { // 事件密度低,增大批次
return Math.min(Math.floor(batchSize * 1.5), this.maxBatchSize);
}
return batchSize;
}
}
// 使用布隆过滤器预筛选
class BloomFilterEventQuery {
constructor() {
this.addressBloom = new Set(); // 简化版布隆过滤器
}
// 添加地址到过滤器
addAddress(address) {
this.addressBloom.add(address.toLowerCase());
}
// 快速检查地址是否可能存在
mightContain(address) {
return this.addressBloom.has(address.toLowerCase());
}
// 预过滤事件
preFilterEvents(events, targetAddresses) {
targetAddresses.forEach(addr => this.addAddress(addr));
return events.filter(event => {
const { from, to } = event.args || event;
return this.mightContain(from) || this.mightContain(to);
});
}
}
最佳实践:
性能考虑:
How to implement transaction retry mechanisms? What issues need attention?
How to implement transaction retry mechanisms? What issues need attention?
考察点:交易重试策略。
答案:
交易重试机制是处理区块链网络不稳定性和临时故障的重要技术。合理的重试策略可以提高交易成功率和用户体验,但需要注意避免重复执行、nonce 管理、Gas 费用控制等关键问题。
重试机制的核心组件:
class TransactionRetryManager {
constructor(signer, options = {}) {
this.signer = signer;
this.options = {
maxRetries: options.maxRetries || 3,
baseDelay: options.baseDelay || 1000,
maxDelay: options.maxDelay || 30000,
backoffMultiplier: options.backoffMultiplier || 2,
gasPriceIncrement: options.gasPriceIncrement || 1.2, // 每次重试增加20%
...options
};
this.activeTransactions = new Map();
this.nonceManager = new NonceManager(signer);
}
// 执行带重试的交易
async executeWithRetry(transactionRequest, retryOptions = {}) {
const txId = this.generateTxId();
const finalOptions = { ...this.options, ...retryOptions };
let attempt = 0;
let lastError;
while (attempt < finalOptions.maxRetries) {
attempt++;
try {
console.log(`交易尝试 ${attempt}/${finalOptions.maxRetries}`);
// 获取并锁定 nonce
const nonce = await this.nonceManager.getNextNonce();
const adjustedTx = await this.adjustTransactionForRetry(
transactionRequest,
attempt,
finalOptions
);
adjustedTx.nonce = nonce;
// 记录活跃交易
this.activeTransactions.set(txId, {
nonce,
attempt,
originalRequest: transactionRequest,
status: 'pending'
});
const tx = await this.signer.sendTransaction(adjustedTx);
// 等待确认
const receipt = await this.waitForConfirmation(tx, finalOptions);
// 清理
this.activeTransactions.delete(txId);
this.nonceManager.confirmNonce(nonce);
return receipt;
} catch (error) {
lastError = error;
// 检查是否应该重试
if (!this.shouldRetry(error, attempt, finalOptions)) {
break;
}
// 释放 nonce(如果交易未提交)
if (this.isPreSubmissionError(error)) {
this.nonceManager.releaseNonce(
this.activeTransactions.get(txId)?.nonce
);
}
// 等待后重试
const delay = this.calculateRetryDelay(attempt, finalOptions);
console.log(`${delay}ms 后重试...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
// 清理失败的交易
this.activeTransactions.delete(txId);
throw new Error(`交易重试失败,尝试次数: ${attempt}, 最后错误: ${lastError.message}`);
}
// 调整重试参数
async adjustTransactionForRetry(originalTx, attempt, options) {
const adjustedTx = { ...originalTx };
// 调整 Gas 价格
if (originalTx.gasPrice) {
const multiplier = Math.pow(options.gasPriceIncrement, attempt - 1);
adjustedTx.gasPrice = originalTx.gasPrice.mul(
Math.floor(multiplier * 100)
).div(100);
}
// EIP-1559 网络的费用调整
if (originalTx.maxFeePerGas) {
const multiplier = Math.pow(options.gasPriceIncrement, attempt - 1);
adjustedTx.maxFeePerGas = originalTx.maxFeePerGas.mul(
Math.floor(multiplier * 100)
).div(100);
if (originalTx.maxPriorityFeePerGas) {
adjustedTx.maxPriorityFeePerGas = originalTx.maxPriorityFeePerGas.mul(
Math.floor(multiplier * 100)
).div(100);
}
}
// 调整 Gas Limit(如果之前因为 Gas 不足失败)
if (originalTx.gasLimit && attempt > 1) {
adjustedTx.gasLimit = originalTx.gasLimit.mul(110).div(100); // 增加10%
}
return adjustedTx;
}
// 判断是否应该重试
shouldRetry(error, attempt, options) {
if (attempt >= options.maxRetries) {
return false;
}
// 不应重试的错误类型
const nonRetryableErrors = [
'ACTION_REJECTED', // 用户拒绝
'INSUFFICIENT_FUNDS', // 余额不足
'INVALID_ARGUMENT', // 参数错误
'CONTRACT_ERROR' // 合约逻辑错误
];
const errorCode = error.code || error.reason;
// 检查错误码
if (nonRetryableErrors.some(code =>
errorCode.toString().includes(code) ||
error.message.includes(code)
)) {
return false;
}
// 可重试的错误类型
const retryableErrors = [
'NETWORK_ERROR',
'TIMEOUT',
'SERVER_ERROR',
'REPLACEMENT_UNDERPRICED',
'NONCE_EXPIRED'
];
return retryableErrors.some(code =>
errorCode.toString().includes(code) ||
error.message.includes(code)
);
}
// 计算重试延迟
calculateRetryDelay(attempt, options) {
const delay = options.baseDelay * Math.pow(options.backoffMultiplier, attempt - 1);
return Math.min(delay, options.maxDelay);
}
// 判断是否为提交前错误
isPreSubmissionError(error) {
const preSubmissionErrors = [
'INSUFFICIENT_FUNDS',
'INVALID_ARGUMENT',
'UNPREDICTABLE_GAS_LIMIT'
];
return preSubmissionErrors.some(code =>
error.code?.includes(code) ||
error.message.includes(code)
);
}
// 等待交易确认
async waitForConfirmation(tx, options) {
const timeout = options.confirmationTimeout || 300000; // 5分钟
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error("交易确认超时")), timeout);
});
try {
const receipt = await Promise.race([
tx.wait(options.confirmations || 1),
timeoutPromise
]);
if (receipt.status === 0) {
throw new Error("交易执行失败");
}
return receipt;
} catch (error) {
// 如果是超时错误,尝试查询交易状态
if (error.message.includes("超时")) {
const receipt = await this.signer.provider.getTransactionReceipt(tx.hash);
if (receipt) {
return receipt;
}
}
throw error;
}
}
generateTxId() {
return `retry_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
Nonce 管理器:
class NonceManager {
constructor(signer) {
this.signer = signer;
this.pendingNonces = new Set();
this.currentNonce = null;
this.lock = false;
}
async getNextNonce() {
// 等待锁释放
while (this.lock) {
await new Promise(resolve => setTimeout(resolve, 10));
}
this.lock = true;
try {
if (this.currentNonce === null) {
this.currentNonce = await this.signer.getTransactionCount('pending');
}
// 找到下一个可用的 nonce
while (this.pendingNonces.has(this.currentNonce)) {
this.currentNonce++;
}
const nonce = this.currentNonce;
this.pendingNonces.add(nonce);
this.currentNonce++;
return nonce;
} finally {
this.lock = false;
}
}
confirmNonce(nonce) {
this.pendingNonces.delete(nonce);
}
releaseNonce(nonce) {
if (nonce !== undefined) {
this.pendingNonces.delete(nonce);
}
}
async reset() {
this.lock = true;
try {
this.currentNonce = await this.signer.getTransactionCount('pending');
this.pendingNonces.clear();
} finally {
this.lock = false;
}
}
}
交易替换机制:
class TransactionReplacement {
constructor(signer) {
this.signer = signer;
this.pendingReplacements = new Map();
}
// 替换卡住的交易
async replaceTransaction(originalTxHash, newGasPrice, speedUp = true) {
try {
// 获取原始交易
const originalTx = await this.signer.provider.getTransaction(originalTxHash);
if (!originalTx) {
throw new Error("原始交易不存在");
}
// 检查交易是否已确认
const receipt = await this.signer.provider.getTransactionReceipt(originalTxHash);
if (receipt) {
throw new Error("交易已确认,无法替换");
}
// 构造替换交易
const replacementTx = {
...originalTx,
gasPrice: newGasPrice,
nonce: originalTx.nonce // 使用相同的 nonce
};
// 删除交易哈希字段
delete replacementTx.hash;
delete replacementTx.r;
delete replacementTx.s;
delete replacementTx.v;
if (speedUp) {
// 加速交易 - 保持相同的目标和数据
console.log("加速交易:", originalTxHash);
} else {
// 取消交易 - 发送给自己
replacementTx.to = await this.signer.getAddress();
replacementTx.value = 0;
replacementTx.data = "0x";
console.log("取消交易:", originalTxHash);
}
const newTx = await this.signer.sendTransaction(replacementTx);
this.pendingReplacements.set(originalTxHash, {
replacementHash: newTx.hash,
type: speedUp ? 'speedup' : 'cancel',
timestamp: Date.now()
});
return newTx;
} catch (error) {
console.error("交易替换失败:", error);
throw error;
}
}
// 监控替换结果
async monitorReplacement(originalTxHash) {
const replacement = this.pendingReplacements.get(originalTxHash);
if (!replacement) {
throw new Error("没有找到替换交易");
}
try {
const receipt = await this.signer.provider.waitForTransaction(
replacement.replacementHash
);
console.log(`交易替换成功: ${originalTxHash} -> ${replacement.replacementHash}`);
this.pendingReplacements.delete(originalTxHash);
return receipt;
} catch (error) {
console.error("替换交易失败:", error);
throw error;
}
}
}
智能重试策略:
class SmartRetryStrategy {
constructor(provider) {
this.provider = provider;
this.networkConditions = {
gasPrice: null,
blockTime: null,
congestionLevel: 'normal'
};
}
// 更新网络状况
async updateNetworkConditions() {
try {
const [gasPrice, latestBlock, previousBlock] = await Promise.all([
this.provider.getGasPrice(),
this.provider.getBlock('latest'),
this.provider.getBlock('latest').then(block =>
this.provider.getBlock(block.number - 1)
)
]);
this.networkConditions = {
gasPrice,
blockTime: latestBlock.timestamp - previousBlock.timestamp,
congestionLevel: this.assessCongestionLevel(gasPrice)
};
} catch (error) {
console.error("网络状况更新失败:", error);
}
}
// 评估网络拥堵程度
assessCongestionLevel(gasPrice) {
const gasPriceGwei = parseFloat(ethers.utils.formatUnits(gasPrice, 'gwei'));
if (gasPriceGwei > 100) return 'high';
if (gasPriceGwei > 50) return 'medium';
return 'normal';
}
// 根据网络状况调整重试参数
adjustRetryParameters(baseOptions) {
const { congestionLevel } = this.networkConditions;
switch (congestionLevel) {
case 'high':
return {
...baseOptions,
maxRetries: 5,
baseDelay: 3000,
gasPriceIncrement: 1.5
};
case 'medium':
return {
...baseOptions,
maxRetries: 4,
baseDelay: 2000,
gasPriceIncrement: 1.3
};
default:
return baseOptions;
}
}
}
使用示例:
// 创建重试管理器
const retryManager = new TransactionRetryManager(signer, {
maxRetries: 3,
baseDelay: 1000,
gasPriceIncrement: 1.2
});
// 执行交易
async function executeTransactionWithRetry() {
try {
const txRequest = {
to: contractAddress,
data: contract.interface.encodeFunctionData("transfer", [
recipientAddress,
ethers.utils.parseEther("1.0")
]),
gasLimit: 100000,
gasPrice: ethers.utils.parseUnits("20", "gwei")
};
const receipt = await retryManager.executeWithRetry(txRequest);
console.log("交易成功:", receipt.transactionHash);
return receipt;
} catch (error) {
console.error("交易最终失败:", error);
// 可以考虑其他措施,如用户通知、数据回滚等
throw error;
}
}
重要注意事项:
最佳实践:
What is the transaction pool (Mempool)? How to handle transaction congestion?
What is the transaction pool (Mempool)? How to handle transaction congestion?
考察点:网络拥堵处理。
答案:
交易池(Mempool,Memory Pool)是区块链网络中存储待确认交易的内存区域。当用户提交交易后,交易首先进入 Mempool 等待矿工或验证者选择打包到区块中。处理交易拥堵需要理解 Mempool 机制,并采用动态调整策略来优化交易确认时间。
Mempool 工作机制:
// Mempool 状态监控
class MempoolMonitor {
constructor(provider) {
this.provider = provider;
this.congestionMetrics = {
pendingTxCount: 0,
averageGasPrice: 0,
congestionLevel: 'normal',
estimatedWaitTime: 0
};
}
// 监控 Mempool 状态
async updateMempoolStatus() {
try {
// 获取待处理交易数量(某些提供商支持)
const pendingBlock = await this.provider.getBlock('pending');
const latestBlock = await this.provider.getBlock('latest');
// 计算拥堵指标
this.congestionMetrics = {
pendingTxCount: pendingBlock?.transactions?.length || 0,
blockUtilization: this.calculateBlockUtilization(latestBlock),
averageGasPrice: await this.getAverageGasPrice(),
congestionLevel: this.assessCongestionLevel(),
estimatedWaitTime: this.estimateWaitTime()
};
console.log("Mempool 状态:", this.congestionMetrics);
} catch (error) {
console.error("Mempool 监控失败:", error);
}
}
// 计算区块利用率
calculateBlockUtilization(block) {
if (!block || !block.gasUsed || !block.gasLimit) {
return 0;
}
return block.gasUsed.div(block.gasLimit).mul(100).toNumber();
}
// 获取平均 Gas 价格
async getAverageGasPrice() {
try {
const gasPrice = await this.provider.getGasPrice();
return parseFloat(ethers.utils.formatUnits(gasPrice, 'gwei'));
} catch (error) {
return 0;
}
}
// 评估拥堵级别
assessCongestionLevel() {
const { averageGasPrice, blockUtilization } = this.congestionMetrics;
if (averageGasPrice > 100 || blockUtilization > 95) {
return 'severe';
} else if (averageGasPrice > 50 || blockUtilization > 80) {
return 'high';
} else if (averageGasPrice > 25 || blockUtilization > 60) {
return 'medium';
}
return 'normal';
}
// 估算等待时间
estimateWaitTime() {
const { congestionLevel, averageGasPrice } = this.congestionMetrics;
// 基于历史数据的简单估算
const waitTimeMap = {
'severe': 30, // 30+ 分钟
'high': 15, // 15 分钟
'medium': 5, // 5 分钟
'normal': 2 // 2 分钟
};
return waitTimeMap[congestionLevel] || 2;
}
}
动态 Gas 策略:
class DynamicGasStrategy {
constructor(provider, mempoolMonitor) {
this.provider = provider;
this.mempoolMonitor = mempoolMonitor;
this.gasHistory = [];
}
// 获取推荐的 Gas 设置
async getRecommendedGas(urgency = 'standard') {
await this.mempoolMonitor.updateMempoolStatus();
const { congestionLevel, averageGasPrice } = this.mempoolMonitor.congestionMetrics;
const baseGasPrice = await this.provider.getGasPrice();
const urgencyMultipliers = {
'slow': 0.8,
'standard': 1.0,
'fast': 1.2,
'urgent': 1.5
};
const congestionMultipliers = {
'normal': 1.0,
'medium': 1.2,
'high': 1.5,
'severe': 2.0
};
const multiplier = urgencyMultipliers[urgency] * congestionMultipliers[congestionLevel];
const recommendedGasPrice = baseGasPrice.mul(Math.floor(multiplier * 100)).div(100);
return {
gasPrice: recommendedGasPrice,
estimatedTime: this.getEstimatedConfirmationTime(urgency, congestionLevel),
confidence: this.calculateConfidence(congestionLevel)
};
}
// EIP-1559 费用建议
async getEIP1559Recommendation(urgency = 'standard') {
try {
const feeData = await this.provider.getFeeData();
const { congestionLevel } = this.mempoolMonitor.congestionMetrics;
const urgencyMultipliers = {
'slow': { base: 0.9, priority: 0.5 },
'standard': { base: 1.0, priority: 1.0 },
'fast': { base: 1.1, priority: 2.0 },
'urgent': { base: 1.2, priority: 3.0 }
};
const multiplier = urgencyMultipliers[urgency];
const maxFeePerGas = feeData.maxFeePerGas.mul(
Math.floor(multiplier.base * 100)
).div(100);
const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas.mul(
Math.floor(multiplier.priority * 100)
).div(100);
return {
maxFeePerGas,
maxPriorityFeePerGas,
estimatedTime: this.getEstimatedConfirmationTime(urgency, congestionLevel)
};
} catch (error) {
console.error("EIP-1559 费用获取失败:", error);
throw error;
}
}
getEstimatedConfirmationTime(urgency, congestionLevel) {
const baseTimeMap = {
'slow': { normal: 10, medium: 20, high: 30, severe: 45 },
'standard': { normal: 3, medium: 8, high: 15, severe: 25 },
'fast': { normal: 1, medium: 3, high: 8, severe: 15 },
'urgent': { normal: 0.5, medium: 1, high: 3, severe: 8 }
};
return baseTimeMap[urgency][congestionLevel] || 3;
}
calculateConfidence(congestionLevel) {
const confidenceMap = {
'normal': 0.9,
'medium': 0.7,
'high': 0.5,
'severe': 0.3
};
return confidenceMap[congestionLevel] || 0.7;
}
}
拥堵处理策略:
class CongestionHandler {
constructor(provider, gasStrategy) {
this.provider = provider;
this.gasStrategy = gasStrategy;
this.queuedTransactions = [];
this.isProcessing = false;
}
// 智能交易调度
async scheduleTransaction(txRequest, options = {}) {
const {
urgency = 'standard',
maxWaitTime = 300000, // 5分钟
allowQueuing = true
} = options;
// 检查当前网络状况
const gasRecommendation = await this.gasStrategy.getRecommendedGas(urgency);
if (gasRecommendation.confidence < 0.5 && allowQueuing) {
// 网络拥堵严重,考虑排队
return this.queueTransaction(txRequest, options);
}
// 立即执行
return this.executeImmediately(txRequest, gasRecommendation);
}
// 排队交易
async queueTransaction(txRequest, options) {
const queueId = this.generateQueueId();
this.queuedTransactions.push({
id: queueId,
txRequest,
options,
timestamp: Date.now(),
status: 'queued'
});
console.log(`交易已排队: ${queueId}`);
// 开始处理队列
if (!this.isProcessing) {
this.processQueue();
}
return { queueId, status: 'queued' };
}
// 处理交易队列
async processQueue() {
this.isProcessing = true;
while (this.queuedTransactions.length > 0) {
const queuedTx = this.queuedTransactions[0];
try {
// 检查是否应该执行
const gasRecommendation = await this.gasStrategy.getRecommendedGas(
queuedTx.options.urgency
);
if (gasRecommendation.confidence > 0.6) {
// 网络状况改善,执行交易
await this.executeQueuedTransaction(queuedTx);
this.queuedTransactions.shift();
} else {
// 继续等待
console.log("网络仍然拥堵,继续等待...");
await new Promise(resolve => setTimeout(resolve, 30000)); // 等待30秒
}
} catch (error) {
console.error("队列处理错误:", error);
queuedTx.status = 'failed';
queuedTx.error = error.message;
this.queuedTransactions.shift();
}
}
this.isProcessing = false;
}
// 执行排队的交易
async executeQueuedTransaction(queuedTx) {
queuedTx.status = 'executing';
const gasRecommendation = await this.gasStrategy.getRecommendedGas(
queuedTx.options.urgency
);
const result = await this.executeImmediately(
queuedTx.txRequest,
gasRecommendation
);
queuedTx.status = 'completed';
queuedTx.result = result;
return result;
}
// 立即执行交易
async executeImmediately(txRequest, gasRecommendation) {
const finalTxRequest = {
...txRequest,
gasPrice: gasRecommendation.gasPrice
};
const signer = this.provider.getSigner();
const tx = await signer.sendTransaction(finalTxRequest);
console.log(`交易已提交: ${tx.hash}, 预计确认时间: ${gasRecommendation.estimatedTime} 分钟`);
return tx;
}
generateQueueId() {
return `queue_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
批处理优化:
class BatchTransactionOptimizer {
constructor(provider) {
this.provider = provider;
this.pendingBatch = [];
this.batchTimer = null;
this.batchDelay = 5000; // 5秒批处理延迟
}
// 添加交易到批处理
addToBatch(txRequest, callback) {
this.pendingBatch.push({
txRequest,
callback,
timestamp: Date.now()
});
// 重置计时器
if (this.batchTimer) {
clearTimeout(this.batchTimer);
}
this.batchTimer = setTimeout(() => {
this.processBatch();
}, this.batchDelay);
// 如果批次已满,立即处理
if (this.pendingBatch.length >= 10) {
this.processBatch();
}
}
// 处理批次
async processBatch() {
if (this.pendingBatch.length === 0) return;
const batch = [...this.pendingBatch];
this.pendingBatch = [];
if (this.batchTimer) {
clearTimeout(this.batchTimer);
this.batchTimer = null;
}
console.log(`处理批次: ${batch.length} 个交易`);
// 获取最优 Gas 价格
const gasRecommendation = await this.getOptimalBatchGasPrice();
// 获取起始 nonce
const signer = this.provider.getSigner();
let nonce = await signer.getTransactionCount('pending');
// 并发发送交易
const txPromises = batch.map((item, index) =>
this.sendBatchTransaction(item, gasRecommendation.gasPrice, nonce + index)
);
try {
const results = await Promise.allSettled(txPromises);
// 处理结果
results.forEach((result, index) => {
const { callback } = batch[index];
if (result.status === 'fulfilled') {
callback(null, result.value);
} else {
callback(result.reason);
}
});
} catch (error) {
console.error("批处理失败:", error);
}
}
async sendBatchTransaction(item, gasPrice, nonce) {
const signer = this.provider.getSigner();
const tx = await signer.sendTransaction({
...item.txRequest,
gasPrice,
nonce
});
return tx;
}
async getOptimalBatchGasPrice() {
// 为批处理获取适中的 Gas 价格
const baseGasPrice = await this.provider.getGasPrice();
return {
gasPrice: baseGasPrice.mul(110).div(100) // +10% 确保及时确认
};
}
}
用户体验优化:
// 拥堵状况用户提示
class CongestionUI {
constructor(congestionHandler) {
this.congestionHandler = congestionHandler;
}
async showCongestionWarning() {
const status = await this.congestionHandler.mempoolMonitor.updateMempoolStatus();
const { congestionLevel, estimatedWaitTime } = status;
if (congestionLevel === 'high' || congestionLevel === 'severe') {
return {
show: true,
level: congestionLevel,
message: `网络当前拥堵程度: ${congestionLevel}`,
estimatedWait: `预计等待时间: ${estimatedWaitTime} 分钟`,
suggestions: this.getCongestionSuggestions(congestionLevel)
};
}
return { show: false };
}
getCongestionSuggestions(level) {
const suggestions = {
'high': [
"考虑稍后重试",
"使用更高的 Gas 价格加速",
"合并多个操作以节省费用"
],
'severe': [
"建议延后非紧急交易",
"考虑使用 Layer 2 方案",
"等待网络拥堵缓解"
]
};
return suggestions[level] || [];
}
}
监控和预警:
class CongestionAlerts {
constructor(mempoolMonitor) {
this.mempoolMonitor = mempoolMonitor;
this.subscribers = new Set();
this.alertThresholds = {
gasPrice: 80, // Gwei
blockUtilization: 90, // %
waitTime: 20 // minutes
};
}
subscribe(callback) {
this.subscribers.add(callback);
return () => this.subscribers.delete(callback);
}
startMonitoring() {
setInterval(async () => {
await this.checkAndAlert();
}, 60000); // 每分钟检查
}
async checkAndAlert() {
const metrics = this.mempoolMonitor.congestionMetrics;
const alerts = [];
if (metrics.averageGasPrice > this.alertThresholds.gasPrice) {
alerts.push({
type: 'highGasPrice',
message: `Gas 价格异常高: ${metrics.averageGasPrice} Gwei`
});
}
if (metrics.blockUtilization > this.alertThresholds.blockUtilization) {
alerts.push({
type: 'highBlockUtilization',
message: `区块利用率过高: ${metrics.blockUtilization}%`
});
}
if (alerts.length > 0) {
this.notifySubscribers(alerts);
}
}
notifySubscribers(alerts) {
this.subscribers.forEach(callback => {
try {
callback(alerts);
} catch (error) {
console.error("Alert callback error:", error);
}
});
}
}
最佳实践:
注意事项:
How to implement local caching of contract state to reduce RPC calls?
How to implement local caching of contract state to reduce RPC calls?
考察点:性能优化策略。
答案:
本地缓存合约状态是提升 DApp 性能的关键技术,通过智能缓存策略可以显著减少 RPC 调用次数,提高响应速度并降低 API 成本。需要考虑缓存有效性、数据一致性、存储策略和缓存失效机制等多个方面。
缓存系统架构:
class ContractStateCache {
constructor(options = {}) {
this.cache = new Map();
this.blockCache = new Map();
this.eventListeners = new Map();
this.options = {
defaultTTL: options.defaultTTL || 300000, // 5分钟默认TTL
maxCacheSize: options.maxCacheSize || 1000,
enableBlockCache: options.enableBlockCache !== false,
enableEventInvalidation: options.enableEventInvalidation !== false,
storage: options.storage || 'memory' // 'memory', 'localStorage', 'indexedDB'
};
this.stats = {
hits: 0,
misses: 0,
evictions: 0
};
}
// 生成缓存键
generateCacheKey(contractAddress, method, params = [], blockNumber) {
const paramsStr = JSON.stringify(params);
const blockStr = blockNumber ? `@${blockNumber}` : '';
return `${contractAddress.toLowerCase()}:${method}:${paramsStr}${blockStr}`;
}
// 获取缓存值
async get(key) {
const entry = this.cache.get(key);
if (!entry) {
this.stats.misses++;
return null;
}
// 检查是否过期
if (entry.expiry && Date.now() > entry.expiry) {
this.cache.delete(key);
this.stats.misses++;
return null;
}
// 更新访问时间和计数
entry.lastAccessed = Date.now();
entry.accessCount++;
this.stats.hits++;
return entry.data;
}
// 设置缓存值
set(key, data, options = {}) {
const ttl = options.ttl || this.options.defaultTTL;
const blockNumber = options.blockNumber;
const tags = options.tags || [];
const entry = {
data,
timestamp: Date.now(),
expiry: ttl > 0 ? Date.now() + ttl : null,
lastAccessed: Date.now(),
accessCount: 1,
blockNumber,
tags: new Set(tags)
};
// 检查缓存大小限制
if (this.cache.size >= this.options.maxCacheSize) {
this.evictLRU();
}
this.cache.set(key, entry);
// 持久化存储
if (this.options.storage !== 'memory') {
this.persistToStorage(key, entry);
}
}
// LRU驱逐策略
evictLRU() {
let oldestKey = null;
let oldestTime = Date.now();
for (const [key, entry] of this.cache.entries()) {
if (entry.lastAccessed < oldestTime) {
oldestTime = entry.lastAccessed;
oldestKey = key;
}
}
if (oldestKey) {
this.cache.delete(oldestKey);
this.stats.evictions++;
}
}
// 按标签清除缓存
invalidateByTags(tags) {
const keysToDelete = [];
for (const [key, entry] of this.cache.entries()) {
const hasMatchingTag = tags.some(tag => entry.tags.has(tag));
if (hasMatchingTag) {
keysToDelete.push(key);
}
}
keysToDelete.forEach(key => this.cache.delete(key));
console.log(`按标签清除缓存: ${keysToDelete.length} 个条目`);
}
// 按区块号清除缓存
invalidateByBlock(blockNumber) {
const keysToDelete = [];
for (const [key, entry] of this.cache.entries()) {
if (entry.blockNumber && entry.blockNumber <= blockNumber) {
keysToDelete.push(key);
}
}
keysToDelete.forEach(key => this.cache.delete(key));
}
// 获取缓存统计
getStats() {
const hitRate = this.stats.hits / (this.stats.hits + this.stats.misses) || 0;
return {
...this.stats,
hitRate: Math.round(hitRate * 100) / 100,
cacheSize: this.cache.size
};
}
// 清空缓存
clear() {
this.cache.clear();
if (this.options.storage !== 'memory') {
this.clearStorage();
}
}
}
合约状态缓存管理器:
class ContractStateCacheManager {
constructor(provider, cache) {
this.provider = provider;
this.cache = cache;
this.contracts = new Map();
this.blockNumberCache = null;
this.eventFilters = new Map();
}
// 注册合约
registerContract(address, abi, cacheConfig = {}) {
const contract = new ethers.Contract(address, abi, this.provider);
this.contracts.set(address.toLowerCase(), {
contract,
abi,
config: {
staticMethods: cacheConfig.staticMethods || [], // 永不变化的方法
viewMethods: cacheConfig.viewMethods || [], // 只读方法
ttlOverrides: cacheConfig.ttlOverrides || {}, // 方法特定TTL
eventInvalidation: cacheConfig.eventInvalidation || {} // 事件失效映射
}
});
// 设置事件监听
if (this.cache.options.enableEventInvalidation) {
this.setupEventListeners(address, cacheConfig.eventInvalidation);
}
}
// 缓存合约调用
async cachedCall(contractAddress, method, params = [], options = {}) {
const address = contractAddress.toLowerCase();
const contractInfo = this.contracts.get(address);
if (!contractInfo) {
throw new Error("合约未注册");
}
const { contract, config } = contractInfo;
// 确定缓存策略
const cacheStrategy = this.determineCacheStrategy(method, config, options);
if (!cacheStrategy.cacheable) {
// 不缓存,直接调用
return await contract[method](...params);
}
// 生成缓存键
const blockNumber = cacheStrategy.useBlockNumber ?
await this.getCurrentBlockNumber() : null;
const cacheKey = this.cache.generateCacheKey(
address,
method,
params,
blockNumber
);
// 尝试从缓存获取
const cachedResult = await this.cache.get(cacheKey);
if (cachedResult !== null) {
return cachedResult;
}
// 缓存未命中,执行调用
try {
const result = await contract[method](...params);
// 存储到缓存
const cacheOptions = {
ttl: cacheStrategy.ttl,
blockNumber,
tags: cacheStrategy.tags
};
this.cache.set(cacheKey, result, cacheOptions);
return result;
} catch (error) {
console.error(`合约调用失败: ${method}`, error);
throw error;
}
}
// 确定缓存策略
determineCacheStrategy(method, config, options) {
// 静态方法 - 永久缓存
if (config.staticMethods.includes(method)) {
return {
cacheable: true,
ttl: 0, // 永不过期
useBlockNumber: false,
tags: ['static', method]
};
}
// 视图方法 - 按区块缓存
if (config.viewMethods.includes(method)) {
return {
cacheable: true,
ttl: config.ttlOverrides[method] || 60000, // 1分钟默认
useBlockNumber: true,
tags: ['view', method]
};
}
// 自定义选项
if (options.cache !== false) {
return {
cacheable: true,
ttl: options.ttl || this.cache.options.defaultTTL,
useBlockNumber: options.useBlockNumber || false,
tags: options.tags || [method]
};
}
return { cacheable: false };
}
// 获取当前区块号(带缓存)
async getCurrentBlockNumber() {
const now = Date.now();
if (this.blockNumberCache &&
(now - this.blockNumberCache.timestamp) < 15000) { // 15秒缓存
return this.blockNumberCache.number;
}
const blockNumber = await this.provider.getBlockNumber();
this.blockNumberCache = {
number: blockNumber,
timestamp: now
};
return blockNumber;
}
// 设置事件监听器
setupEventListeners(contractAddress, eventInvalidation) {
const address = contractAddress.toLowerCase();
const contractInfo = this.contracts.get(address);
if (!contractInfo) return;
const { contract } = contractInfo;
// 为每个事件设置监听器
Object.entries(eventInvalidation).forEach(([eventName, invalidationConfig]) => {
const filter = contract.filters[eventName]();
const listener = (...args) => {
const event = args[args.length - 1]; // 最后一个参数是事件对象
this.handleEventInvalidation(
address,
eventName,
event,
invalidationConfig
);
};
contract.on(filter, listener);
// 记录监听器以便清理
const filterKey = `${address}:${eventName}`;
this.eventFilters.set(filterKey, { filter, listener });
});
}
// 处理事件失效
handleEventInvalidation(contractAddress, eventName, event, config) {
console.log(`处理事件失效: ${contractAddress}:${eventName}`);
if (config.invalidateAll) {
// 清除该合约的所有缓存
this.invalidateContract(contractAddress);
} else if (config.invalidateTags) {
// 按标签清除
this.cache.invalidateByTags(config.invalidateTags);
} else if (config.invalidateMethods) {
// 按方法清除
this.invalidateMethods(contractAddress, config.invalidateMethods);
}
}
// 清除特定合约的缓存
invalidateContract(contractAddress) {
const pattern = contractAddress.toLowerCase();
const keysToDelete = [];
for (const key of this.cache.cache.keys()) {
if (key.startsWith(pattern)) {
keysToDelete.push(key);
}
}
keysToDelete.forEach(key => this.cache.cache.delete(key));
}
// 清除特定方法的缓存
invalidateMethods(contractAddress, methods) {
const address = contractAddress.toLowerCase();
const keysToDelete = [];
for (const key of this.cache.cache.keys()) {
if (key.startsWith(address)) {
const hasMethod = methods.some(method =>
key.includes(`:${method}:`)
);
if (hasMethod) {
keysToDelete.push(key);
}
}
}
keysToDelete.forEach(key => this.cache.cache.delete(key));
}
}
批量数据缓存:
class BatchDataCache {
constructor(cacheManager) {
this.cacheManager = cacheManager;
this.batchQueue = [];
this.batchTimer = null;
this.batchDelay = 100; // 100ms 批处理延迟
}
// 批量获取数据
async batchGet(requests) {
// 检查缓存命中
const results = new Array(requests.length);
const uncachedRequests = [];
for (let i = 0; i < requests.length; i++) {
const request = requests[i];
const cacheKey = this.cacheManager.cache.generateCacheKey(
request.contract,
request.method,
request.params
);
const cached = await this.cacheManager.cache.get(cacheKey);
if (cached !== null) {
results[i] = cached;
} else {
uncachedRequests.push({ index: i, ...request });
}
}
// 批量执行未缓存的请求
if (uncachedRequests.length > 0) {
const batchResults = await this.executeBatch(uncachedRequests);
// 填充结果并更新缓存
batchResults.forEach((result, batchIndex) => {
const request = uncachedRequests[batchIndex];
results[request.index] = result;
// 缓存结果
const cacheKey = this.cacheManager.cache.generateCacheKey(
request.contract,
request.method,
request.params
);
this.cacheManager.cache.set(cacheKey, result, {
ttl: request.ttl || 300000
});
});
}
return results;
}
// 执行批量请求
async executeBatch(requests) {
// 使用 multicall 或并发执行
const promises = requests.map(request =>
this.cacheManager.cachedCall(
request.contract,
request.method,
request.params,
{ cache: false } // 避免重复缓存检查
)
);
return Promise.all(promises);
}
}
持久化存储:
class PersistentCacheStorage {
constructor(storageType = 'localStorage') {
this.storageType = storageType;
this.keyPrefix = 'contract_cache_';
}
// 保存到存储
async save(key, data) {
const storageKey = this.keyPrefix + key;
const serialized = JSON.stringify({
...data,
timestamp: Date.now()
});
switch (this.storageType) {
case 'localStorage':
localStorage.setItem(storageKey, serialized);
break;
case 'sessionStorage':
sessionStorage.setItem(storageKey, serialized);
break;
case 'indexedDB':
await this.saveToIndexedDB(storageKey, serialized);
break;
}
}
// 从存储加载
async load(key) {
const storageKey = this.keyPrefix + key;
let serialized = null;
switch (this.storageType) {
case 'localStorage':
serialized = localStorage.getItem(storageKey);
break;
case 'sessionStorage':
serialized = sessionStorage.getItem(storageKey);
break;
case 'indexedDB':
serialized = await this.loadFromIndexedDB(storageKey);
break;
}
if (serialized) {
try {
const data = JSON.parse(serialized);
// 检查是否过期
if (data.expiry && Date.now() > data.expiry) {
this.remove(key);
return null;
}
return data;
} catch (error) {
console.error("缓存数据解析失败:", error);
this.remove(key);
}
}
return null;
}
// 移除存储
remove(key) {
const storageKey = this.keyPrefix + key;
switch (this.storageType) {
case 'localStorage':
localStorage.removeItem(storageKey);
break;
case 'sessionStorage':
sessionStorage.removeItem(storageKey);
break;
case 'indexedDB':
this.removeFromIndexedDB(storageKey);
break;
}
}
// IndexedDB 操作
async saveToIndexedDB(key, data) {
return new Promise((resolve, reject) => {
const request = indexedDB.open('ContractCache', 1);
request.onsuccess = () => {
const db = request.result;
const transaction = db.transaction(['cache'], 'readwrite');
const store = transaction.objectStore('cache');
store.put({ key, data });
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
};
request.onerror = () => reject(request.error);
});
}
}
使用示例:
// 初始化缓存系统
const cache = new ContractStateCache({
defaultTTL: 300000, // 5分钟
maxCacheSize: 2000, // 最大缓存条目数
storage: 'localStorage' // 持久化存储
});
const cacheManager = new ContractStateCacheManager(provider, cache);
// 注册合约
cacheManager.registerContract(tokenAddress, tokenABI, {
staticMethods: ['name', 'symbol', 'decimals'],
viewMethods: ['balanceOf', 'allowance', 'totalSupply'],
ttlOverrides: {
'balanceOf': 60000, // 余额缓存1分钟
'totalSupply': 300000 // 总供应量缓存5分钟
},
eventInvalidation: {
'Transfer': {
invalidateMethods: ['balanceOf']
},
'Approval': {
invalidateMethods: ['allowance']
}
}
});
// 使用缓存调用
async function getUserBalance(userAddress) {
return await cacheManager.cachedCall(
tokenAddress,
'balanceOf',
[userAddress]
);
}
// 批量数据获取
const batchCache = new BatchDataCache(cacheManager);
const requests = [
{ contract: tokenAddress, method: 'balanceOf', params: [user1] },
{ contract: tokenAddress, method: 'balanceOf', params: [user2] },
{ contract: tokenAddress, method: 'totalSupply', params: [] }
];
const results = await batchCache.batchGet(requests);
最佳实践:
注意事项:
What is signature verification? How to verify message signatures on the frontend?
What is signature verification? How to verify message signatures on the frontend?
考察点:签名验证机制。
答案:
签名验证是区块链中确认消息来源和完整性的密码学机制。通过椭圆曲线数字签名算法(ECDSA),可以验证消息确实由特定私钥的持有者签名且未被篡改。在 Web3 应用中,签名验证广泛用于身份认证、消息验证和离线授权等场景。
签名验证原理:
// 基础签名验证流程
class MessageSignatureVerifier {
constructor(provider) {
this.provider = provider;
}
// 创建消息签名
async signMessage(signer, message) {
try {
// 使用 EIP-191 标准签名
const signature = await signer.signMessage(message);
return {
message,
signature,
address: await signer.getAddress(),
timestamp: Date.now()
};
} catch (error) {
console.error("消息签名失败:", error);
throw error;
}
}
// 验证消息签名
verifyMessageSignature(message, signature, expectedAddress) {
try {
// 恢复签名者地址
const recoveredAddress = ethers.utils.verifyMessage(message, signature);
// 比较地址(不区分大小写)
const isValid = recoveredAddress.toLowerCase() === expectedAddress.toLowerCase();
return {
isValid,
recoveredAddress,
expectedAddress
};
} catch (error) {
console.error("签名验证失败:", error);
return {
isValid: false,
error: error.message
};
}
}
// EIP-712 结构化数据签名
async signTypedData(signer, domain, types, value) {
try {
// 使用 EIP-712 标准
const signature = await signer._signTypedData(domain, types, value);
return {
domain,
types,
value,
signature,
address: await signer.getAddress()
};
} catch (error) {
console.error("结构化数据签名失败:", error);
throw error;
}
}
// 验证 EIP-712 签名
verifyTypedData(domain, types, value, signature, expectedAddress) {
try {
const recoveredAddress = ethers.utils.verifyTypedData(
domain,
types,
value,
signature
);
const isValid = recoveredAddress.toLowerCase() === expectedAddress.toLowerCase();
return {
isValid,
recoveredAddress,
expectedAddress
};
} catch (error) {
console.error("EIP-712 签名验证失败:", error);
return {
isValid: false,
error: error.message
};
}
}
}
EIP-712 结构化签名:
// EIP-712 域和类型定义
class EIP712SignatureHandler {
constructor(contractName, version, chainId, verifyingContract) {
this.domain = {
name: contractName,
version: version,
chainId: chainId,
verifyingContract: verifyingContract
};
// 定义常用的数据类型
this.types = {
// 授权类型
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' }
],
// 投票类型
Vote: [
{ name: 'voter', type: 'address' },
{ name: 'proposalId', type: 'uint256' },
{ name: 'support', type: 'bool' },
{ name: 'weight', type: 'uint256' },
{ name: 'nonce', type: 'uint256' }
],
// 订单类型
Order: [
{ name: 'maker', type: 'address' },
{ name: 'taker', type: 'address' },
{ name: 'tokenA', type: 'address' },
{ name: 'tokenB', type: 'address' },
{ name: 'amountA', type: 'uint256' },
{ name: 'amountB', type: 'uint256' },
{ name: 'expiration', type: 'uint256' },
{ name: 'nonce', type: 'uint256' }
]
};
}
// 创建 Permit 签名
async createPermitSignature(signer, permitData) {
const value = {
owner: permitData.owner,
spender: permitData.spender,
value: permitData.value,
nonce: permitData.nonce,
deadline: permitData.deadline
};
const signature = await signer._signTypedData(
this.domain,
{ Permit: this.types.Permit },
value
);
// 分解签名
const { r, s, v } = ethers.utils.splitSignature(signature);
return {
...value,
signature,
r,
s,
v
};
}
// 验证 Permit 签名
verifyPermitSignature(permitData, signature) {
const recoveredAddress = ethers.utils.verifyTypedData(
this.domain,
{ Permit: this.types.Permit },
{
owner: permitData.owner,
spender: permitData.spender,
value: permitData.value,
nonce: permitData.nonce,
deadline: permitData.deadline
},
signature
);
return {
isValid: recoveredAddress.toLowerCase() === permitData.owner.toLowerCase(),
recoveredAddress
};
}
// 创建投票签名
async createVoteSignature(signer, voteData) {
const value = {
voter: voteData.voter,
proposalId: voteData.proposalId,
support: voteData.support,
weight: voteData.weight,
nonce: voteData.nonce
};
return await signer._signTypedData(
this.domain,
{ Vote: this.types.Vote },
value
);
}
}
身份认证系统:
class Web3AuthSystem {
constructor() {
this.activeSessions = new Map();
this.nonces = new Map();
this.verifier = new MessageSignatureVerifier();
}
// 生成认证随机数
generateAuthNonce(address) {
const nonce = Math.random().toString(36).substring(2, 15);
const expiry = Date.now() + 300000; // 5分钟有效期
this.nonces.set(address.toLowerCase(), {
nonce,
expiry,
used: false
});
return nonce;
}
// 创建认证消息
createAuthMessage(address, nonce, timestamp) {
return `请签名登录到我们的应用
地址: ${address}
随机数: ${nonce}
时间戳: ${timestamp}
此签名仅用于身份验证,不会触发任何区块链交易。`;
}
// 请求用户认证
async requestAuth(signer) {
const address = await signer.getAddress();
const nonce = this.generateAuthNonce(address);
const timestamp = Date.now();
const message = this.createAuthMessage(address, nonce, timestamp);
try {
const signature = await signer.signMessage(message);
return {
address,
message,
signature,
nonce,
timestamp
};
} catch (error) {
// 清理 nonce
this.nonces.delete(address.toLowerCase());
throw error;
}
}
// 验证认证签名
verifyAuth(authData) {
const { address, message, signature, nonce } = authData;
const addressKey = address.toLowerCase();
// 检查 nonce
const nonceData = this.nonces.get(addressKey);
if (!nonceData) {
return { isValid: false, reason: 'Invalid nonce' };
}
if (nonceData.used) {
return { isValid: false, reason: 'Nonce already used' };
}
if (Date.now() > nonceData.expiry) {
this.nonces.delete(addressKey);
return { isValid: false, reason: 'Nonce expired' };
}
if (nonceData.nonce !== nonce) {
return { isValid: false, reason: 'Nonce mismatch' };
}
// 验证签名
const signatureResult = this.verifier.verifyMessageSignature(
message,
signature,
address
);
if (signatureResult.isValid) {
// 标记 nonce 为已使用
nonceData.used = true;
// 创建会话
const sessionToken = this.createSession(address);
return {
isValid: true,
sessionToken,
address
};
}
return {
isValid: false,
reason: 'Invalid signature'
};
}
// 创建会话
createSession(address) {
const sessionToken = ethers.utils.id(`${address}_${Date.now()}_${Math.random()}`);
const expiry = Date.now() + 86400000; // 24小时
this.activeSessions.set(sessionToken, {
address: address.toLowerCase(),
expiry,
createdAt: Date.now()
});
return sessionToken;
}
// 验证会话
verifySession(sessionToken) {
const session = this.activeSessions.get(sessionToken);
if (!session) {
return { isValid: false, reason: 'Session not found' };
}
if (Date.now() > session.expiry) {
this.activeSessions.delete(sessionToken);
return { isValid: false, reason: 'Session expired' };
}
return {
isValid: true,
address: session.address,
createdAt: session.createdAt
};
}
// 撤销会话
revokeSession(sessionToken) {
return this.activeSessions.delete(sessionToken);
}
}
合约验证集成:
// 智能合约中的签名验证
const verificationContract = `
pragma solidity ^0.8.0;
contract SignatureVerifier {
mapping(address => uint256) public nonces;
// 验证消息签名
function verifyMessageSignature(
string memory message,
bytes memory signature,
address expectedSigner
) public pure returns (bool) {
bytes32 messageHash = keccak256(abi.encodePacked(
"\\x19Ethereum Signed Message:\\n",
Strings.toString(bytes(message).length),
message
));
address recoveredSigner = recoverSigner(messageHash, signature);
return recoveredSigner == expectedSigner;
}
// 验证 EIP-712 签名
function verifyTypedDataSignature(
bytes32 domainSeparator,
bytes32 structHash,
bytes memory signature,
address expectedSigner
) public pure returns (bool) {
bytes32 digest = keccak256(abi.encodePacked(
"\\x19\\x01",
domainSeparator,
structHash
));
address recoveredSigner = recoverSigner(digest, signature);
return recoveredSigner == expectedSigner;
}
// 恢复签名者地址
function recoverSigner(
bytes32 hash,
bytes memory signature
) internal pure returns (address) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 32))
s := mload(add(signature, 64))
v := byte(0, mload(add(signature, 96)))
}
return ecrecover(hash, v, r, s);
}
}
`;
// 前端与合约验证的集成
class ContractSignatureVerification {
constructor(contract, signer) {
this.contract = contract;
this.signer = signer;
}
// 在合约中验证签名
async verifyOnContract(message, signature, expectedSigner) {
try {
const isValid = await this.contract.verifyMessageSignature(
message,
signature,
expectedSigner
);
return { isValid };
} catch (error) {
console.error("合约签名验证失败:", error);
return { isValid: false, error: error.message };
}
}
// 提交经过签名验证的交易
async submitVerifiedTransaction(signedData) {
const { message, signature, action } = signedData;
const userAddress = await this.signer.getAddress();
// 先在前端验证
const frontendVerification = new MessageSignatureVerifier();
const verifyResult = frontendVerification.verifyMessageSignature(
message,
signature,
userAddress
);
if (!verifyResult.isValid) {
throw new Error("前端签名验证失败");
}
// 提交到合约
const tx = await this.contract.executeVerifiedAction(
message,
signature,
action
);
return tx;
}
}
实际应用示例:
// 完整的签名认证流程
async function implementSignatureAuth() {
// 1. 初始化系统
const authSystem = new Web3AuthSystem();
const signer = provider.getSigner();
// 2. 用户登录
try {
const authData = await authSystem.requestAuth(signer);
console.log("用户签名完成:", authData);
// 3. 验证签名
const authResult = authSystem.verifyAuth(authData);
if (authResult.isValid) {
console.log("认证成功,会话令牌:", authResult.sessionToken);
// 4. 使用会话进行后续操作
const sessionCheck = authSystem.verifySession(authResult.sessionToken);
console.log("会话验证:", sessionCheck);
} else {
console.error("认证失败:", authResult.reason);
}
} catch (error) {
console.error("签名认证过程失败:", error);
}
}
// EIP-712 Permit 示例
async function usePermitSignature() {
const eip712Handler = new EIP712SignatureHandler(
"MyToken", // 合约名称
"1", // 版本
1, // 链ID (Mainnet)
tokenAddress // 验证合约地址
);
const permitData = {
owner: userAddress,
spender: spenderAddress,
value: ethers.utils.parseEther("100"),
nonce: 0,
deadline: Math.floor(Date.now() / 1000) + 3600 // 1小时后过期
};
const signedPermit = await eip712Handler.createPermitSignature(
signer,
permitData
);
console.log("Permit 签名:", signedPermit);
// 验证签名
const verification = eip712Handler.verifyPermitSignature(
permitData,
signedPermit.signature
);
console.log("签名验证结果:", verification);
}
最佳实践:
安全注意事项:
How to handle contract address mapping across different networks?
How to handle contract address mapping across different networks?
考察点:多网络适配。
答案:
处理不同网络间的合约地址映射是构建多链 DApp 的核心技术。由于同一协议在不同网络上的合约地址通常不同,需要建立完善的地址管理系统来确保应用能够在各网络间正确切换和操作。
网络配置管理:
class NetworkConfigManager {
constructor() {
this.networks = new Map();
this.currentNetwork = null;
this.contractMappings = new Map();
// 初始化网络配置
this.initializeNetworks();
}
// 初始化支持的网络
initializeNetworks() {
const networkConfigs = {
1: {
name: 'Ethereum Mainnet',
symbol: 'ETH',
rpcUrls: [
'https://mainnet.infura.io/v3/YOUR-PROJECT-ID',
'https://eth-mainnet.alchemyapi.io/v2/YOUR-API-KEY'
],
blockExplorerUrls: ['https://etherscan.io'],
nativeCurrency: {
name: 'Ether',
symbol: 'ETH',
decimals: 18
}
},
5: {
name: 'Goerli Testnet',
symbol: 'ETH',
rpcUrls: ['https://goerli.infura.io/v3/YOUR-PROJECT-ID'],
blockExplorerUrls: ['https://goerli.etherscan.io'],
nativeCurrency: {
name: 'Goerli Ether',
symbol: 'ETH',
decimals: 18
}
},
137: {
name: 'Polygon Mainnet',
symbol: 'MATIC',
rpcUrls: ['https://polygon-rpc.com'],
blockExplorerUrls: ['https://polygonscan.com'],
nativeCurrency: {
name: 'MATIC',
symbol: 'MATIC',
decimals: 18
}
},
42161: {
name: 'Arbitrum One',
symbol: 'ETH',
rpcUrls: ['https://arb1.arbitrum.io/rpc'],
blockExplorerUrls: ['https://arbiscan.io'],
nativeCurrency: {
name: 'Ether',
symbol: 'ETH',
decimals: 18
}
},
56: {
name: 'Binance Smart Chain',
symbol: 'BNB',
rpcUrls: ['https://bsc-dataseed.binance.org'],
blockExplorerUrls: ['https://bscscan.com'],
nativeCurrency: {
name: 'BNB',
symbol: 'BNB',
decimals: 18
}
}
};
Object.entries(networkConfigs).forEach(([chainId, config]) => {
this.networks.set(parseInt(chainId), config);
});
}
// 获取网络配置
getNetworkConfig(chainId) {
return this.networks.get(chainId);
}
// 获取所有支持的网络
getSupportedNetworks() {
return Array.from(this.networks.entries()).map(([chainId, config]) => ({
chainId,
...config
}));
}
// 设置当前网络
setCurrentNetwork(chainId) {
if (this.networks.has(chainId)) {
this.currentNetwork = chainId;
return true;
}
return false;
}
// 检查网络是否支持
isNetworkSupported(chainId) {
return this.networks.has(chainId);
}
}
合约地址映射管理:
class ContractAddressManager {
constructor() {
this.contractMappings = new Map();
this.contractABIs = new Map();
// 初始化合约映射
this.initializeContractMappings();
}
// 初始化合约地址映射
initializeContractMappings() {
// 定义合约在不同网络上的地址
const contractConfigs = {
// USDC 代币
'USDC': {
1: '0xA0b86a33E6441e2b000E85C8b60E54E48e48B2BB', // Ethereum
137: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', // Polygon
42161: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', // Arbitrum
56: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d' // BSC
},
// Uniswap V3 Router
'UNISWAP_V3_ROUTER': {
1: '0xE592427A0AEce92De3Edee1F18E0157C05861564', // Ethereum
5: '0xE592427A0AEce92De3Edee1F18E0157C05861564', // Goerli
137: '0xE592427A0AEce92De3Edee1F18E0157C05861564', // Polygon
42161: '0xE592427A0AEce92De3Edee1F18E0157C05861564' // Arbitrum
},
// 自定义协议合约
'MY_PROTOCOL': {
1: '0x1234567890123456789012345678901234567890', // Ethereum
5: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef', // Goerli
137: '0x9876543210987654321098765432109876543210', // Polygon
42161: '0xfedcbafedcbafedcbafedcbafedcbafedcbafedcba' // Arbitrum
}
};
// 存储映射关系
Object.entries(contractConfigs).forEach(([contractName, networkMappings]) => {
this.contractMappings.set(contractName, new Map(
Object.entries(networkMappings).map(([chainId, address]) => [
parseInt(chainId),
address.toLowerCase()
])
));
});
}
// 获取合约地址
getContractAddress(contractName, chainId) {
const mapping = this.contractMappings.get(contractName);
if (!mapping) {
throw new Error(`未知的合约: ${contractName}`);
}
const address = mapping.get(chainId);
if (!address) {
throw new Error(`合约 ${contractName} 在网络 ${chainId} 上不存在`);
}
return address;
}
// 批量获取合约地址
getMultipleContractAddresses(contractNames, chainId) {
const result = {};
contractNames.forEach(contractName => {
try {
result[contractName] = this.getContractAddress(contractName, chainId);
} catch (error) {
result[contractName] = null;
console.warn(`获取合约地址失败: ${contractName} on ${chainId}`, error);
}
});
return result;
}
// 检查合约在网络上是否存在
isContractAvailableOnNetwork(contractName, chainId) {
try {
this.getContractAddress(contractName, chainId);
return true;
} catch {
return false;
}
}
// 获取合约支持的所有网络
getSupportedNetworks(contractName) {
const mapping = this.contractMappings.get(contractName);
if (!mapping) {
return [];
}
return Array.from(mapping.keys());
}
// 添加新的合约映射
addContractMapping(contractName, chainId, address) {
if (!this.contractMappings.has(contractName)) {
this.contractMappings.set(contractName, new Map());
}
const mapping = this.contractMappings.get(contractName);
mapping.set(chainId, address.toLowerCase());
console.log(`添加合约映射: ${contractName} -> ${address} (链ID: ${chainId})`);
}
// 移除合约映射
removeContractMapping(contractName, chainId) {
const mapping = this.contractMappings.get(contractName);
if (mapping) {
mapping.delete(chainId);
if (mapping.size === 0) {
this.contractMappings.delete(contractName);
}
}
}
}
多网络合约实例管理:
class MultiNetworkContractManager {
constructor(networkManager, addressManager) {
this.networkManager = networkManager;
this.addressManager = addressManager;
this.providers = new Map();
this.contracts = new Map();
this.currentChainId = null;
}
// 初始化提供商
async initializeProvider(chainId) {
const config = this.networkManager.getNetworkConfig(chainId);
if (!config) {
throw new Error(`不支持的网络: ${chainId}`);
}
// 尝试多个 RPC 端点
for (const rpcUrl of config.rpcUrls) {
try {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
await provider.getNetwork(); // 测试连接
this.providers.set(chainId, provider);
console.log(`网络 ${chainId} 提供商初始化成功`);
return provider;
} catch (error) {
console.warn(`RPC 端点 ${rpcUrl} 连接失败:`, error);
}
}
throw new Error(`无法连接到网络 ${chainId}`);
}
// 获取提供商
async getProvider(chainId) {
if (!this.providers.has(chainId)) {
await this.initializeProvider(chainId);
}
return this.providers.get(chainId);
}
// 获取合约实例
async getContract(contractName, chainId, signer = null) {
const contractKey = `${contractName}_${chainId}`;
// 检查缓存
if (this.contracts.has(contractKey)) {
const cachedContract = this.contracts.get(contractKey);
// 如果需要签名者但缓存的合约没有,则重新创建
if (signer && !cachedContract.signer) {
return this.createContract(contractName, chainId, signer);
}
return cachedContract;
}
return this.createContract(contractName, chainId, signer);
}
// 创建合约实例
async createContract(contractName, chainId, signer = null) {
try {
// 获取合约地址
const address = this.addressManager.getContractAddress(contractName, chainId);
// 获取 ABI
const abi = this.getContractABI(contractName);
// 获取提供商或签名者
const providerOrSigner = signer || await this.getProvider(chainId);
// 创建合约实例
const contract = new ethers.Contract(address, abi, providerOrSigner);
// 缓存合约实例
const contractKey = `${contractName}_${chainId}`;
this.contracts.set(contractKey, contract);
console.log(`合约 ${contractName} 在网络 ${chainId} 上初始化成功`);
return contract;
} catch (error) {
console.error(`创建合约实例失败: ${contractName} on ${chainId}`, error);
throw error;
}
}
// 获取合约 ABI
getContractABI(contractName) {
// 这里应该从存储的 ABI 映射中获取
// 为了示例,返回一个通用的 ERC20 ABI
if (contractName === 'USDC') {
return [
"function balanceOf(address owner) view returns (uint256)",
"function transfer(address to, uint256 amount) returns (bool)",
"function approve(address spender, uint256 amount) returns (bool)",
"function allowance(address owner, address spender) view returns (uint256)",
"function decimals() view returns (uint8)",
"function symbol() view returns (string)",
"function name() view returns (string)"
];
}
throw new Error(`未找到合约 ${contractName} 的 ABI`);
}
// 切换网络
async switchNetwork(newChainId) {
if (!this.networkManager.isNetworkSupported(newChainId)) {
throw new Error(`不支持的网络: ${newChainId}`);
}
// 清除当前网络的合约缓存
if (this.currentChainId) {
this.clearNetworkCache(this.currentChainId);
}
this.currentChainId = newChainId;
this.networkManager.setCurrentNetwork(newChainId);
console.log(`切换到网络: ${newChainId}`);
}
// 清除特定网络的缓存
clearNetworkCache(chainId) {
const keysToDelete = [];
for (const key of this.contracts.keys()) {
if (key.endsWith(`_${chainId}`)) {
keysToDelete.push(key);
}
}
keysToDelete.forEach(key => this.contracts.delete(key));
}
// 批量获取多网络合约
async getMultiNetworkContracts(contractName, chainIds) {
const contracts = {};
const promises = chainIds.map(async (chainId) => {
try {
const contract = await this.getContract(contractName, chainId);
return { chainId, contract };
} catch (error) {
console.warn(`网络 ${chainId} 上的合约 ${contractName} 不可用:`, error);
return { chainId, contract: null };
}
});
const results = await Promise.all(promises);
results.forEach(({ chainId, contract }) => {
contracts[chainId] = contract;
});
return contracts;
}
}
网络切换处理:
class NetworkSwitchHandler {
constructor(multiNetworkManager) {
this.multiNetworkManager = multiNetworkManager;
this.listeners = new Set();
this.isHandlingSwitchRequest = false;
}
// 监听网络切换
onNetworkChange(callback) {
this.listeners.add(callback);
return () => this.listeners.delete(callback);
}
// 请求用户切换网络
async requestNetworkSwitch(targetChainId) {
if (this.isHandlingSwitchRequest) {
throw new Error("网络切换请求正在进行中");
}
this.isHandlingSwitchRequest = true;
try {
const networkConfig = this.multiNetworkManager.networkManager.getNetworkConfig(targetChainId);
if (!networkConfig) {
throw new Error(`不支持的网络: ${targetChainId}`);
}
// 尝试切换到目标网络
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${targetChainId.toString(16)}` }]
});
console.log(`成功切换到网络: ${targetChainId}`);
} catch (switchError) {
// 如果网络不存在,尝试添加网络
if (switchError.code === 4902) {
await this.addNetwork(targetChainId, networkConfig);
// 添加后再次尝试切换
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${targetChainId.toString(16)}` }]
});
} else {
throw switchError;
}
}
// 更新内部状态
await this.multiNetworkManager.switchNetwork(targetChainId);
// 通知监听者
this.notifyListeners(targetChainId);
return true;
} finally {
this.isHandlingSwitchRequest = false;
}
}
// 添加网络到钱包
async addNetwork(chainId, networkConfig) {
const params = {
chainId: `0x${chainId.toString(16)}`,
chainName: networkConfig.name,
nativeCurrency: networkConfig.nativeCurrency,
rpcUrls: networkConfig.rpcUrls,
blockExplorerUrls: networkConfig.blockExplorerUrls
};
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [params]
});
console.log(`网络 ${networkConfig.name} 已添加到钱包`);
}
// 检测当前网络
async detectCurrentNetwork() {
if (!window.ethereum) {
throw new Error("未检测到钱包");
}
const chainId = await window.ethereum.request({
method: 'eth_chainId'
});
return parseInt(chainId, 16);
}
// 通知监听者网络变化
notifyListeners(newChainId) {
this.listeners.forEach(callback => {
try {
callback(newChainId);
} catch (error) {
console.error("网络变化监听器错误:", error);
}
});
}
}
使用示例:
// 初始化多网络管理系统
const networkManager = new NetworkConfigManager();
const addressManager = new ContractAddressManager();
const multiNetworkManager = new MultiNetworkContractManager(networkManager, addressManager);
const switchHandler = new NetworkSwitchHandler(multiNetworkManager);
// 使用示例
async function demonstrateMultiNetwork() {
try {
// 1. 检测当前网络
const currentChainId = await switchHandler.detectCurrentNetwork();
console.log("当前网络:", currentChainId);
// 2. 获取 USDC 合约(当前网络)
const usdcContract = await multiNetworkManager.getContract('USDC', currentChainId);
const balance = await usdcContract.balanceOf(userAddress);
console.log("USDC 余额:", ethers.utils.formatUnits(balance, 6));
// 3. 切换到 Polygon 网络
await switchHandler.requestNetworkSwitch(137);
// 4. 获取 Polygon 上的 USDC 合约
const polygonUsdcContract = await multiNetworkManager.getContract('USDC', 137);
const polygonBalance = await polygonUsdcContract.balanceOf(userAddress);
console.log("Polygon USDC 余额:", ethers.utils.formatUnits(polygonBalance, 6));
// 5. 批量获取多网络合约
const multiContracts = await multiNetworkManager.getMultiNetworkContracts(
'USDC',
[1, 137, 42161] // Ethereum, Polygon, Arbitrum
);
console.log("多网络合约:", multiContracts);
} catch (error) {
console.error("多网络操作失败:", error);
}
}
// 监听网络变化
switchHandler.onNetworkChange((newChainId) => {
console.log(`网络已切换至: ${newChainId}`);
// 更新 UI、重新加载数据等
});
最佳实践:
注意事项:
What is EIP-1559? How does it affect Gas fee calculation?
What is EIP-1559? How does it affect Gas fee calculation?
考察点:新Gas机制理解。
答案:
EIP-1559 是以太坊的一个重要改进提案,于 2021年8月随伦敦硬分叉实施。它彻底改变了以太坊的 Gas 费用机制,引入了基础费用(Base Fee)和优先费用(Priority Fee)的概念,替代了之前简单的 Gas Price 拍卖机制。
核心机制变化:
// EIP-1559 前的传统 Gas 机制
const legacyTransaction = {
gasPrice: ethers.utils.parseUnits('20', 'gwei'), // 固定 Gas 价格
gasLimit: 21000
};
// EIP-1559 新机制
const eip1559Transaction = {
maxFeePerGas: ethers.utils.parseUnits('25', 'gwei'), // 最大愿意支付的费用
maxPriorityFeePerGas: ethers.utils.parseUnits('2', 'gwei'), // 给矿工的小费
gasLimit: 21000,
type: 2 // 标识为 EIP-1559 交易
};
基础费用计算逻辑:
class BaseFeeCalculator {
constructor() {
this.targetGasUsed = 15_000_000; // 目标区块Gas使用量
this.maxGasUsed = 30_000_000; // 最大区块Gas使用量
this.baseFeeChangeDenominator = 8; // 变化分母(12.5%最大变化)
}
// 计算下一个区块的基础费用
calculateNextBaseFee(currentBaseFee, parentGasUsed, parentGasLimit) {
const parentGasTarget = parentGasLimit / 2;
if (parentGasUsed === parentGasTarget) {
// Gas 使用量等于目标,基础费用不变
return currentBaseFee;
} else if (parentGasUsed > parentGasTarget) {
// Gas 使用量超过目标,基础费用增加
const gasUsedDelta = parentGasUsed - parentGasTarget;
const targetGasUsed = parentGasTarget;
const baseFeePerGasDelta = Math.max(
currentBaseFee * gasUsedDelta / targetGasUsed / this.baseFeeChangeDenominator,
1
);
return currentBaseFee + baseFeePerGasDelta;
} else {
// Gas 使用量低于目标,基础费用减少
const gasUsedDelta = parentGasTarget - parentGasUsed;
const targetGasUsed = parentGasTarget;
const baseFeePerGasDelta = currentBaseFee * gasUsedDelta / targetGasUsed / this.baseFeeChangeDenominator;
return Math.max(currentBaseFee - baseFeePerGasDelta, 0);
}
}
// 模拟基础费用变化
simulateBaseFeeChanges(initialBaseFee, gasUsagePattern, blocks = 10) {
let currentBaseFee = initialBaseFee;
const changes = [currentBaseFee];
for (let i = 0; i < blocks; i++) {
const gasUsed = gasUsagePattern[i] || this.targetGasUsed;
currentBaseFee = this.calculateNextBaseFee(
currentBaseFee,
gasUsed,
this.maxGasUsed
);
changes.push(currentBaseFee);
}
return changes;
}
}
智能费用管理器:
class SmartFeeManager {
constructor(provider) {
this.provider = provider;
this.feeHistory = [];
this.predictionCache = new Map();
}
// 获取费用历史数据
async getFeeHistory(blockCount = 20, rewardPercentiles = [25, 50, 75]) {
try {
const feeHistory = await this.provider.send('eth_feeHistory', [
blockCount,
'latest',
rewardPercentiles
]);
return {
baseFeePerGas: feeHistory.baseFeePerGas.map(fee => ethers.BigNumber.from(fee)),
gasUsedRatio: feeHistory.gasUsedRatio,
reward: feeHistory.reward?.map(rewards =>
rewards.map(reward => ethers.BigNumber.from(reward))
),
oldestBlock: parseInt(feeHistory.oldestBlock, 16)
};
} catch (error) {
console.error('获取费用历史失败:', error);
return null;
}
}
// 预测优先费用
async predictPriorityFee(urgency = 'standard') {
const feeHistory = await this.getFeeHistory();
if (!feeHistory || !feeHistory.reward) {
// 回退到默认值
const defaults = {
slow: ethers.utils.parseUnits('1', 'gwei'),
standard: ethers.utils.parseUnits('2', 'gwei'),
fast: ethers.utils.parseUnits('3', 'gwei')
};
return defaults[urgency] || defaults.standard;
}
// 分析历史优先费用
const percentileMap = { slow: 0, standard: 1, fast: 2 };
const percentileIndex = percentileMap[urgency] || 1;
const recentRewards = feeHistory.reward.slice(-10); // 最近10个区块
let totalReward = ethers.BigNumber.from(0);
let count = 0;
recentRewards.forEach(rewards => {
if (rewards && rewards[percentileIndex]) {
totalReward = totalReward.add(rewards[percentileIndex]);
count++;
}
});
if (count === 0) {
return ethers.utils.parseUnits('2', 'gwei');
}
return totalReward.div(count);
}
// 估算最优 Gas 费用
async estimateOptimalFee(urgency = 'standard', blocksAhead = 3) {
try {
// 获取当前基础费用
const currentBlock = await this.provider.getBlock('pending');
const currentBaseFee = currentBlock.baseFeePerGas;
// 预测未来基础费用
const feeHistory = await this.getFeeHistory();
const predictedBaseFee = this.predictFutureBaseFee(
currentBaseFee,
feeHistory,
blocksAhead
);
// 计算推荐优先费用
const priorityFee = await this.predictPriorityFee(urgency);
// 计算最大费用(包含安全边际)
const safetyMultiplier = {
slow: 1.1,
standard: 1.25,
fast: 1.5
}[urgency] || 1.25;
const maxFeePerGas = predictedBaseFee.mul(Math.floor(safetyMultiplier * 100)).div(100).add(priorityFee);
return {
maxFeePerGas,
maxPriorityFeePerGas: priorityFee,
predictedBaseFee,
currentBaseFee,
estimatedGasPrice: predictedBaseFee.add(priorityFee)
};
} catch (error) {
console.error('费用估算失败:', error);
throw error;
}
}
// 预测未来基础费用
predictFutureBaseFee(currentBaseFee, feeHistory, blocksAhead) {
if (!feeHistory || !feeHistory.gasUsedRatio) {
return currentBaseFee;
}
// 计算最近的平均Gas使用率
const recentUsage = feeHistory.gasUsedRatio.slice(-5);
const avgUsageRatio = recentUsage.reduce((sum, ratio) => sum + ratio, 0) / recentUsage.length;
// 基于使用率趋势预测费用变化
let predictedFee = currentBaseFee;
for (let i = 0; i < blocksAhead; i++) {
if (avgUsageRatio > 0.5) {
// 网络拥堵,费用可能上升
const increaseRate = Math.min((avgUsageRatio - 0.5) * 0.25, 0.125); // 最多12.5%
predictedFee = predictedFee.mul(Math.floor((1 + increaseRate) * 10000)).div(10000);
} else {
// 网络空闲,费用可能下降
const decreaseRate = Math.min((0.5 - avgUsageRatio) * 0.25, 0.125);
predictedFee = predictedFee.mul(Math.floor((1 - decreaseRate) * 10000)).div(10000);
}
}
return predictedFee;
}
// 动态调整交易费用
async createDynamicTransaction(txParams, urgency = 'standard') {
const feeEstimate = await this.estimateOptimalFee(urgency);
const optimizedTx = {
...txParams,
maxFeePerGas: feeEstimate.maxFeePerGas,
maxPriorityFeePerGas: feeEstimate.maxPriorityFeePerGas,
type: 2 // EIP-1559 类型
};
// 记录费用信息用于分析
console.log('EIP-1559 交易费用:', {
maxFeePerGas: ethers.utils.formatUnits(feeEstimate.maxFeePerGas, 'gwei') + ' Gwei',
maxPriorityFeePerGas: ethers.utils.formatUnits(feeEstimate.maxPriorityFeePerGas, 'gwei') + ' Gwei',
predictedBaseFee: ethers.utils.formatUnits(feeEstimate.predictedBaseFee, 'gwei') + ' Gwei',
estimatedTotal: ethers.utils.formatUnits(feeEstimate.estimatedGasPrice, 'gwei') + ' Gwei'
});
return optimizedTx;
}
}
费用监控和分析:
class GasFeeAnalyzer {
constructor(provider) {
this.provider = provider;
this.transactionHistory = [];
this.feeEfficiencyMetrics = {
totalSaved: ethers.BigNumber.from(0),
averageEfficiency: 0,
transactionCount: 0
};
}
// 分析交易费用效率
async analyzeFeeEfficiency(txHash) {
const receipt = await this.provider.getTransactionReceipt(txHash);
const transaction = await this.provider.getTransaction(txHash);
if (!receipt || !transaction) {
throw new Error('交易不存在');
}
const actualGasUsed = receipt.gasUsed;
const effectiveGasPrice = receipt.effectiveGasPrice || transaction.gasPrice;
const maxFeePerGas = transaction.maxFeePerGas || transaction.gasPrice;
// 计算实际费用 vs 最大可能费用
const actualCost = actualGasUsed.mul(effectiveGasPrice);
const maxPossibleCost = actualGasUsed.mul(maxFeePerGas);
const savedAmount = maxPossibleCost.sub(actualCost);
// 计算效率百分比
const efficiency = actualCost.mul(10000).div(maxPossibleCost).toNumber() / 100;
const analysis = {
txHash,
actualCost: ethers.utils.formatEther(actualCost),
maxPossibleCost: ethers.utils.formatEther(maxPossibleCost),
savedAmount: ethers.utils.formatEther(savedAmount),
efficiency: efficiency.toFixed(2) + '%',
gasUsed: actualGasUsed.toString(),
effectiveGasPrice: ethers.utils.formatUnits(effectiveGasPrice, 'gwei') + ' Gwei',
timestamp: new Date()
};
// 更新统计数据
this.updateEfficiencyMetrics(savedAmount, efficiency);
this.transactionHistory.push(analysis);
console.log('交易费用分析:', analysis);
return analysis;
}
// 更新效率指标
updateEfficiencyMetrics(savedAmount, efficiency) {
this.feeEfficiencyMetrics.totalSaved = this.feeEfficiencyMetrics.totalSaved.add(savedAmount);
const currentAvg = this.feeEfficiencyMetrics.averageEfficiency;
const count = this.feeEfficiencyMetrics.transactionCount;
this.feeEfficiencyMetrics.averageEfficiency =
(currentAvg * count + efficiency) / (count + 1);
this.feeEfficiencyMetrics.transactionCount = count + 1;
}
// 生成费用报告
generateFeeReport() {
if (this.transactionHistory.length === 0) {
return { message: '暂无交易数据' };
}
const totalTransactions = this.transactionHistory.length;
const totalSaved = ethers.utils.formatEther(this.feeEfficiencyMetrics.totalSaved);
const avgEfficiency = this.feeEfficiencyMetrics.averageEfficiency.toFixed(2);
// 计算费用趋势
const recentTxs = this.transactionHistory.slice(-10);
const avgRecentEfficiency = recentTxs.reduce(
(sum, tx) => sum + parseFloat(tx.efficiency.replace('%', '')), 0
) / recentTxs.length;
return {
summary: {
totalTransactions,
totalSaved: totalSaved + ' ETH',
averageEfficiency: avgEfficiency + '%',
recentEfficiency: avgRecentEfficiency.toFixed(2) + '%'
},
recommendations: this.generateOptimizationRecommendations(avgRecentEfficiency),
recentTransactions: recentTxs.map(tx => ({
hash: tx.txHash.slice(0, 10) + '...',
efficiency: tx.efficiency,
saved: tx.savedAmount,
timestamp: tx.timestamp.toLocaleString()
}))
};
}
// 生成优化建议
generateOptimizationRecommendations(recentEfficiency) {
const recommendations = [];
if (recentEfficiency < 70) {
recommendations.push('考虑使用更保守的 maxFeePerGas 设置');
recommendations.push('监控网络拥堵情况,选择合适的交易时机');
}
if (recentEfficiency > 95) {
recommendations.push('可以适当提高 maxPriorityFeePerGas 以加快确认速度');
}
recommendations.push('定期分析费用历史数据,调整费用策略');
recommendations.push('考虑使用费用预测API优化交易时机');
return recommendations;
}
}
实际应用示例:
// EIP-1559 完整使用流程
async function demonstrateEIP1559Usage() {
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
const feeManager = new SmartFeeManager(provider);
const feeAnalyzer = new GasFeeAnalyzer(provider);
try {
// 1. 准备交易参数
const baseTransaction = {
to: '0x742d35Cc6155A0532DF17E1E2B6b46e010e13E57',
value: ethers.utils.parseEther('0.1'),
gasLimit: 21000
};
// 2. 创建优化的 EIP-1559 交易
const optimizedTransaction = await feeManager.createDynamicTransaction(
baseTransaction,
'standard'
);
console.log('准备发送交易:', optimizedTransaction);
// 3. 发送交易
const tx = await wallet.sendTransaction(optimizedTransaction);
console.log('交易已发送:', tx.hash);
// 4. 等待确认
const receipt = await tx.wait();
console.log('交易已确认:', receipt.transactionHash);
// 5. 分析费用效率
const analysis = await feeAnalyzer.analyzeFeeEfficiency(receipt.transactionHash);
// 6. 生成费用报告
const report = feeAnalyzer.generateFeeReport();
console.log('费用效率报告:', report);
} catch (error) {
console.error('EIP-1559 交易失败:', error);
}
}
// 监控费用变化
async function monitorFeeChanges() {
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const feeManager = new SmartFeeManager(provider);
setInterval(async () => {
try {
const feeEstimate = await feeManager.estimateOptimalFee('standard');
console.log('当前费用状况:', {
baseFee: ethers.utils.formatUnits(feeEstimate.currentBaseFee, 'gwei') + ' Gwei',
priorityFee: ethers.utils.formatUnits(feeEstimate.maxPriorityFeePerGas, 'gwei') + ' Gwei',
totalEstimate: ethers.utils.formatUnits(feeEstimate.estimatedGasPrice, 'gwei') + ' Gwei'
});
} catch (error) {
console.error('费用监控失败:', error);
}
}, 30000); // 每30秒检查一次
}
EIP-1559 的关键优势:
最佳实践建议:
maxFeePerGas(通常为预测基础费用的1.5-2倍)maxPriorityFeePerGasHow to implement atomic operations for contract calls?
How to implement atomic operations for contract calls?
考察点:原子性操作设计。
答案:
合约调用的原子性操作是指一系列操作要么全部成功,要么全部失败回滚的机制。这在 DeFi 协议中尤其重要,确保复杂的金融操作不会因部分失败而导致不一致的状态。实现原子性主要通过智能合约内部逻辑、Multicall 模式和事务性操作设计。
智能合约层面的原子性:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract AtomicSwapContract is ReentrancyGuard {
struct SwapParams {
address tokenA;
address tokenB;
uint256 amountA;
uint256 minAmountB;
address recipient;
uint256 deadline;
}
// 原子性代币交换
function atomicSwap(SwapParams calldata params)
external
nonReentrant
{
require(block.timestamp <= params.deadline, "交易已过期");
require(params.amountA > 0, "数量必须大于0");
require(params.minAmountB > 0, "最小接收数量必须大于0");
// 1. 转入代币A
IERC20(params.tokenA).transferFrom(
msg.sender,
address(this),
params.amountA
);
// 2. 执行交换逻辑(这里简化为1:2的固定比例)
uint256 amountB = params.amountA * 2;
require(amountB >= params.minAmountB, "滑点过大");
// 3. 检查合约是否有足够的代币B
require(
IERC20(params.tokenB).balanceOf(address(this)) >= amountB,
"流动性不足"
);
// 4. 转出代币B
IERC20(params.tokenB).transfer(params.recipient, amountB);
emit AtomicSwapCompleted(
msg.sender,
params.tokenA,
params.tokenB,
params.amountA,
amountB
);
// 注意:如果任何步骤失败,整个交易都会回滚
}
// 原子性多步骤操作
function atomicMultiStep(
address[] calldata tokens,
uint256[] calldata amounts,
bytes[] calldata data
) external nonReentrant {
require(
tokens.length == amounts.length &&
amounts.length == data.length,
"参数长度不匹配"
);
// 记录初始状态
mapping(address => uint256) storage initialBalances;
// 执行所有步骤
for (uint256 i = 0; i < tokens.length; i++) {
_executeStep(tokens[i], amounts[i], data[i]);
}
// 验证最终状态
_validateFinalState();
emit MultiStepCompleted(tokens, amounts);
}
function _executeStep(
address token,
uint256 amount,
bytes calldata data
) internal {
// 执行具体的步骤逻辑
// 任何失败都会导致整个交易回滚
}
function _validateFinalState() internal view {
// 验证最终状态的合理性
// 例如:检查余额变化是否符合预期
}
event AtomicSwapCompleted(
address indexed user,
address indexed tokenA,
address indexed tokenB,
uint256 amountA,
uint256 amountB
);
event MultiStepCompleted(
address[] tokens,
uint256[] amounts
);
}
Multicall 原子性模式:
class AtomicMulticall {
constructor(provider, multicallAddress) {
this.provider = provider;
this.multicallAddress = multicallAddress;
// Multicall3 ABI
this.multicallABI = [
"function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
];
this.multicallContract = new ethers.Contract(
multicallAddress,
this.multicallABI,
provider
);
}
// 构建原子性调用
async buildAtomicCalls(operations) {
const calls = [];
for (const operation of operations) {
const { contract, method, params, allowFailure = false } = operation;
// 编码函数调用
const callData = contract.interface.encodeFunctionData(method, params);
calls.push({
target: contract.address,
allowFailure,
callData
});
}
return calls;
}
// 执行原子性调用
async executeAtomicCalls(operations, signer, options = {}) {
try {
// 构建调用数据
const calls = await this.buildAtomicCalls(operations);
console.log(`准备执行 ${calls.length} 个原子性操作`);
// 估算 Gas
const gasEstimate = await this.multicallContract.connect(signer)
.estimateGas.aggregate3(calls, options);
// 添加 Gas 缓冲
const gasLimit = gasEstimate.mul(120).div(100);
// 执行交易
const tx = await this.multicallContract.connect(signer)
.aggregate3(calls, { ...options, gasLimit });
console.log(`原子性交易已提交: ${tx.hash}`);
// 等待确认
const receipt = await tx.wait();
// 解析结果
const results = await this.parseMulticallResults(receipt, operations);
console.log('原子性操作完成:', results);
return results;
} catch (error) {
console.error('原子性操作失败:', error);
throw error;
}
}
// 解析 Multicall 结果
async parseMulticallResults(receipt, operations) {
const results = [];
// 获取交易日志
const logs = receipt.logs;
operations.forEach((operation, index) => {
const { contract, method, expectedEvents = [] } = operation;
// 查找相关事件
const relatedLogs = logs.filter(log =>
log.address.toLowerCase() === contract.address.toLowerCase()
);
const parsedEvents = relatedLogs.map(log => {
try {
return contract.interface.parseLog(log);
} catch (error) {
return null;
}
}).filter(event => event !== null);
results.push({
operationIndex: index,
method,
success: true,
events: parsedEvents,
gasUsed: receipt.gasUsed.div(operations.length) // 平均分配
});
});
return results;
}
// 构建条件原子性操作
async buildConditionalAtomicCalls(conditions, operations) {
const calls = [];
for (let i = 0; i < operations.length; i++) {
const operation = operations[i];
const condition = conditions[i];
// 如果有条件,先添加条件检查
if (condition) {
const conditionCall = await this.buildConditionCheck(condition);
calls.push(conditionCall);
}
// 添加实际操作
const operationCall = await this.buildOperationCall(operation);
calls.push(operationCall);
}
return calls;
}
async buildConditionCheck(condition) {
const { contract, method, params, expectedResult } = condition;
// 构建条件检查调用
return {
target: contract.address,
allowFailure: false, // 条件检查不允许失败
callData: contract.interface.encodeFunctionData(method, params)
};
}
async buildOperationCall(operation) {
const { contract, method, params } = operation;
return {
target: contract.address,
allowFailure: false,
callData: contract.interface.encodeFunctionData(method, params)
};
}
}
原子性 DEX 交易实现:
class AtomicDEXTrader {
constructor(provider, signer, contracts) {
this.provider = provider;
this.signer = signer;
this.contracts = contracts; // { router, factory, weth, tokens }
this.multicall = new AtomicMulticall(provider, contracts.multicall);
}
// 原子性代币交换
async atomicSwap(swapParams) {
const {
tokenA,
tokenB,
amountIn,
minAmountOut,
deadline,
slippageTolerance = 0.005 // 0.5%
} = swapParams;
try {
// 1. 计算交换路径
const path = await this.calculateOptimalPath(tokenA, tokenB);
// 2. 获取预期输出
const expectedOutput = await this.getAmountsOut(amountIn, path);
const minAmountOutWithSlippage = expectedOutput[expectedOutput.length - 1]
.mul(Math.floor((1 - slippageTolerance) * 10000))
.div(10000);
// 确保满足最小输出要求
if (minAmountOutWithSlippage.lt(minAmountOut)) {
throw new Error('滑点过大,交易取消');
}
// 3. 构建原子性操作序列
const operations = [
// 授权代币
{
contract: new ethers.Contract(tokenA, ERC20_ABI, this.signer),
method: 'approve',
params: [this.contracts.router, amountIn],
expectedEvents: ['Approval']
},
// 执行交换
{
contract: this.contracts.router,
method: 'swapExactTokensForTokens',
params: [
amountIn,
minAmountOutWithSlippage,
path,
await this.signer.getAddress(),
deadline
],
expectedEvents: ['Swap']
}
];
// 4. 执行原子性交换
const result = await this.multicall.executeAtomicCalls(
operations,
this.signer
);
return {
success: true,
transactionHash: result.transactionHash,
amountIn,
amountOut: this.extractAmountOut(result),
gasUsed: result.gasUsed,
path
};
} catch (error) {
console.error('原子性交换失败:', error);
throw error;
}
}
// 原子性流动性操作
async atomicLiquidityOperation(liquidityParams) {
const {
operation, // 'add' | 'remove'
tokenA,
tokenB,
amountA,
amountB,
minAmountA,
minAmountB,
deadline
} = liquidityParams;
const operations = [];
if (operation === 'add') {
// 添加流动性的原子性操作
operations.push(
// 授权代币A
{
contract: new ethers.Contract(tokenA, ERC20_ABI, this.signer),
method: 'approve',
params: [this.contracts.router, amountA]
},
// 授权代币B
{
contract: new ethers.Contract(tokenB, ERC20_ABI, this.signer),
method: 'approve',
params: [this.contracts.router, amountB]
},
// 添加流动性
{
contract: this.contracts.router,
method: 'addLiquidity',
params: [
tokenA,
tokenB,
amountA,
amountB,
minAmountA,
minAmountB,
await this.signer.getAddress(),
deadline
]
}
);
} else if (operation === 'remove') {
// 移除流动性的原子性操作
const pairAddress = await this.contracts.factory.getPair(tokenA, tokenB);
const pairContract = new ethers.Contract(pairAddress, PAIR_ABI, this.signer);
operations.push(
// 授权 LP 代币
{
contract: pairContract,
method: 'approve',
params: [this.contracts.router, amountA] // amountA 这里是 LP 代币数量
},
// 移除流动性
{
contract: this.contracts.router,
method: 'removeLiquidity',
params: [
tokenA,
tokenB,
amountA,
minAmountA,
minAmountB,
await this.signer.getAddress(),
deadline
]
}
);
}
return await this.multicall.executeAtomicCalls(operations, this.signer);
}
// 原子性套利操作
async atomicArbitrage(arbitrageParams) {
const {
tokenA,
tokenB,
amountIn,
exchanges, // [exchange1, exchange2]
minProfit
} = arbitrageParams;
// 计算预期利润
const profit = await this.calculateArbitrageProfit(
tokenA,
tokenB,
amountIn,
exchanges
);
if (profit.lt(minProfit)) {
throw new Error('套利机会不足');
}
// 构建原子性套利操作
const operations = [
// 在交易所1买入
{
contract: exchanges[0].router,
method: 'swapExactTokensForTokens',
params: [
amountIn,
0, // 最小输出在后面计算
[tokenA, tokenB],
await this.signer.getAddress(),
Math.floor(Date.now() / 1000) + 300
]
},
// 在交易所2卖出
{
contract: exchanges[1].router,
method: 'swapExactTokensForTokens',
params: [
0, // 将使用第一步的输出
amountIn.add(minProfit), // 确保有利润
[tokenB, tokenA],
await this.signer.getAddress(),
Math.floor(Date.now() / 1000) + 300
]
}
];
return await this.multicall.executeAtomicCalls(operations, this.signer);
}
// 计算最优路径
async calculateOptimalPath(tokenA, tokenB) {
// 直接路径
const directPath = [tokenA, tokenB];
// 通过 WETH 的路径
const wethPath = [tokenA, this.contracts.weth, tokenB];
// 比较两个路径的输出
try {
const directOutput = await this.contracts.router.getAmountsOut(
ethers.utils.parseEther('1'),
directPath
);
const wethOutput = await this.contracts.router.getAmountsOut(
ethers.utils.parseEther('1'),
wethPath
);
return wethOutput[wethOutput.length - 1].gt(directOutput[directOutput.length - 1])
? wethPath : directPath;
} catch (error) {
return directPath;
}
}
async getAmountsOut(amountIn, path) {
return await this.contracts.router.getAmountsOut(amountIn, path);
}
extractAmountOut(result) {
// 从交易结果中提取实际输出数量
// 这需要根据具体的事件日志来解析
return ethers.BigNumber.from(0);
}
async calculateArbitrageProfit(tokenA, tokenB, amountIn, exchanges) {
// 计算套利利润的逻辑
return ethers.BigNumber.from(0);
}
}
原子性状态管理:
class AtomicStateManager {
constructor() {
this.stateSnapshots = new Map();
this.operationQueue = [];
this.rollbackHandlers = [];
}
// 创建状态快照
async createSnapshot(key, stateReader) {
const snapshot = await stateReader();
this.stateSnapshots.set(key, {
timestamp: Date.now(),
state: snapshot
});
console.log(`状态快照已创建: ${key}`);
return snapshot;
}
// 原子性操作包装器
async executeAtomicOperation(operation, rollbackHandler) {
const operationId = Math.random().toString(36).substr(2, 9);
try {
// 添加到操作队列
this.operationQueue.push({
id: operationId,
operation,
rollbackHandler,
startTime: Date.now()
});
// 执行操作
const result = await operation();
// 操作成功,从队列移除
this.operationQueue = this.operationQueue.filter(op => op.id !== operationId);
console.log(`原子性操作完成: ${operationId}`);
return result;
} catch (error) {
console.error(`原子性操作失败: ${operationId}`, error);
// 执行回滚
if (rollbackHandler) {
try {
await rollbackHandler();
console.log(`回滚成功: ${operationId}`);
} catch (rollbackError) {
console.error(`回滚失败: ${operationId}`, rollbackError);
}
}
// 从队列移除
this.operationQueue = this.operationQueue.filter(op => op.id !== operationId);
throw error;
}
}
// 批量原子性操作
async executeBatchAtomicOperations(operations) {
const batchId = Math.random().toString(36).substr(2, 9);
const completedOperations = [];
try {
console.log(`开始批量原子性操作: ${batchId}`);
for (let i = 0; i < operations.length; i++) {
const { operation, rollbackHandler } = operations[i];
const result = await operation();
completedOperations.push({ result, rollbackHandler });
console.log(`批量操作 ${i + 1}/${operations.length} 完成`);
}
console.log(`批量原子性操作完成: ${batchId}`);
return completedOperations.map(op => op.result);
} catch (error) {
console.error(`批量原子性操作失败: ${batchId}`, error);
// 逆序回滚已完成的操作
console.log('开始回滚已完成的操作...');
for (let i = completedOperations.length - 1; i >= 0; i--) {
const { rollbackHandler } = completedOperations[i];
if (rollbackHandler) {
try {
await rollbackHandler();
console.log(`回滚操作 ${i + 1} 成功`);
} catch (rollbackError) {
console.error(`回滚操作 ${i + 1} 失败:`, rollbackError);
}
}
}
throw error;
}
}
// 清理过期快照
cleanupExpiredSnapshots(maxAge = 3600000) { // 1小时
const now = Date.now();
const expiredKeys = [];
for (const [key, snapshot] of this.stateSnapshots) {
if (now - snapshot.timestamp > maxAge) {
expiredKeys.push(key);
}
}
expiredKeys.forEach(key => this.stateSnapshots.delete(key));
if (expiredKeys.length > 0) {
console.log(`清理了 ${expiredKeys.length} 个过期快照`);
}
}
}
使用示例:
// 使用原子性操作进行复杂的 DeFi 交易
async function demonstrateAtomicOperations() {
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
const contracts = {
router: new ethers.Contract(ROUTER_ADDRESS, ROUTER_ABI, signer),
factory: new ethers.Contract(FACTORY_ADDRESS, FACTORY_ABI, provider),
multicall: MULTICALL_ADDRESS,
weth: WETH_ADDRESS
};
const atomicTrader = new AtomicDEXTrader(provider, signer, contracts);
const stateManager = new AtomicStateManager();
try {
// 1. 原子性代币交换
const swapResult = await atomicTrader.atomicSwap({
tokenA: TOKEN_A_ADDRESS,
tokenB: TOKEN_B_ADDRESS,
amountIn: ethers.utils.parseEther('1'),
minAmountOut: ethers.utils.parseEther('0.95'),
deadline: Math.floor(Date.now() / 1000) + 300,
slippageTolerance: 0.01
});
console.log('原子性交换完成:', swapResult);
// 2. 原子性流动性操作
const liquidityResult = await atomicTrader.atomicLiquidityOperation({
operation: 'add',
tokenA: TOKEN_A_ADDRESS,
tokenB: TOKEN_B_ADDRESS,
amountA: ethers.utils.parseEther('1'),
amountB: ethers.utils.parseEther('2'),
minAmountA: ethers.utils.parseEther('0.95'),
minAmountB: ethers.utils.parseEther('1.9'),
deadline: Math.floor(Date.now() / 1000) + 300
});
console.log('原子性流动性操作完成:', liquidityResult);
} catch (error) {
console.error('原子性操作演示失败:', error);
}
}
原子性操作的关键要点:
最佳实践:
What is the Factory contract pattern? How to interact with factory contracts?
What is the Factory contract pattern? How to interact with factory contracts?
考察点:合约设计模式。
答案:
Factory(工厂)合约模式是一种创建型设计模式,用于标准化和自动化其他合约的部署过程。工厂合约作为合约部署的中央管理器,可以创建具有相似功能但不同参数的合约实例,广泛应用于 DEX、代币发行、NFT 集合等场景。
基础工厂合约实现:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
contract TokenFactory is Ownable {
// 部署记录
mapping(address => bool) public isDeployedByFactory;
mapping(bytes32 => address) public deployedContracts;
address[] public allDeployedContracts;
// 统计信息
uint256 public totalDeployed;
mapping(address => uint256) public deployerCount;
uint256 public deploymentFee = 0.01 ether;
bool public feeRequired = false;
event ContractDeployed(
address indexed deployer,
address indexed contractAddress,
string name,
string symbol,
uint256 totalSupply,
bytes32 salt
);
// 部署代币合约
function deployToken(
string memory name,
string memory symbol,
uint256 totalSupply,
bytes32 salt
) external payable returns (address) {
if (feeRequired) {
require(msg.value >= deploymentFee, "部署费用不足");
}
// 检查重复部署
bytes32 contractId = keccak256(
abi.encodePacked(name, symbol, totalSupply, msg.sender)
);
require(deployedContracts[contractId] == address(0), "合约已存在");
// 准备字节码
bytes memory bytecode = abi.encodePacked(
type(StandardERC20).creationCode,
abi.encode(name, symbol, totalSupply, msg.sender)
);
// 使用 CREATE2 部署
address tokenAddress = Create2.deploy(0, salt, bytecode);
// 记录部署信息
isDeployedByFactory[tokenAddress] = true;
deployedContracts[contractId] = tokenAddress;
allDeployedContracts.push(tokenAddress);
totalDeployed++;
deployerCount[msg.sender]++;
emit ContractDeployed(
msg.sender, tokenAddress, name, symbol, totalSupply, salt
);
return tokenAddress;
}
// 预计算地址
function computeAddress(
string memory name,
string memory symbol,
uint256 totalSupply,
address deployer,
bytes32 salt
) external view returns (address) {
bytes memory bytecode = abi.encodePacked(
type(StandardERC20).creationCode,
abi.encode(name, symbol, totalSupply, deployer)
);
return Create2.computeAddress(salt, keccak256(bytecode), address(this));
}
// 批量部署
function batchDeployTokens(
TokenParams[] memory tokens,
bytes32[] memory salts
) external payable returns (address[] memory) {
require(tokens.length == salts.length, "参数长度不匹配");
require(tokens.length <= 10, "批量限制10个");
uint256 totalFee = feeRequired ? deploymentFee * tokens.length : 0;
require(msg.value >= totalFee, "批量费用不足");
address[] memory addresses = new address[](tokens.length);
for (uint256 i = 0; i < tokens.length; i++) {
addresses[i] = _deployTokenInternal(
tokens[i].name,
tokens[i].symbol,
tokens[i].totalSupply,
salts[i]
);
}
return addresses;
}
struct TokenParams {
string name;
string symbol;
uint256 totalSupply;
}
}
工厂合约交互管理器:
class FactoryManager {
constructor(provider, factoryAddress, factoryABI) {
this.provider = provider;
this.factoryAddress = factoryAddress;
this.factoryContract = new ethers.Contract(
factoryAddress,
factoryABI,
provider
);
this.deployedContracts = new Map();
this.eventCache = new Map();
}
// 部署单个代币
async deployToken(params, signer) {
const { name, symbol, totalSupply, salt } = params;
try {
// 检查部署费用
const [feeRequired, deploymentFee] = await Promise.all([
this.factoryContract.feeRequired(),
this.factoryContract.deploymentFee()
]);
const value = feeRequired ? deploymentFee : 0;
// 预计算地址
const saltBytes = salt || ethers.utils.id(`${name}-${symbol}-${Date.now()}`);
const predictedAddress = await this.factoryContract.computeAddress(
name,
symbol,
ethers.utils.parseEther(totalSupply.toString()),
await signer.getAddress(),
saltBytes
);
console.log(`预计合约地址: ${predictedAddress}`);
// 估算 Gas
const gasEstimate = await this.factoryContract.connect(signer)
.estimateGas.deployToken(
name,
symbol,
ethers.utils.parseEther(totalSupply.toString()),
saltBytes,
{ value }
);
// 执行部署
const tx = await this.factoryContract.connect(signer).deployToken(
name,
symbol,
ethers.utils.parseEther(totalSupply.toString()),
saltBytes,
{
value,
gasLimit: gasEstimate.mul(120).div(100)
}
);
console.log(`部署交易: ${tx.hash}`);
// 等待确认
const receipt = await tx.wait();
const deployedAddress = this.extractContractAddress(receipt);
// 缓存合约信息
this.cacheContractInfo(deployedAddress, {
name, symbol, totalSupply,
deployer: await signer.getAddress(),
txHash: tx.hash,
blockNumber: receipt.blockNumber
});
return {
success: true,
contractAddress: deployedAddress,
transactionHash: tx.hash,
gasUsed: receipt.gasUsed,
predictedCorrect: deployedAddress.toLowerCase() === predictedAddress.toLowerCase()
};
} catch (error) {
console.error('代币部署失败:', error);
throw error;
}
}
// 批量部署代币
async batchDeployTokens(tokensParams, signer) {
try {
const tokens = tokensParams.map(token => ({
name: token.name,
symbol: token.symbol,
totalSupply: ethers.utils.parseEther(token.totalSupply.toString())
}));
const salts = tokensParams.map((token, index) =>
token.salt || ethers.utils.id(`${token.name}-batch-${Date.now()}-${index}`)
);
// 计算总费用
const [feeRequired, deploymentFee] = await Promise.all([
this.factoryContract.feeRequired(),
this.factoryContract.deploymentFee()
]);
const totalValue = feeRequired ? deploymentFee.mul(tokens.length) : 0;
console.log(`批量部署 ${tokens.length} 个代币,总费用: ${ethers.utils.formatEther(totalValue)} ETH`);
// 执行批量部署
const tx = await this.factoryContract.connect(signer)
.batchDeployTokens(tokens, salts, { value: totalValue });
console.log(`批量部署交易: ${tx.hash}`);
const receipt = await tx.wait();
const addresses = this.extractBatchAddresses(receipt);
// 缓存所有合约信息
addresses.forEach((address, index) => {
this.cacheContractInfo(address, {
...tokensParams[index],
deployer: signer.address,
txHash: tx.hash,
blockNumber: receipt.blockNumber,
batchIndex: index
});
});
return {
success: true,
contractAddresses: addresses,
transactionHash: tx.hash,
gasUsed: receipt.gasUsed
};
} catch (error) {
console.error('批量部署失败:', error);
throw error;
}
}
// 获取用户部署的所有合约
async getUserContracts(userAddress) {
try {
const contractCount = await this.factoryContract.deployerCount(userAddress);
if (contractCount.eq(0)) {
return [];
}
// 获取工厂部署的所有合约
const totalContracts = await this.factoryContract.totalDeployed();
const userContracts = [];
for (let i = 0; i < totalContracts.toNumber(); i++) {
const contractAddress = await this.factoryContract.allDeployedContracts(i);
// 检查是否为指定用户部署
if (await this.isContractOwnedBy(contractAddress, userAddress)) {
const contractInfo = await this.getContractDetails(contractAddress);
userContracts.push(contractInfo);
}
}
return userContracts;
} catch (error) {
console.error('获取用户合约失败:', error);
throw error;
}
}
// 获取合约详细信息
async getContractDetails(contractAddress) {
try {
// 检查缓存
const cached = this.deployedContracts.get(contractAddress);
if (cached) {
return cached;
}
// 创建合约实例获取信息
const contract = new ethers.Contract(
contractAddress,
[
'function name() view returns (string)',
'function symbol() view returns (string)',
'function totalSupply() view returns (uint256)',
'function owner() view returns (address)',
'function balanceOf(address) view returns (uint256)'
],
this.provider
);
const [name, symbol, totalSupply, owner] = await Promise.all([
contract.name(),
contract.symbol(),
contract.totalSupply(),
contract.owner()
]);
const info = {
address: contractAddress,
name,
symbol,
totalSupply: ethers.utils.formatEther(totalSupply),
owner,
isVerified: await this.factoryContract.isDeployedByFactory(contractAddress)
};
// 缓存信息
this.deployedContracts.set(contractAddress, info);
return info;
} catch (error) {
console.error('获取合约详情失败:', error);
throw error;
}
}
// 监听工厂事件
async startEventMonitoring() {
const filter = this.factoryContract.filters.ContractDeployed();
// 监听新部署事件
this.factoryContract.on(filter, (deployer, contractAddress, name, symbol, totalSupply, salt, event) => {
const deploymentInfo = {
deployer,
contractAddress,
name,
symbol,
totalSupply: ethers.utils.formatEther(totalSupply),
salt,
txHash: event.transactionHash,
blockNumber: event.blockNumber,
timestamp: new Date()
};
console.log('🚀 新合约部署:', deploymentInfo);
// 缓存事件信息
this.eventCache.set(event.transactionHash, deploymentInfo);
this.cacheContractInfo(contractAddress, deploymentInfo);
});
console.log('开始监听工厂合约事件...');
}
// 停止事件监听
stopEventMonitoring() {
this.factoryContract.removeAllListeners();
console.log('已停止事件监听');
}
// 获取工厂统计信息
async getFactoryStats() {
try {
const [
totalDeployed,
deploymentFee,
feeRequired,
owner
] = await Promise.all([
this.factoryContract.totalDeployed(),
this.factoryContract.deploymentFee(),
this.factoryContract.feeRequired(),
this.factoryContract.owner()
]);
return {
totalDeployed: totalDeployed.toNumber(),
deploymentFee: ethers.utils.formatEther(deploymentFee) + ' ETH',
feeRequired,
factoryOwner: owner,
factoryAddress: this.factoryAddress
};
} catch (error) {
console.error('获取工厂统计失败:', error);
throw error;
}
}
// 辅助方法
extractContractAddress(receipt) {
const deployEvent = receipt.logs.find(log => {
try {
const parsed = this.factoryContract.interface.parseLog(log);
return parsed.name === 'ContractDeployed';
} catch {
return false;
}
});
if (deployEvent) {
const parsed = this.factoryContract.interface.parseLog(deployEvent);
return parsed.args.contractAddress;
}
throw new Error('未找到部署事件');
}
extractBatchAddresses(receipt) {
const addresses = [];
receipt.logs.forEach(log => {
try {
const parsed = this.factoryContract.interface.parseLog(log);
if (parsed.name === 'ContractDeployed') {
addresses.push(parsed.args.contractAddress);
}
} catch {
// 忽略解析失败的日志
}
});
return addresses;
}
cacheContractInfo(address, info) {
this.deployedContracts.set(address, {
...info,
cachedAt: new Date()
});
}
async isContractOwnedBy(contractAddress, userAddress) {
try {
const contract = new ethers.Contract(
contractAddress,
['function owner() view returns (address)'],
this.provider
);
const owner = await contract.owner();
return owner.toLowerCase() === userAddress.toLowerCase();
} catch {
return false;
}
}
}
高级工厂模式应用:
// NFT 集合工厂
class NFTCollectionFactory {
constructor(provider, factoryAddress) {
this.provider = provider;
this.factoryContract = new ethers.Contract(
factoryAddress,
NFT_FACTORY_ABI,
provider
);
this.collections = new Map();
}
// 部署 NFT 集合
async deployNFTCollection(params, signer) {
const {
name, symbol, baseURI, maxSupply,
mintPrice, royaltyReceiver, royaltyFee
} = params;
try {
const tx = await this.factoryContract.connect(signer)
.deployCollection(
name, symbol, baseURI, maxSupply,
ethers.utils.parseEther(mintPrice.toString()),
royaltyReceiver, royaltyFee * 100
);
const receipt = await tx.wait();
const collectionAddress = this.extractCollectionAddress(receipt);
// 缓存集合信息
this.collections.set(collectionAddress, {
name, symbol, baseURI, maxSupply,
mintPrice, royaltyReceiver, royaltyFee,
creator: await signer.getAddress(),
deployedAt: new Date()
});
return {
success: true,
collectionAddress,
transactionHash: tx.hash
};
} catch (error) {
console.error('NFT 集合部署失败:', error);
throw error;
}
}
// 获取集合信息
async getCollectionInfo(collectionAddress) {
const cached = this.collections.get(collectionAddress);
if (cached) return cached;
const collection = new ethers.Contract(
collectionAddress,
NFT_ABI,
this.provider
);
const [name, symbol, totalSupply, maxSupply] = await Promise.all([
collection.name(),
collection.symbol(),
collection.totalSupply(),
collection.maxSupply()
]);
return {
address: collectionAddress,
name, symbol,
totalSupply: totalSupply.toNumber(),
maxSupply: maxSupply.toNumber(),
progress: (totalSupply.toNumber() / maxSupply.toNumber() * 100).toFixed(2)
};
}
extractCollectionAddress(receipt) {
// 实现提取逻辑
return receipt.logs[0].address; // 简化实现
}
}
// DEX 交易对工厂
class DEXFactory {
constructor(provider, factoryAddress) {
this.provider = provider;
this.factoryContract = new ethers.Contract(
factoryAddress,
DEX_FACTORY_ABI,
provider
);
this.pairs = new Map();
}
// 创建交易对
async createPair(tokenA, tokenB, signer) {
try {
// 检查是否已存在
const existingPair = await this.factoryContract.getPair(tokenA, tokenB);
if (existingPair !== ethers.constants.AddressZero) {
return existingPair;
}
const tx = await this.factoryContract.connect(signer)
.createPair(tokenA, tokenB);
const receipt = await tx.wait();
const pairAddress = this.extractPairAddress(receipt);
// 缓存交易对
this.pairs.set(`${tokenA}-${tokenB}`, pairAddress);
this.pairs.set(`${tokenB}-${tokenA}`, pairAddress);
return pairAddress;
} catch (error) {
console.error('创建交易对失败:', error);
throw error;
}
}
// 获取所有交易对
async getAllPairs() {
const pairCount = await this.factoryContract.allPairsLength();
const pairs = [];
for (let i = 0; i < pairCount.toNumber(); i++) {
const pairAddress = await this.factoryContract.allPairs(i);
const pairInfo = await this.getPairInfo(pairAddress);
pairs.push(pairInfo);
}
return pairs;
}
async getPairInfo(pairAddress) {
const pair = new ethers.Contract(pairAddress, PAIR_ABI, this.provider);
const [token0, token1, reserves] = await Promise.all([
pair.token0(),
pair.token1(),
pair.getReserves()
]);
return {
address: pairAddress,
token0, token1,
reserve0: reserves[0],
reserve1: reserves[1]
};
}
extractPairAddress(receipt) {
// 从 PairCreated 事件中提取地址
const event = receipt.logs.find(log => {
try {
const parsed = this.factoryContract.interface.parseLog(log);
return parsed.name === 'PairCreated';
} catch {
return false;
}
});
if (event) {
const parsed = this.factoryContract.interface.parseLog(event);
return parsed.args.pair;
}
throw new Error('未找到 PairCreated 事件');
}
}
使用示例:
async function demonstrateFactoryPattern() {
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
// 1. 代币工厂演示
const tokenFactory = new FactoryManager(
provider,
TOKEN_FACTORY_ADDRESS,
TOKEN_FACTORY_ABI
);
try {
// 开始监听事件
await tokenFactory.startEventMonitoring();
// 部署单个代币
const deployResult = await tokenFactory.deployToken({
name: 'MyFactoryToken',
symbol: 'MFT',
totalSupply: 1000000
}, signer);
console.log('部署结果:', deployResult);
// 批量部署
const batchResult = await tokenFactory.batchDeployTokens([
{ name: 'BatchToken1', symbol: 'BT1', totalSupply: 100000 },
{ name: 'BatchToken2', symbol: 'BT2', totalSupply: 200000 }
], signer);
console.log('批量部署结果:', batchResult);
// 获取统计信息
const stats = await tokenFactory.getFactoryStats();
console.log('工厂统计:', stats);
// 获取用户合约
const userContracts = await tokenFactory.getUserContracts(await signer.getAddress());
console.log('用户合约:', userContracts);
} catch (error) {
console.error('工厂演示失败:', error);
} finally {
tokenFactory.stopEventMonitoring();
}
}
// 执行演示
demonstrateFactoryPattern();
Factory 模式的关键优势:
最佳实践:
How to monitor and analyze contract Gas usage?
How to monitor and analyze contract Gas usage?
考察点:Gas使用分析与优化。
答案:
Gas 使用监控和分析是智能合约优化的重要环节。通过系统性地跟踪和分析 Gas 消耗模式,可以识别性能瓶颈、优化合约设计、预测交易成本,并确保最佳的用户体验。
Gas 监控基础架构:
class GasMonitor {
constructor(provider, contractAddress, contractABI) {
this.provider = provider;
this.contractAddress = contractAddress;
this.contract = new ethers.Contract(contractAddress, contractABI, provider);
// Gas 使用历史记录
this.gasHistory = [];
this.functionGasStats = new Map();
this.dailyStats = new Map();
// 监控配置
this.alertThresholds = {
high: 500000, // 高 Gas 使用警告
extreme: 1000000, // 极高 Gas 使用警告
efficiency: 0.7 // 效率警告阈值
};
this.isMonitoring = false;
}
// 开始监控合约 Gas 使用
async startMonitoring() {
if (this.isMonitoring) {
console.log('监控已在运行');
return;
}
this.isMonitoring = true;
console.log(`开始监控合约 ${this.contractAddress} 的 Gas 使用`);
// 监听所有交易
this.provider.on('block', async (blockNumber) => {
if (!this.isMonitoring) return;
try {
await this.analyzeBlockTransactions(blockNumber);
} catch (error) {
console.error(`分析区块 ${blockNumber} 失败:`, error);
}
});
// 定期生成报告
this.reportInterval = setInterval(() => {
this.generatePeriodicReport();
}, 300000); // 每5分钟
console.log('Gas 监控已启动');
}
// 停止监控
stopMonitoring() {
this.isMonitoring = false;
this.provider.removeAllListeners('block');
if (this.reportInterval) {
clearInterval(this.reportInterval);
}
console.log('Gas 监控已停止');
}
// 分析区块中的相关交易
async analyzeBlockTransactions(blockNumber) {
try {
const block = await this.provider.getBlock(blockNumber, true);
if (!block || !block.transactions) {
return;
}
// 过滤与监控合约相关的交易
const relevantTxs = block.transactions.filter(tx =>
tx.to && tx.to.toLowerCase() === this.contractAddress.toLowerCase()
);
if (relevantTxs.length === 0) {
return;
}
console.log(`区块 ${blockNumber} 发现 ${relevantTxs.length} 个相关交易`);
// 分析每个交易
for (const tx of relevantTxs) {
await this.analyzeTransaction(tx);
}
} catch (error) {
console.error(`分析区块 ${blockNumber} 失败:`, error);
}
}
// 分析单个交易的 Gas 使用
async analyzeTransaction(tx) {
try {
const receipt = await this.provider.getTransactionReceipt(tx.hash);
if (!receipt) {
return;
}
// 解析函数调用
const functionCall = this.parseFunctionCall(tx.data);
// 计算 Gas 效率
const gasEfficiency = receipt.gasUsed.mul(10000).div(tx.gasLimit);
// 计算实际费用
const actualCost = receipt.gasUsed.mul(receipt.effectiveGasPrice || tx.gasPrice);
const gasAnalysis = {
txHash: tx.hash,
blockNumber: receipt.blockNumber,
timestamp: new Date(),
functionName: functionCall.name,
functionParams: functionCall.params,
// Gas 数据
gasLimit: tx.gasLimit,
gasUsed: receipt.gasUsed,
gasPrice: receipt.effectiveGasPrice || tx.gasPrice,
// 效率指标
efficiency: gasEfficiency.toNumber() / 100, // 转换为百分比
actualCost: ethers.utils.formatEther(actualCost),
// 状态
status: receipt.status,
success: receipt.status === 1
};
// 记录到历史
this.recordGasUsage(gasAnalysis);
// 检查警告条件
this.checkAlerts(gasAnalysis);
console.log(`交易 ${tx.hash.slice(0, 10)}... Gas分析:`, {
function: functionCall.name,
gasUsed: receipt.gasUsed.toString(),
efficiency: (gasAnalysis.efficiency).toFixed(2) + '%',
cost: gasAnalysis.actualCost + ' ETH'
});
return gasAnalysis;
} catch (error) {
console.error(`分析交易 ${tx.hash} 失败:`, error);
}
}
// 解析函数调用
parseFunctionCall(data) {
try {
if (!data || data.length < 10) {
return { name: 'unknown', params: [] };
}
const functionSelector = data.slice(0, 10);
// 尝试从合约 ABI 中匹配函数
for (const fragment of this.contract.interface.fragments) {
if (fragment.type === 'function') {
const selector = this.contract.interface.getSighash(fragment);
if (selector === functionSelector) {
try {
const decoded = this.contract.interface.decodeFunctionData(fragment, data);
return {
name: fragment.name,
params: decoded,
signature: fragment.format()
};
} catch (decodeError) {
return { name: fragment.name, params: [], error: 'decode_failed' };
}
}
}
}
return { name: 'unknown', selector: functionSelector, params: [] };
} catch (error) {
return { name: 'parse_error', error: error.message, params: [] };
}
}
// 记录 Gas 使用数据
recordGasUsage(gasAnalysis) {
// 添加到历史记录
this.gasHistory.push(gasAnalysis);
// 限制历史记录大小
if (this.gasHistory.length > 1000) {
this.gasHistory = this.gasHistory.slice(-1000);
}
// 更新函数级别统计
this.updateFunctionStats(gasAnalysis);
// 更新日常统计
this.updateDailyStats(gasAnalysis);
}
// 更新函数级别统计
updateFunctionStats(gasAnalysis) {
const functionName = gasAnalysis.functionName;
if (!this.functionGasStats.has(functionName)) {
this.functionGasStats.set(functionName, {
callCount: 0,
totalGasUsed: ethers.BigNumber.from(0),
totalCost: ethers.BigNumber.from(0),
avgGasUsed: ethers.BigNumber.from(0),
avgCost: ethers.BigNumber.from(0),
minGasUsed: ethers.constants.MaxUint256,
maxGasUsed: ethers.BigNumber.from(0),
successRate: 0,
successCount: 0
});
}
const stats = this.functionGasStats.get(functionName);
stats.callCount++;
stats.totalGasUsed = stats.totalGasUsed.add(gasAnalysis.gasUsed);
const cost = gasAnalysis.gasUsed.mul(gasAnalysis.gasPrice);
stats.totalCost = stats.totalCost.add(cost);
if (gasAnalysis.gasUsed.lt(stats.minGasUsed)) {
stats.minGasUsed = gasAnalysis.gasUsed;
}
if (gasAnalysis.gasUsed.gt(stats.maxGasUsed)) {
stats.maxGasUsed = gasAnalysis.gasUsed;
}
if (gasAnalysis.success) {
stats.successCount++;
}
// 计算平均值
stats.avgGasUsed = stats.totalGasUsed.div(stats.callCount);
stats.avgCost = stats.totalCost.div(stats.callCount);
stats.successRate = (stats.successCount / stats.callCount) * 100;
}
// 更新日常统计
updateDailyStats(gasAnalysis) {
const dateKey = gasAnalysis.timestamp.toISOString().split('T')[0];
if (!this.dailyStats.has(dateKey)) {
this.dailyStats.set(dateKey, {
date: dateKey,
transactionCount: 0,
totalGasUsed: ethers.BigNumber.from(0),
totalCost: ethers.BigNumber.from(0),
avgGasPrice: ethers.BigNumber.from(0),
successCount: 0
});
}
const dailyStat = this.dailyStats.get(dateKey);
dailyStat.transactionCount++;
dailyStat.totalGasUsed = dailyStat.totalGasUsed.add(gasAnalysis.gasUsed);
const cost = gasAnalysis.gasUsed.mul(gasAnalysis.gasPrice);
dailyStat.totalCost = dailyStat.totalCost.add(cost);
if (gasAnalysis.success) {
dailyStat.successCount++;
}
// 计算平均 Gas 价格
dailyStat.avgGasPrice = dailyStat.totalCost.div(dailyStat.totalGasUsed);
}
// 检查警告条件
checkAlerts(gasAnalysis) {
const alerts = [];
// 高 Gas 使用警告
if (gasAnalysis.gasUsed.gte(this.alertThresholds.high)) {
alerts.push({
type: 'HIGH_GAS_USAGE',
severity: gasAnalysis.gasUsed.gte(this.alertThresholds.extreme) ? 'critical' : 'warning',
message: `函数 ${gasAnalysis.functionName} 使用了 ${gasAnalysis.gasUsed.toString()} Gas`,
gasUsed: gasAnalysis.gasUsed.toString(),
threshold: this.alertThresholds.high
});
}
// 低效率警告
if (gasAnalysis.efficiency < this.alertThresholds.efficiency) {
alerts.push({
type: 'LOW_EFFICIENCY',
severity: 'info',
message: `交易效率较低: ${(gasAnalysis.efficiency * 100).toFixed(2)}%`,
efficiency: gasAnalysis.efficiency,
threshold: this.alertThresholds.efficiency
});
}
// 失败交易警告
if (!gasAnalysis.success) {
alerts.push({
type: 'TRANSACTION_FAILED',
severity: 'error',
message: '交易执行失败',
txHash: gasAnalysis.txHash
});
}
if (alerts.length > 0) {
console.warn('🚨 Gas 监控警告:', alerts);
}
}
// 生成定期报告
generatePeriodicReport() {
const recentTransactions = this.gasHistory.slice(-50); // 最近50笔交易
if (recentTransactions.length === 0) {
return;
}
console.log('\n📊 Gas 使用报告 (最近50笔交易):');
console.log('=' .repeat(50));
// 总体统计
const totalGas = recentTransactions.reduce(
(sum, tx) => sum.add(tx.gasUsed),
ethers.BigNumber.from(0)
);
const avgGas = totalGas.div(recentTransactions.length);
const successfulTxs = recentTransactions.filter(tx => tx.success).length;
const successRate = (successfulTxs / recentTransactions.length) * 100;
console.log(`总交易数: ${recentTransactions.length}`);
console.log(`成功率: ${successRate.toFixed(2)}%`);
console.log(`平均 Gas 使用: ${avgGas.toString()}`);
console.log(`总 Gas 消耗: ${totalGas.toString()}`);
// 函数级别统计
console.log('\n📋 函数 Gas 使用统计:');
this.functionGasStats.forEach((stats, functionName) => {
console.log(`${functionName}:`);
console.log(` 调用次数: ${stats.callCount}`);
console.log(` 平均 Gas: ${stats.avgGasUsed.toString()}`);
console.log(` Gas 范围: ${stats.minGasUsed.toString()} - ${stats.maxGasUsed.toString()}`);
console.log(` 成功率: ${stats.successRate.toFixed(2)}%`);
});
console.log('=' .repeat(50) + '\n');
}
}
Gas 分析工具:
class GasAnalyzer {
constructor(provider) {
this.provider = provider;
this.analysisCache = new Map();
this.benchmarks = new Map();
}
// 深度分析单个交易的 Gas 使用
async analyzeTransactionGas(txHash) {
try {
console.log(`开始深度分析交易: ${txHash}`);
const [tx, receipt] = await Promise.all([
this.provider.getTransaction(txHash),
this.provider.getTransactionReceipt(txHash)
]);
if (!tx || !receipt) {
throw new Error('交易不存在或未确认');
}
// 基础 Gas 分析
const basicAnalysis = this.calculateBasicGasMetrics(tx, receipt);
// 如果是合约调用,进行详细分析
let detailedAnalysis = null;
if (tx.to && tx.data && tx.data !== '0x') {
detailedAnalysis = await this.analyzeContractCall(tx, receipt);
}
// 与基准对比
const benchmarkComparison = this.compareToBenchmarks(basicAnalysis, detailedAnalysis);
const analysis = {
txHash,
basic: basicAnalysis,
detailed: detailedAnalysis,
benchmark: benchmarkComparison,
recommendations: this.generateOptimizationRecommendations(basicAnalysis, detailedAnalysis),
timestamp: new Date()
};
// 缓存分析结果
this.analysisCache.set(txHash, analysis);
console.log('交易 Gas 分析完成:', analysis);
return analysis;
} catch (error) {
console.error('交易 Gas 分析失败:', error);
throw error;
}
}
// 计算基础 Gas 指标
calculateBasicGasMetrics(tx, receipt) {
const gasLimit = tx.gasLimit;
const gasUsed = receipt.gasUsed;
const gasPrice = receipt.effectiveGasPrice || tx.gasPrice;
// Gas 效率(使用率)
const gasEfficiency = gasUsed.mul(10000).div(gasLimit).toNumber() / 100;
// 实际费用
const actualCost = gasUsed.mul(gasPrice);
const maxPossibleCost = gasLimit.mul(gasPrice);
const savedAmount = maxPossibleCost.sub(actualCost);
// Gas 价格分析
const gasPriceGwei = parseFloat(ethers.utils.formatUnits(gasPrice, 'gwei'));
return {
gasLimit: gasLimit.toString(),
gasUsed: gasUsed.toString(),
gasPrice: gasPriceGwei + ' Gwei',
efficiency: gasEfficiency.toFixed(2) + '%',
actualCost: ethers.utils.formatEther(actualCost) + ' ETH',
maxPossibleCost: ethers.utils.formatEther(maxPossibleCost) + ' ETH',
savedAmount: ethers.utils.formatEther(savedAmount) + ' ETH',
status: receipt.status === 1 ? 'success' : 'failed'
};
}
// 分析合约调用
async analyzeContractCall(tx, receipt) {
try {
// 获取合约字节码以分析复杂度
const contractCode = await this.provider.getCode(tx.to);
const codeSize = (contractCode.length - 2) / 2; // 移除 '0x' 前缀
// 分析调用数据
const callDataSize = (tx.data.length - 2) / 2;
// 分析事件日志
const eventAnalysis = this.analyzeEventLogs(receipt.logs);
// 估算操作复杂度
const complexityAnalysis = this.estimateOperationComplexity(
tx, receipt, codeSize, callDataSize
);
return {
contractAddress: tx.to,
codeSize,
callDataSize,
eventCount: receipt.logs.length,
eventAnalysis,
complexityAnalysis,
gasPerByte: receipt.gasUsed.div(Math.max(callDataSize, 1)).toString(),
storageOperations: this.estimateStorageOperations(receipt.logs)
};
} catch (error) {
console.error('合约调用分析失败:', error);
return null;
}
}
// 分析事件日志
analyzeEventLogs(logs) {
const topicCounts = new Map();
const addressCounts = new Map();
logs.forEach(log => {
// 统计事件类型(通过第一个 topic)
if (log.topics.length > 0) {
const eventSignature = log.topics[0];
topicCounts.set(eventSignature, (topicCounts.get(eventSignature) || 0) + 1);
}
// 统计合约地址
addressCounts.set(log.address, (addressCounts.get(log.address) || 0) + 1);
});
return {
totalEvents: logs.length,
uniqueEventTypes: topicCounts.size,
uniqueContracts: addressCounts.size,
topEventTypes: Array.from(topicCounts.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 5),
topContracts: Array.from(addressCounts.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 3)
};
}
// 估算操作复杂度
estimateOperationComplexity(tx, receipt, codeSize, callDataSize) {
const gasUsed = receipt.gasUsed.toNumber();
// 基础操作成本估算
const baseCost = 21000; // 基础交易成本
const callDataCost = callDataSize * 16; // 大约每字节16 Gas
const remainingGas = Math.max(gasUsed - baseCost - callDataCost, 0);
// 复杂度评级
let complexityRating;
if (remainingGas < 50000) {
complexityRating = 'simple';
} else if (remainingGas < 200000) {
complexityRating = 'moderate';
} else if (remainingGas < 500000) {
complexityRating = 'complex';
} else {
complexityRating = 'very_complex';
}
return {
baseCost,
callDataCost,
computationCost: remainingGas,
complexityRating,
gasPerComputationUnit: remainingGas / Math.max(codeSize, 1),
estimatedStorageCost: this.estimateStorageCost(receipt.logs)
};
}
// 估算存储操作
estimateStorageOperations(logs) {
// 基于日志数量和类型估算存储操作
let storageWrites = 0;
let storageReads = 0;
logs.forEach(log => {
// 简化估算:每个事件通常涉及1-2个存储写入
storageWrites += 1;
// 基于 topics 数量估算读取操作
storageReads += Math.max(log.topics.length - 1, 0);
});
return {
estimatedWrites: storageWrites,
estimatedReads: storageReads,
totalOperations: storageWrites + storageReads
};
}
// 估算存储成本
estimateStorageCost(logs) {
const SSTORE_SET_COST = 20000; // 新存储槽成本
const SSTORE_RESET_COST = 5000; // 修改现有存储槽成本
// 基于日志估算存储成本
const estimatedStorageOps = this.estimateStorageOperations(logs);
return {
minCost: estimatedStorageOps.estimatedWrites * SSTORE_RESET_COST,
maxCost: estimatedStorageOps.estimatedWrites * SSTORE_SET_COST,
avgCost: estimatedStorageOps.estimatedWrites * ((SSTORE_SET_COST + SSTORE_RESET_COST) / 2)
};
}
// 与基准对比
compareToBenchmarks(basicAnalysis, detailedAnalysis) {
// 这里可以与已知的基准数据对比
// 例如:标准 ERC20 转账、Uniswap 交换等
const gasUsed = parseInt(basicAnalysis.gasUsed);
const benchmarks = {
'ETH Transfer': { min: 21000, max: 21000, avg: 21000 },
'ERC20 Transfer': { min: 45000, max: 65000, avg: 55000 },
'Uniswap V2 Swap': { min: 100000, max: 150000, avg: 125000 },
'Uniswap V3 Swap': { min: 120000, max: 180000, avg: 150000 },
'NFT Mint': { min: 80000, max: 200000, avg: 140000 }
};
const comparisons = [];
Object.entries(benchmarks).forEach(([operation, benchmark]) => {
const efficiency = (benchmark.avg / gasUsed) * 100;
let status;
if (gasUsed <= benchmark.min) {
status = 'excellent';
} else if (gasUsed <= benchmark.avg) {
status = 'good';
} else if (gasUsed <= benchmark.max) {
status = 'acceptable';
} else {
status = 'poor';
}
comparisons.push({
operation,
benchmark,
actualGas: gasUsed,
efficiency: efficiency.toFixed(2) + '%',
status
});
});
return comparisons;
}
// 生成优化建议
generateOptimizationRecommendations(basicAnalysis, detailedAnalysis) {
const recommendations = [];
const gasUsed = parseInt(basicAnalysis.gasUsed);
const efficiency = parseFloat(basicAnalysis.efficiency);
// 基于效率的建议
if (efficiency < 50) {
recommendations.push({
type: 'efficiency',
priority: 'high',
suggestion: '交易 Gas 效率较低,考虑优化 Gas Limit 设置',
impact: 'cost_reduction'
});
}
// 基于 Gas 使用量的建议
if (gasUsed > 200000) {
recommendations.push({
type: 'optimization',
priority: 'medium',
suggestion: 'Gas 使用量较高,考虑拆分复杂操作或使用更高效的算法',
impact: 'performance'
});
}
// 基于详细分析的建议
if (detailedAnalysis) {
if (detailedAnalysis.callDataSize > 1000) {
recommendations.push({
type: 'data_optimization',
priority: 'medium',
suggestion: '调用数据较大,考虑压缩参数或使用 IPFS 存储大数据',
impact: 'cost_reduction'
});
}
if (detailedAnalysis.eventCount > 10) {
recommendations.push({
type: 'event_optimization',
priority: 'low',
suggestion: '事件数量较多,考虑合并相关事件减少日志成本',
impact: 'cost_reduction'
});
}
}
return recommendations;
}
// 批量分析多个交易
async batchAnalyzeTransactions(txHashes) {
console.log(`开始批量分析 ${txHashes.length} 个交易`);
const analyses = [];
for (let i = 0; i < txHashes.length; i++) {
try {
console.log(`分析进度: ${i + 1}/${txHashes.length}`);
const analysis = await this.analyzeTransactionGas(txHashes[i]);
analyses.push(analysis);
// 避免请求过于频繁
if (i < txHashes.length - 1) {
await this.delay(1000); // 1秒延迟
}
} catch (error) {
console.error(`分析交易 ${txHashes[i]} 失败:`, error);
}
}
// 生成批量分析报告
const batchReport = this.generateBatchAnalysisReport(analyses);
console.log('批量分析完成');
return {
analyses,
report: batchReport
};
}
// 生成批量分析报告
generateBatchAnalysisReport(analyses) {
if (analyses.length === 0) {
return { message: '没有成功分析的交易' };
}
// 统计数据
const totalGas = analyses.reduce((sum, analysis) =>
sum + parseInt(analysis.basic.gasUsed), 0
);
const avgGas = totalGas / analyses.length;
const gasUsageDistribution = this.calculateGasDistribution(analyses);
const complexityDistribution = this.calculateComplexityDistribution(analyses);
return {
summary: {
totalTransactions: analyses.length,
totalGasUsed: totalGas,
averageGasUsed: Math.round(avgGas),
gasRange: {
min: Math.min(...analyses.map(a => parseInt(a.basic.gasUsed))),
max: Math.max(...analyses.map(a => parseInt(a.basic.gasUsed)))
}
},
distributions: {
gasUsage: gasUsageDistribution,
complexity: complexityDistribution
},
recommendations: this.generateBatchRecommendations(analyses)
};
}
// 计算 Gas 使用分布
calculateGasDistribution(analyses) {
const ranges = [
{ min: 0, max: 50000, label: 'Low (0-50K)' },
{ min: 50000, max: 100000, label: 'Medium (50K-100K)' },
{ min: 100000, max: 200000, label: 'High (100K-200K)' },
{ min: 200000, max: Infinity, label: 'Very High (200K+)' }
];
const distribution = ranges.map(range => ({
...range,
count: 0,
percentage: 0
}));
analyses.forEach(analysis => {
const gasUsed = parseInt(analysis.basic.gasUsed);
const rangeIndex = ranges.findIndex(range =>
gasUsed >= range.min && gasUsed < range.max
);
if (rangeIndex !== -1) {
distribution[rangeIndex].count++;
}
});
// 计算百分比
distribution.forEach(range => {
range.percentage = ((range.count / analyses.length) * 100).toFixed(2);
});
return distribution;
}
// 计算复杂度分布
calculateComplexityDistribution(analyses) {
const complexityCount = {
simple: 0,
moderate: 0,
complex: 0,
very_complex: 0
};
analyses.forEach(analysis => {
if (analysis.detailed && analysis.detailed.complexityAnalysis) {
const complexity = analysis.detailed.complexityAnalysis.complexityRating;
if (complexityCount.hasOwnProperty(complexity)) {
complexityCount[complexity]++;
}
}
});
const total = Object.values(complexityCount).reduce((sum, count) => sum + count, 0);
return Object.entries(complexityCount).map(([complexity, count]) => ({
complexity,
count,
percentage: total > 0 ? ((count / total) * 100).toFixed(2) : '0.00'
}));
}
// 生成批量建议
generateBatchRecommendations(analyses) {
const recommendations = [];
// 分析整体趋势
const avgGas = analyses.reduce((sum, analysis) =>
sum + parseInt(analysis.basic.gasUsed), 0
) / analyses.length;
const highGasCount = analyses.filter(analysis =>
parseInt(analysis.basic.gasUsed) > 200000
).length;
const lowEfficiencyCount = analyses.filter(analysis =>
parseFloat(analysis.basic.efficiency) < 70
).length;
if (highGasCount > analyses.length * 0.3) {
recommendations.push({
type: 'batch_optimization',
priority: 'high',
suggestion: '超过30%的交易Gas使用量过高,建议全面优化合约逻辑',
affectedTransactions: highGasCount
});
}
if (lowEfficiencyCount > analyses.length * 0.5) {
recommendations.push({
type: 'gas_estimation',
priority: 'medium',
suggestion: '超过50%的交易Gas效率较低,建议改进Gas估算策略',
affectedTransactions: lowEfficiencyCount
});
}
return recommendations;
}
// 延迟函数
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
使用示例:
// Gas 监控和分析完整示例
async function demonstrateGasMonitoring() {
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
// 1. 启动 Gas 监控
const gasMonitor = new GasMonitor(
provider,
CONTRACT_ADDRESS,
CONTRACT_ABI
);
// 配置警告阈值
gasMonitor.alertThresholds = {
high: 300000,
extreme: 800000,
efficiency: 0.6
};
// 开始监控
await gasMonitor.startMonitoring();
// 2. Gas 分析器
const gasAnalyzer = new GasAnalyzer(provider);
try {
// 分析特定交易
const singleAnalysis = await gasAnalyzer.analyzeTransactionGas(SAMPLE_TX_HASH);
console.log('单个交易分析:', singleAnalysis);
// 批量分析多个交易
const batchResult = await gasAnalyzer.batchAnalyzeTransactions([
TX_HASH_1,
TX_HASH_2,
TX_HASH_3
]);
console.log('批量分析报告:', batchResult.report);
// 运行一段时间后生成报告
setTimeout(() => {
gasMonitor.generatePeriodicReport();
}, 60000); // 1分钟后
// 设置定时停止监控
setTimeout(() => {
gasMonitor.stopMonitoring();
console.log('Gas 监控演示结束');
}, 300000); // 5分钟后停止
} catch (error) {
console.error('Gas 分析演示失败:', error);
}
}
// 执行演示
demonstrateGasMonitoring();
Gas 监控的关键要点:
最佳实践:
What is a Web3 wallet? What are the mainstream Web3 wallets?
What is a Web3 wallet? What are the mainstream Web3 wallets?
考察点:钱包基本概念与分类。
答案:
Web3钱包是用户在去中心化网络中管理数字资产和身份的工具。它不仅存储加密货币,还能与智能合约交互、签署交易,是用户进入Web3世界的入口。Web3钱包通过私钥控制资产,用户拥有完全的资产控制权,体现了"Not your keys, not your crypto"的核心理念。
主要类型:
主流Web3钱包:
核心功能:
What is the basic working principle of MetaMask?
What is the basic working principle of MetaMask?
考察点:MetaMask核心机制理解。
答案:
MetaMask是一个浏览器插件钱包,作为用户与以太坊区块链之间的桥梁。它在浏览器中注入ethereum对象,使网页能够与区块链交互。MetaMask管理用户的私钥,代替用户签名交易,并通过RPC节点将交易广播到区块链网络。
工作原理:
核心组件:
安全机制:
How to detect if a user has MetaMask installed?
How to detect if a user has MetaMask installed?
考察点:钱包检测基础。
答案:
检测MetaMask安装的核心是检查浏览器中是否存在ethereum对象。MetaMask会在页面加载时注入window.ethereum,我们可以通过检测这个对象来判断钱包是否已安装。
基础检测方法:
// 简单检测
function isMetaMaskInstalled() {
return typeof window.ethereum !== 'undefined';
}
// 更准确的检测
function detectMetaMask() {
if (typeof window.ethereum !== 'undefined') {
// 检查是否确实是MetaMask
if (window.ethereum.isMetaMask) {
return true;
}
}
return false;
}
完整检测实现:
async function checkMetaMask() {
// 检查ethereum对象是否存在
if (typeof window.ethereum === 'undefined') {
return {
installed: false,
message: 'MetaMask未安装,请先安装MetaMask插件'
};
}
// 检查是否为MetaMask
if (!window.ethereum.isMetaMask) {
return {
installed: false,
message: '检测到其他钱包,但未安装MetaMask'
};
}
return {
installed: true,
message: 'MetaMask已安装并可用'
};
}
// checkMetaMask() => {installed: true, message: 'MetaMask已安装并可用'}
注意事项:
How to request users to connect their wallet? What should be noted during the connection process?
How to request users to connect their wallet? What should be noted during the connection process?
考察点:钱包连接流程。
答案:
钱包连接是通过调用ethereum.request()方法请求用户授权访问账户。这个过程会弹出MetaMask授权窗口,用户确认后返回账户地址数组。连接建立了网站与钱包的信任关系,是后续所有交互的基础。
基本连接方法:
async function connectWallet() {
try {
// 请求连接钱包
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
if (accounts.length > 0) {
console.log('连接成功,当前账户:', accounts[0]);
return accounts[0];
}
} catch (error) {
console.error('连接失败:', error);
handleConnectionError(error);
}
}
// connectWallet() => '0x742d35Cc6327C0532...'
完整连接实现:
async function connectToMetaMask() {
// 检查MetaMask是否安装
if (!window.ethereum) {
throw new Error('MetaMask未安装');
}
try {
// 请求账户访问权限
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
// 获取网络信息
const chainId = await window.ethereum.request({
method: 'eth_chainId'
});
return {
success: true,
account: accounts[0],
chainId: chainId,
message: '钱包连接成功'
};
} catch (error) {
return handleConnectionError(error);
}
}
function handleConnectionError(error) {
if (error.code === 4001) {
return { success: false, message: '用户拒绝连接请求' };
} else if (error.code === -32002) {
return { success: false, message: '连接请求已存在,请检查MetaMask' };
} else {
return { success: false, message: `连接失败: ${error.message}` };
}
}
连接注意事项:
What are account permissions? How to get the user's Ethereum address?
What are account permissions? How to get the user’s Ethereum address?
考察点:权限管理基础。
答案:
账户权限是指网站访问用户钱包账户信息的授权。用户必须明确授权,网站才能获取账户地址、发送交易等。这是Web3安全机制的核心,确保用户对自己的资产和隐私有完全控制权。权限一旦授予,网站可以读取账户地址和余额,但不能自动发送交易。
获取账户地址的方法:
// 方法1:检查已授权账户
async function getConnectedAccounts() {
try {
const accounts = await window.ethereum.request({
method: 'eth_accounts'
});
return accounts; // 返回已授权的账户数组
} catch (error) {
console.error('获取账户失败:', error);
return [];
}
}
// 方法2:请求账户访问(会弹窗)
async function requestAccountAccess() {
try {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
return accounts[0]; // 返回主账户地址
} catch (error) {
throw new Error('用户拒绝授权或发生错误');
}
}
完整账户管理实现:
class WalletManager {
constructor() {
this.currentAccount = null;
this.isConnected = false;
}
// 检查连接状态并获取账户
async checkConnection() {
try {
const accounts = await window.ethereum.request({
method: 'eth_accounts'
});
if (accounts.length > 0) {
this.currentAccount = accounts[0];
this.isConnected = true;
return this.currentAccount;
} else {
this.isConnected = false;
return null;
}
} catch (error) {
console.error('检查连接状态失败:', error);
return null;
}
}
// 获取当前账户地址
getCurrentAccount() {
return this.currentAccount;
}
}
// const wallet = new WalletManager();
// await wallet.checkConnection() => '0x742d35Cc6327C0532...'
权限相关概念:
最佳实践:
How to listen for account switching events in the wallet?
How to listen for account switching events in the wallet?
考察点:账户状态监听。
答案:
账户切换事件监听通过ethereum.on(‘accountsChanged’, callback)方法实现。当用户在MetaMask中切换账户、锁定钱包或撤销权限时,都会触发此事件。监听这个事件对于保持应用状态与钱包状态同步非常重要。
基础监听实现:
// 监听账户变化
function setupAccountListener() {
if (window.ethereum) {
window.ethereum.on('accountsChanged', handleAccountsChanged);
}
}
function handleAccountsChanged(accounts) {
if (accounts.length === 0) {
// 用户断开连接或锁定了钱包
console.log('钱包已断开连接');
handleDisconnection();
} else {
// 用户切换了账户
console.log('账户已切换:', accounts[0]);
updateCurrentAccount(accounts[0]);
}
}
function handleDisconnection() {
// 清理应用状态
currentAccount = null;
isConnected = false;
// 更新UI显示
updateUI();
}
// setupAccountListener()
完整账户监听管理:
class AccountManager {
constructor() {
this.currentAccount = null;
this.listeners = [];
this.setupListener();
}
setupListener() {
if (window.ethereum) {
window.ethereum.on('accountsChanged', (accounts) => {
this.handleAccountChange(accounts);
});
}
}
handleAccountChange(accounts) {
const previousAccount = this.currentAccount;
if (accounts.length === 0) {
// 钱包断开或锁定
this.currentAccount = null;
this.notifyListeners('disconnected', { previousAccount });
} else if (accounts[0] !== previousAccount) {
// 账户切换
this.currentAccount = accounts[0];
this.notifyListeners('accountChanged', {
newAccount: accounts[0],
previousAccount: previousAccount
});
}
}
// 添加事件监听器
addListener(callback) {
this.listeners.push(callback);
}
// 移除事件监听器
removeListener(callback) {
this.listeners = this.listeners.filter(listener => listener !== callback);
}
// 通知所有监听器
notifyListeners(event, data) {
this.listeners.forEach(callback => {
try {
callback(event, data);
} catch (error) {
console.error('监听器回调错误:', error);
}
});
}
// 清理监听器
cleanup() {
if (window.ethereum && window.ethereum.removeListener) {
window.ethereum.removeListener('accountsChanged', this.handleAccountChange);
}
}
}
// 使用示例
const accountManager = new AccountManager();
accountManager.addListener((event, data) => {
if (event === 'accountChanged') {
console.log(`账户从 ${data.previousAccount} 切换到 ${data.newAccount}`);
} else if (event === 'disconnected') {
console.log('钱包已断开连接');
}
});
监听注意事项:
What is network switching? How to request users to switch to a specific network?
What is network switching? How to request users to switch to a specific network?
考察点:网络管理基础。
答案:
网络切换是指在不同的区块链网络之间切换,如从以太坊主网切换到Polygon或BSC。不同网络有不同的Chain ID、RPC端点和原生代币。DApp通常需要在特定网络上运行,因此需要引导用户切换到正确的网络。
请求网络切换:
// 请求切换到指定网络
async function switchNetwork(chainId) {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: chainId }]
});
console.log('网络切换成功');
} catch (error) {
if (error.code === 4902) {
// 网络未添加,需要先添加网络
console.log('网络未添加,尝试添加网络');
throw new Error('NETWORK_NOT_ADDED');
} else {
console.error('网络切换失败:', error);
throw error;
}
}
}
// 切换到Polygon网络
// switchNetwork('0x89') => 切换成功
添加新网络:
// 添加新网络配置
async function addNetwork(networkConfig) {
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [networkConfig]
});
console.log('网络添加成功');
} catch (error) {
console.error('网络添加失败:', error);
throw error;
}
}
// Polygon网络配置
const polygonNetwork = {
chainId: '0x89', // 137 in decimal
chainName: 'Polygon Mainnet',
nativeCurrency: {
name: 'MATIC',
symbol: 'MATIC',
decimals: 18
},
rpcUrls: ['https://polygon-rpc.com'],
blockExplorerUrls: ['https://polygonscan.com']
};
完整网络管理实现:
class NetworkManager {
constructor() {
this.networks = {
'0x1': {
chainId: '0x1',
chainName: 'Ethereum Mainnet',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: ['https://mainnet.infura.io/v3/YOUR_KEY'],
blockExplorerUrls: ['https://etherscan.io']
},
'0x89': {
chainId: '0x89',
chainName: 'Polygon Mainnet',
nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
rpcUrls: ['https://polygon-rpc.com'],
blockExplorerUrls: ['https://polygonscan.com']
}
};
}
async switchToNetwork(chainId) {
try {
// 尝试切换网络
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId }]
});
return { success: true, message: '网络切换成功' };
} catch (error) {
if (error.code === 4902) {
// 网络未添加,尝试添加
return await this.addAndSwitchNetwork(chainId);
} else if (error.code === 4001) {
return { success: false, message: '用户拒绝切换网络' };
} else {
return { success: false, message: `切换失败: ${error.message}` };
}
}
}
async addAndSwitchNetwork(chainId) {
const networkConfig = this.networks[chainId];
if (!networkConfig) {
return { success: false, message: '不支持的网络' };
}
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [networkConfig]
});
return { success: true, message: '网络添加并切换成功' };
} catch (error) {
return { success: false, message: `添加网络失败: ${error.message}` };
}
}
async getCurrentNetwork() {
try {
const chainId = await window.ethereum.request({
method: 'eth_chainId'
});
return chainId;
} catch (error) {
console.error('获取当前网络失败:', error);
return null;
}
}
}
// 使用示例
const networkManager = new NetworkManager();
await networkManager.switchToNetwork('0x89'); // 切换到Polygon
常见网络配置:
How to listen for network switching events in the wallet?
How to listen for network switching events in the wallet?
考察点:网络状态监听。
答案:
网络切换事件通过ethereum.on(‘chainChanged’, callback)方法监听。当用户在MetaMask中切换网络时,会触发此事件并返回新的Chain ID。监听网络变化对于确保DApp在正确网络上运行至关重要。
基础网络监听:
// 监听网络变化
function setupChainListener() {
if (window.ethereum) {
window.ethereum.on('chainChanged', handleChainChanged);
}
}
function handleChainChanged(chainId) {
console.log('网络已切换:', chainId);
// 转换为十进制显示
const networkId = parseInt(chainId, 16);
console.log('网络ID (十进制):', networkId);
// 更新应用状态
updateNetworkState(chainId);
// 刷新页面(推荐方式)
window.location.reload();
}
// setupChainListener()
完整网络监听管理:
class ChainManager {
constructor() {
this.currentChainId = null;
this.supportedChains = ['0x1', '0x89', '0x38']; // ETH, Polygon, BSC
this.listeners = [];
this.setupListener();
}
setupListener() {
if (window.ethereum) {
window.ethereum.on('chainChanged', (chainId) => {
this.handleChainChange(chainId);
});
}
}
async handleChainChange(newChainId) {
const previousChainId = this.currentChainId;
this.currentChainId = newChainId;
const isSupported = this.supportedChains.includes(newChainId);
const networkInfo = this.getNetworkInfo(newChainId);
// 通知监听器
this.notifyListeners({
previousChainId,
newChainId,
isSupported,
networkInfo
});
// 处理不支持的网络
if (!isSupported) {
this.handleUnsupportedNetwork(newChainId);
}
}
getNetworkInfo(chainId) {
const networks = {
'0x1': { name: 'Ethereum Mainnet', symbol: 'ETH' },
'0x89': { name: 'Polygon', symbol: 'MATIC' },
'0x38': { name: 'BSC', symbol: 'BNB' },
'0xa4b1': { name: 'Arbitrum One', symbol: 'ETH' },
'0xa': { name: 'Optimism', symbol: 'ETH' }
};
return networks[chainId] || {
name: 'Unknown Network',
symbol: 'Unknown'
};
}
handleUnsupportedNetwork(chainId) {
console.warn(`不支持的网络: ${chainId}`);
// 显示警告消息
this.showNetworkWarning(chainId);
}
showNetworkWarning(chainId) {
const networkInfo = this.getNetworkInfo(chainId);
alert(`当前网络 ${networkInfo.name} 不受支持,请切换到支持的网络`);
}
// 添加监听器
addListener(callback) {
this.listeners.push(callback);
}
// 移除监听器
removeListener(callback) {
this.listeners = this.listeners.filter(listener => listener !== callback);
}
// 通知所有监听器
notifyListeners(data) {
this.listeners.forEach(callback => {
try {
callback(data);
} catch (error) {
console.error('网络监听器回调错误:', error);
}
});
}
// 获取当前网络
async getCurrentChain() {
if (!this.currentChainId) {
try {
this.currentChainId = await window.ethereum.request({
method: 'eth_chainId'
});
} catch (error) {
console.error('获取当前网络失败:', error);
}
}
return this.currentChainId;
}
// 检查网络是否受支持
isNetworkSupported(chainId = this.currentChainId) {
return this.supportedChains.includes(chainId);
}
// 清理监听器
cleanup() {
if (window.ethereum && window.ethereum.removeListener) {
window.ethereum.removeListener('chainChanged', this.handleChainChange);
}
}
}
// 使用示例
const chainManager = new ChainManager();
chainManager.addListener((data) => {
console.log(`网络从 ${data.previousChainId} 切换到 ${data.newChainId}`);
console.log(`新网络: ${data.networkInfo.name}`);
if (!data.isSupported) {
console.warn('当前网络不受支持');
}
});
监听最佳实践:
What is Personal Sign? How does it differ from regular signatures?
What is Personal Sign? How does it differ from regular signatures?
考察点:签名类型理解。
答案:
个人签名(Personal Sign)是一种用于签名任意文本消息的方法,遵循EIP-191标准。它在消息前添加特定前缀"\x19Ethereum Signed Message:\n",防止签名被恶意用于区块链交易。个人签名主要用于身份验证、登录授权等场景,不涉及资金转移。
个人签名特点:
个人签名实现:
// 请求个人签名
async function personalSign(message, account) {
try {
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, account]
});
return signature;
} catch (error) {
console.error('签名失败:', error);
throw error;
}
}
// 使用示例
const message = "欢迎登录我们的DApp!";
const account = "0x742d35Cc6327C0532...";
// personalSign(message, account) => "0x1234abcd..."
与普通签名的区别:
| 特性 | Personal Sign | eth_sign | Transaction Sign |
|---|---|---|---|
| 用途 | 文本消息签名 | 原始数据签名 | 交易签名 |
| 安全前缀 | 自动添加 | 无前缀 | 无需前缀 |
| 数据格式 | UTF-8文本 | 32字节哈希 | 交易对象 |
| 安全性 | 高(有前缀保护) | 低(易被滥用) | 高(受钱包保护) |
| 应用场景 | 登录、授权 | 开发调试 | 链上交易 |
完整签名验证示例:
class MessageSigner {
constructor() {
this.account = null;
}
// 个人签名
async signMessage(message) {
if (!this.account) {
throw new Error('请先连接钱包');
}
try {
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, this.account]
});
return {
message: message,
signature: signature,
address: this.account,
timestamp: Date.now()
};
} catch (error) {
if (error.code === 4001) {
throw new Error('用户拒绝签名');
}
throw error;
}
}
// 验证签名(需要ethers.js库)
async verifySignature(message, signature, expectedAddress) {
try {
// 恢复签名者地址
const recoveredAddress = ethers.utils.verifyMessage(message, signature);
// 比较地址(忽略大小写)
return recoveredAddress.toLowerCase() === expectedAddress.toLowerCase();
} catch (error) {
console.error('验证签名失败:', error);
return false;
}
}
setAccount(account) {
this.account = account;
}
}
// 使用示例
const signer = new MessageSigner();
signer.setAccount('0x742d35Cc6327C0532...');
const signData = await signer.signMessage('登录验证消息');
const isValid = await signer.verifySignature(
signData.message,
signData.signature,
signData.address
);
console.log('签名验证结果:', isValid);
应用场景:
注意事项:
How to request users to sign a message?
How to request users to sign a message?
考察点:消息签名基础。
答案:
消息签名是通过调用ethereum.request()方法,使用personal_sign或eth_signTypedData等方法实现的。用户确认后,钱包会使用私钥对消息进行签名并返回签名结果。这个过程不消耗Gas费用,主要用于身份验证和授权。
基础消息签名:
// 简单文本签名
async function signMessage(message, account) {
try {
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, account]
});
console.log('签名成功:', signature);
return signature;
} catch (error) {
console.error('签名失败:', error);
throw error;
}
}
// 使用示例
const message = "请签名以验证您的身份";
const userAccount = "0x742d35Cc6327C0532...";
// signMessage(message, userAccount) => "0x1234abcd..."
完整签名管理系统:
class SignatureManager {
constructor() {
this.currentAccount = null;
this.signatureHistory = [];
}
async connectWallet() {
try {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
this.currentAccount = accounts[0];
return this.currentAccount;
} catch (error) {
throw new Error('连接钱包失败');
}
}
// 文本消息签名
async signTextMessage(message) {
if (!this.currentAccount) {
throw new Error('请先连接钱包');
}
try {
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, this.currentAccount]
});
// 记录签名历史
const signRecord = {
type: 'personal_sign',
message: message,
signature: signature,
account: this.currentAccount,
timestamp: Date.now()
};
this.signatureHistory.push(signRecord);
return signRecord;
} catch (error) {
return this.handleSignError(error);
}
}
// 类型化数据签名(EIP-712)
async signTypedData(typedData) {
if (!this.currentAccount) {
throw new Error('请先连接钱包');
}
try {
const signature = await window.ethereum.request({
method: 'eth_signTypedData_v4',
params: [this.currentAccount, JSON.stringify(typedData)]
});
const signRecord = {
type: 'typed_data',
data: typedData,
signature: signature,
account: this.currentAccount,
timestamp: Date.now()
};
this.signatureHistory.push(signRecord);
return signRecord;
} catch (error) {
return this.handleSignError(error);
}
}
// 批量签名(多条消息)
async signMultipleMessages(messages) {
if (!Array.isArray(messages)) {
throw new Error('消息必须是数组格式');
}
const results = [];
for (let i = 0; i < messages.length; i++) {
try {
const result = await this.signTextMessage(messages[i]);
results.push(result);
} catch (error) {
console.error(`签名第${i+1}条消息失败:`, error);
results.push({ error: error.message, message: messages[i] });
}
}
return results;
}
// 处理签名错误
handleSignError(error) {
let errorMessage = '签名失败';
if (error.code === 4001) {
errorMessage = '用户拒绝签名';
} else if (error.code === -32603) {
errorMessage = '签名处理错误';
} else if (error.message) {
errorMessage = error.message;
}
return {
success: false,
error: errorMessage,
code: error.code
};
}
// 获取签名历史
getSignatureHistory() {
return this.signatureHistory;
}
// 清除签名历史
clearSignatureHistory() {
this.signatureHistory = [];
}
// 验证签名格式
isValidSignature(signature) {
// 以太坊签名格式:0x + 130个十六进制字符
const signatureRegex = /^0x[a-fA-F0-9]{130}$/;
return signatureRegex.test(signature);
}
}
// 使用示例
const sigManager = new SignatureManager();
// 连接钱包并签名
await sigManager.connectWallet();
const signResult = await sigManager.signTextMessage('欢迎使用我们的DApp!');
if (signResult.success !== false) {
console.log('签名成功:', signResult.signature);
} else {
console.log('签名失败:', signResult.error);
}
EIP-712类型化数据签名示例:
// EIP-712结构化数据
const typedData = {
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' }
],
LoginMessage: [
{ name: 'user', type: 'address' },
{ name: 'timestamp', type: 'uint256' },
{ name: 'nonce', type: 'string' }
]
},
primaryType: 'LoginMessage',
domain: {
name: 'MyDApp',
version: '1.0',
chainId: 1
},
message: {
user: '0x742d35Cc6327C0532...',
timestamp: Date.now(),
nonce: 'random-nonce-123'
}
};
// 签名类型化数据
const typedSignResult = await sigManager.signTypedData(typedData);
签名应用场景:
How to handle wallet disconnection? How to gracefully manage connection states?
How to handle wallet disconnection? How to gracefully manage connection states?
考察点:连接状态管理。
答案:
钱包断开连接需要及时检测并更新应用状态,清理相关数据,为用户提供重新连接的选项。优雅的连接状态管理包括状态监听、数据清理、UI更新和错误处理等多个方面。
连接状态检测:
// 检测连接状态变化
function setupConnectionMonitor() {
// 监听账户变化(断开时accounts为空)
window.ethereum.on('accountsChanged', (accounts) => {
if (accounts.length === 0) {
handleDisconnection();
} else {
handleReconnection(accounts[0]);
}
});
// 监听连接状态变化
window.ethereum.on('connect', (connectInfo) => {
console.log('钱包已连接:', connectInfo);
handleConnection(connectInfo);
});
window.ethereum.on('disconnect', (error) => {
console.log('钱包已断开:', error);
handleDisconnection();
});
}
完整连接状态管理:
class WalletConnectionManager {
constructor() {
this.isConnected = false;
this.currentAccount = null;
this.chainId = null;
this.listeners = [];
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 3;
this.setupEventListeners();
}
setupEventListeners() {
if (!window.ethereum) return;
// 账户变化监听
window.ethereum.on('accountsChanged', (accounts) => {
this.handleAccountsChanged(accounts);
});
// 网络变化监听
window.ethereum.on('chainChanged', (chainId) => {
this.handleChainChanged(chainId);
});
// 连接状态监听
window.ethereum.on('connect', (connectInfo) => {
this.handleConnect(connectInfo);
});
window.ethereum.on('disconnect', (error) => {
this.handleDisconnect(error);
});
}
// 处理账户变化
handleAccountsChanged(accounts) {
if (accounts.length === 0) {
// 钱包断开或锁定
this.handleDisconnection('WALLET_LOCKED');
} else if (accounts[0] !== this.currentAccount) {
// 账户切换
this.currentAccount = accounts[0];
this.notifyListeners('accountChanged', {
account: accounts[0]
});
}
}
// 处理网络变化
handleChainChanged(chainId) {
this.chainId = chainId;
this.notifyListeners('chainChanged', { chainId });
// 网络变化可能影响连接状态,重新验证
this.validateConnection();
}
// 处理连接
handleConnect(connectInfo) {
this.isConnected = true;
this.chainId = connectInfo.chainId;
this.reconnectAttempts = 0;
this.notifyListeners('connected', connectInfo);
}
// 处理断开
handleDisconnect(error) {
this.handleDisconnection('PROVIDER_DISCONNECT', error);
}
// 统一断开处理
handleDisconnection(reason = 'UNKNOWN', error = null) {
const wasConnected = this.isConnected;
// 清理连接状态
this.isConnected = false;
this.currentAccount = null;
// 清理应用数据
this.clearApplicationData();
// 通知监听器
this.notifyListeners('disconnected', {
reason,
error,
wasConnected
});
// 尝试自动重连(某些情况下)
if (reason === 'PROVIDER_DISCONNECT' && this.shouldAutoReconnect()) {
this.attemptReconnect();
}
}
// 处理重新连接
handleReconnection(account) {
this.currentAccount = account;
this.isConnected = true;
this.reconnectAttempts = 0;
this.notifyListeners('reconnected', {
account: account
});
}
// 清理应用数据
clearApplicationData() {
// 清理缓存的用户数据
localStorage.removeItem('walletConnection');
localStorage.removeItem('userPreferences');
// 清理会话数据
sessionStorage.removeItem('transactionCache');
// 重置应用状态
this.resetApplicationState();
}
// 重置应用状态
resetApplicationState() {
// 这里可以调用应用的状态重置方法
// 例如:Redux store重置、组件状态清理等
console.log('重置应用状态');
}
// 判断是否应该自动重连
shouldAutoReconnect() {
return this.reconnectAttempts < this.maxReconnectAttempts;
}
// 尝试重连
async attemptReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
return false;
}
this.reconnectAttempts++;
try {
await new Promise(resolve => setTimeout(resolve, 2000)); // 等待2秒
const accounts = await window.ethereum.request({
method: 'eth_accounts'
});
if (accounts.length > 0) {
this.handleReconnection(accounts[0]);
return true;
}
} catch (error) {
console.error(`重连尝试 ${this.reconnectAttempts} 失败:`, error);
}
return false;
}
// 手动连接
async connect() {
try {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
if (accounts.length > 0) {
this.currentAccount = accounts[0];
this.isConnected = true;
// 获取当前网络
this.chainId = await window.ethereum.request({
method: 'eth_chainId'
});
this.notifyListeners('connected', {
account: this.currentAccount,
chainId: this.chainId
});
return this.currentAccount;
}
} catch (error) {
this.notifyListeners('connectionError', { error });
throw error;
}
}
// 手动断开
disconnect() {
this.handleDisconnection('USER_DISCONNECT');
}
// 验证连接状态
async validateConnection() {
try {
const accounts = await window.ethereum.request({
method: 'eth_accounts'
});
const isValid = accounts.length > 0 &&
accounts[0] === this.currentAccount;
if (!isValid && this.isConnected) {
this.handleDisconnection('VALIDATION_FAILED');
}
return isValid;
} catch (error) {
console.error('连接验证失败:', error);
return false;
}
}
// 添加事件监听器
addListener(callback) {
this.listeners.push(callback);
}
// 移除事件监听器
removeListener(callback) {
this.listeners = this.listeners.filter(l => l !== callback);
}
// 通知监听器
notifyListeners(event, data) {
this.listeners.forEach(callback => {
try {
callback(event, data);
} catch (error) {
console.error('监听器回调错误:', error);
}
});
}
// 获取连接状态
getConnectionState() {
return {
isConnected: this.isConnected,
currentAccount: this.currentAccount,
chainId: this.chainId,
reconnectAttempts: this.reconnectAttempts
};
}
}
// 使用示例
const connectionManager = new WalletConnectionManager();
connectionManager.addListener((event, data) => {
switch (event) {
case 'connected':
console.log('钱包已连接:', data.account);
break;
case 'disconnected':
console.log('钱包已断开:', data.reason);
break;
case 'reconnected':
console.log('钱包已重连:', data.account);
break;
}
});
用户界面处理:
// UI状态管理
function updateConnectionUI(isConnected, account = null) {
const connectButton = document.getElementById('connectButton');
const accountDisplay = document.getElementById('accountDisplay');
const appContent = document.getElementById('appContent');
if (isConnected && account) {
connectButton.textContent = '断开连接';
accountDisplay.textContent = `已连接: ${account.substring(0, 6)}...${account.substring(38)}`;
appContent.style.display = 'block';
} else {
connectButton.textContent = '连接钱包';
accountDisplay.textContent = '未连接';
appContent.style.display = 'none';
}
}
What is WalletConnect? What problems does it solve?
What is WalletConnect? What problems does it solve?
考察点:WalletConnect协议理解。
答案:
WalletConnect是一个开放协议,通过扫码或深链接的方式连接桌面DApp与移动端钱包。它解决了移动端钱包与桌面浏览器DApp之间的连接问题,使用户可以在桌面使用DApp的同时,通过手机钱包安全地管理私钥和签署交易。
WalletConnect工作原理:
解决的核心问题:
WalletConnect集成实现:
import WalletConnect from '@walletconnect/client';
import QRCodeModal from '@walletconnect/qrcode-modal';
class WalletConnectManager {
constructor() {
this.connector = null;
this.connected = false;
this.accounts = [];
this.chainId = null;
}
// 初始化WalletConnect
async initWalletConnect() {
try {
// 创建连接器
this.connector = new WalletConnect({
bridge: 'https://bridge.walletconnect.org',
qrcodeModal: QRCodeModal
});
// 设置事件监听
this.setupEventListeners();
// 检查是否已连接
if (this.connector.connected) {
this.onConnect();
}
return this.connector;
} catch (error) {
console.error('WalletConnect初始化失败:', error);
throw error;
}
}
// 设置事件监听器
setupEventListeners() {
// 连接成功
this.connector.on('connect', (error, payload) => {
if (error) {
console.error('连接错误:', error);
return;
}
this.onConnect(payload);
});
// 会话更新
this.connector.on('session_update', (error, payload) => {
if (error) {
console.error('会话更新错误:', error);
return;
}
this.onSessionUpdate(payload);
});
// 断开连接
this.connector.on('disconnect', (error, payload) => {
if (error) {
console.error('断开连接错误:', error);
}
this.onDisconnect();
});
}
// 创建连接会话
async connect() {
try {
if (!this.connector) {
await this.initWalletConnect();
}
// 如果已连接,直接返回
if (this.connector.connected) {
return this.getConnectionInfo();
}
// 创建新会话
await this.connector.createSession();
return new Promise((resolve, reject) => {
// 监听连接结果
this.connector.on('connect', (error, payload) => {
if (error) {
reject(error);
} else {
resolve(this.getConnectionInfo());
}
});
// 设置超时
setTimeout(() => {
if (!this.connected) {
reject(new Error('连接超时'));
}
}, 30000);
});
} catch (error) {
console.error('WalletConnect连接失败:', error);
throw error;
}
}
// 处理连接成功
onConnect(payload) {
const { accounts, chainId } = payload?.params[0] || this.connector;
this.connected = true;
this.accounts = accounts;
this.chainId = chainId;
console.log('WalletConnect连接成功:', {
accounts: this.accounts,
chainId: this.chainId
});
}
// 处理会话更新
onSessionUpdate(payload) {
const { accounts, chainId } = payload.params[0];
this.accounts = accounts;
this.chainId = chainId;
console.log('WalletConnect会话更新:', {
accounts: this.accounts,
chainId: this.chainId
});
}
// 处理断开连接
onDisconnect() {
this.connected = false;
this.accounts = [];
this.chainId = null;
console.log('WalletConnect已断开连接');
}
// 发送交易
async sendTransaction(transaction) {
if (!this.connected) {
throw new Error('WalletConnect未连接');
}
try {
const result = await this.connector.sendTransaction(transaction);
return result;
} catch (error) {
console.error('发送交易失败:', error);
throw error;
}
}
// 签名消息
async signMessage(message) {
if (!this.connected) {
throw new Error('WalletConnect未连接');
}
try {
const msgParams = {
data: message,
from: this.accounts[0]
};
const result = await this.connector.signPersonalMessage(msgParams);
return result;
} catch (error) {
console.error('签名失败:', error);
throw error;
}
}
// 断开连接
async disconnect() {
if (this.connector && this.connected) {
await this.connector.killSession();
}
this.onDisconnect();
}
// 获取连接信息
getConnectionInfo() {
return {
connected: this.connected,
accounts: this.accounts,
chainId: this.chainId,
connector: this.connector
};
}
}
// 使用示例
const walletConnect = new WalletConnectManager();
// 连接钱包
async function connectWalletConnect() {
try {
const connectionInfo = await walletConnect.connect();
console.log('连接成功:', connectionInfo);
} catch (error) {
console.error('连接失败:', error);
}
}
WalletConnect v2.0 新特性:
// WalletConnect v2.0 (Sign API)
import { SignClient } from '@walletconnect/sign-client';
class WalletConnectV2Manager {
constructor() {
this.signClient = null;
this.session = null;
}
async init() {
this.signClient = await SignClient.init({
projectId: 'YOUR_PROJECT_ID', // 从WalletConnect Cloud获取
metadata: {
name: 'My DApp',
description: 'My DApp Description',
url: 'https://mydapp.com',
icons: ['https://mydapp.com/icon.png']
}
});
}
async connect() {
const { uri, approval } = await this.signClient.connect({
requiredNamespaces: {
eip155: {
methods: ['eth_sendTransaction', 'personal_sign'],
chains: ['eip155:1'],
events: ['accountsChanged', 'chainChanged']
}
}
});
if (uri) {
QRCodeModal.open(uri, () => {
console.log('用户关闭了二维码弹窗');
});
}
this.session = await approval();
QRCodeModal.close();
return this.session;
}
}
WalletConnect优势:
How do mobile DApps interact with wallet applications?
How do mobile DApps interact with wallet applications?
考察点:移动端钱包集成。
答案:
移动端DApp与钱包应用的交互主要通过深链接(Deep Link)、应用内浏览器和WalletConnect协议实现。由于移动端浏览器限制,无法像桌面端那样直接注入ethereum对象,需要采用特殊的交互方式来实现钱包连接和交易签名。
主要交互方式:
1. 深链接(Deep Link):
// 构建深链接URL
function buildDeepLink(walletScheme, dappUrl, action) {
const encodedUrl = encodeURIComponent(dappUrl);
return `${walletScheme}://wc?uri=${encodedUrl}&action=${action}`;
}
// 跳转到钱包应用
function openWalletApp(walletName, params) {
const walletSchemes = {
metamask: 'metamask',
trust: 'trust',
coinbase: 'cbwallet',
imtoken: 'imtokenv2'
};
const scheme = walletSchemes[walletName];
if (!scheme) {
throw new Error('不支持的钱包');
}
const deepLink = buildDeepLink(scheme, window.location.href, 'connect');
window.location.href = deepLink;
}
// 检测移动端环境
function isMobile() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
);
}
2. WalletConnect集成:
class MobileWalletConnector {
constructor() {
this.connector = null;
this.isMobile = this.detectMobile();
}
detectMobile() {
return window.innerWidth <= 768 || /Mobi|Android/i.test(navigator.userAgent);
}
async connectWallet() {
if (this.isMobile) {
return await this.connectMobile();
} else {
return await this.connectDesktop();
}
}
async connectMobile() {
// 移动端优先使用WalletConnect
const { WalletConnect } = await import('@walletconnect/client');
this.connector = new WalletConnect({
bridge: 'https://bridge.walletconnect.org',
qrcodeModal: {
open: (uri) => {
// 移动端直接打开钱包应用
this.openMobileWallet(uri);
},
close: () => {
console.log('关闭连接');
}
}
});
if (this.connector.connected) {
return this.connector;
}
await this.connector.createSession();
return this.connector;
}
openMobileWallet(uri) {
// 尝试打开不同的钱包应用
const walletLinks = [
`metamask://wc?uri=${encodeURIComponent(uri)}`,
`trust://wc?uri=${encodeURIComponent(uri)}`,
`rainbow://wc?uri=${encodeURIComponent(uri)}`,
`imtokenv2://wc?uri=${encodeURIComponent(uri)}`
];
// 依次尝试打开钱包
walletLinks.forEach((link, index) => {
setTimeout(() => {
window.location.href = link;
}, index * 100);
});
}
}
3. 应用内浏览器检测:
// 检测钱包内置浏览器
function detectWalletBrowser() {
const userAgent = navigator.userAgent.toLowerCase();
if (userAgent.includes('metamask')) {
return 'metamask';
} else if (userAgent.includes('trust')) {
return 'trust';
} else if (userAgent.includes('coinbase')) {
return 'coinbase';
} else if (userAgent.includes('imtoken')) {
return 'imtoken';
}
return null;
}
// 在钱包浏览器中直接使用注入的对象
async function connectInWalletBrowser() {
const walletType = detectWalletBrowser();
if (walletType && window.ethereum) {
// 钱包内置浏览器,直接使用ethereum对象
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
return accounts[0];
} else {
// 普通浏览器,使用WalletConnect
throw new Error('请在钱包应用中打开或使用WalletConnect');
}
}
How to handle situations where users reject wallet requests?
How to handle situations where users reject wallet requests?
考察点:异常处理基础。
答案:
用户拒绝钱包请求是常见情况,需要优雅处理并提供合适的用户反馈。不同类型的拒绝有不同的错误码和处理方式,应该根据具体情况给出相应的提示和后续操作建议。
错误类型和处理:
class WalletErrorHandler {
constructor() {
this.errorTypes = {
4001: 'USER_REJECTED',
4100: 'UNAUTHORIZED',
4200: 'UNSUPPORTED_METHOD',
4900: 'DISCONNECTED',
-32002: 'PENDING_REQUEST',
-32603: 'INTERNAL_ERROR'
};
}
handleError(error, requestType) {
const errorCode = error.code;
const errorType = this.errorTypes[errorCode] || 'UNKNOWN_ERROR';
console.error(`钱包请求失败 [${requestType}]:`, error);
switch (errorType) {
case 'USER_REJECTED':
return this.handleUserRejection(requestType, error);
case 'UNAUTHORIZED':
return this.handleUnauthorized(requestType);
case 'PENDING_REQUEST':
return this.handlePendingRequest(requestType);
case 'DISCONNECTED':
return this.handleDisconnected();
default:
return this.handleGenericError(error, requestType);
}
}
// 处理用户拒绝
handleUserRejection(requestType, error) {
const messages = {
'connect': '连接被拒绝,请重新尝试连接钱包',
'transaction': '交易被取消,您可以稍后重新发起交易',
'sign': '签名被拒绝,无法完成身份验证',
'network': '网络切换被拒绝,某些功能可能无法使用',
'addToken': '添加代币被拒绝,您可以手动添加代币'
};
const message = messages[requestType] || '操作被用户拒绝';
return {
success: false,
error: 'USER_REJECTED',
message: message,
canRetry: true,
userAction: 'reject'
};
}
// 处理未授权
handleUnauthorized(requestType) {
return {
success: false,
error: 'UNAUTHORIZED',
message: '请先连接钱包后再进行操作',
canRetry: true,
suggestedAction: 'connect'
};
}
// 处理待处理请求
handlePendingRequest(requestType) {
return {
success: false,
error: 'PENDING_REQUEST',
message: '已有请求正在处理中,请检查钱包或稍后重试',
canRetry: true,
retryDelay: 3000
};
}
// 处理断开连接
handleDisconnected() {
return {
success: false,
error: 'DISCONNECTED',
message: '钱包连接已断开,请重新连接',
canRetry: true,
suggestedAction: 'reconnect'
};
}
// 处理通用错误
handleGenericError(error, requestType) {
return {
success: false,
error: 'UNKNOWN_ERROR',
message: `操作失败: ${error.message || '未知错误'}`,
canRetry: true,
originalError: error
};
}
}
// 使用示例
const errorHandler = new WalletErrorHandler();
async function safeWalletRequest(requestFunction, requestType) {
try {
const result = await requestFunction();
return { success: true, data: result };
} catch (error) {
return errorHandler.handleError(error, requestType);
}
}
// 安全的连接请求
async function connectWalletSafely() {
return safeWalletRequest(async () => {
return await window.ethereum.request({
method: 'eth_requestAccounts'
});
}, 'connect');
}
// 使用示例
const connectResult = await connectWalletSafely();
if (connectResult.success) {
console.log('连接成功:', connectResult.data);
} else {
console.log('连接失败:', connectResult.message);
if (connectResult.canRetry) {
// 显示重试按钮
showRetryButton(connectResult);
}
}
用户界面处理:
// UI反馈处理
class WalletUIHandler {
showError(errorResult) {
const errorContainer = document.getElementById('error-message');
const retryButton = document.getElementById('retry-button');
// 显示错误消息
errorContainer.textContent = errorResult.message;
errorContainer.className = `error-message ${errorResult.error.toLowerCase()}`;
// 显示重试按钮
if (errorResult.canRetry) {
retryButton.style.display = 'block';
retryButton.onclick = () => this.handleRetry(errorResult);
}
// 自动隐藏消息
setTimeout(() => {
errorContainer.textContent = '';
errorContainer.className = '';
}, 5000);
}
handleRetry(errorResult) {
if (errorResult.retryDelay) {
setTimeout(() => {
this.performRetry(errorResult);
}, errorResult.retryDelay);
} else {
this.performRetry(errorResult);
}
}
performRetry(errorResult) {
switch (errorResult.suggestedAction) {
case 'connect':
connectWalletSafely();
break;
case 'reconnect':
reconnectWallet();
break;
default:
// 重新执行原始操作
console.log('重试操作');
}
}
}
What are the basic security considerations in wallet integration development?
What are the basic security considerations in wallet integration development?
考察点:安全意识基础。
答案:
钱包集成安全是Web3开发的核心,涉及私钥保护、交易验证、网络安全等多个方面。开发者需要从多个维度确保用户资产和数据安全,防范各种潜在攻击。
核心安全原则:
1. 私钥和助记词安全:
// 永远不要在代码中处理私钥
class SecurityManager {
constructor() {
this.securityRules = {
// 禁止的操作
forbidden: [
'requestPrivateKey',
'storePrivateKey',
'transmitPrivateKey'
]
};
}
// 检查是否安全的钱包操作
isSecureWalletOperation(operation) {
// 只允许通过钱包接口进行操作
const allowedMethods = [
'eth_requestAccounts',
'eth_sendTransaction',
'personal_sign',
'eth_signTypedData_v4'
];
return allowedMethods.includes(operation);
}
// 验证钱包连接的安全性
validateWalletSecurity() {
const checks = {
hasEthereum: typeof window.ethereum !== 'undefined',
isSecureContext: window.isSecureContext, // HTTPS
noPrivateKeyAccess: !this.hasPrivateKeyAccess(),
trustedOrigin: this.isTrustedOrigin()
};
return Object.values(checks).every(check => check === true);
}
hasPrivateKeyAccess() {
// 检查是否有私钥访问(这是危险的)
return window.ethereum?.privateKey !== undefined;
}
isTrustedOrigin() {
// 验证应用运行在受信任的域名
const trustedDomains = ['localhost', 'yourdapp.com'];
const currentDomain = window.location.hostname;
return trustedDomains.includes(currentDomain);
}
}
2. 交易验证和确认:
// 交易安全验证
class TransactionSecurity {
// 验证交易参数
validateTransaction(transaction) {
const errors = [];
// 检查必需字段
if (!transaction.to) {
errors.push('缺少接收地址');
}
// 验证地址格式
if (transaction.to && !this.isValidAddress(transaction.to)) {
errors.push('接收地址格式无效');
}
// 检查金额合理性
if (transaction.value) {
const value = parseFloat(transaction.value);
if (value < 0) {
errors.push('交易金额不能为负数');
}
if (value > 1000) { // 大额交易警告
errors.push('大额交易,请仔细确认');
}
}
// 验证Gas设置
if (transaction.gasLimit && parseInt(transaction.gasLimit) < 21000) {
errors.push('Gas限制过低,交易可能失败');
}
return {
isValid: errors.length === 0,
errors: errors
};
}
isValidAddress(address) {
// 以太坊地址验证
return /^0x[a-fA-F0-9]{40}$/.test(address);
}
// 显示交易确认界面
showTransactionConfirmation(transaction) {
const validation = this.validateTransaction(transaction);
if (!validation.isValid) {
throw new Error(`交易验证失败: ${validation.errors.join(', ')}`);
}
// 显示交易详情供用户确认
return this.displayTransactionDetails(transaction);
}
displayTransactionDetails(transaction) {
return {
to: transaction.to,
value: `${transaction.value} ETH`,
gasLimit: transaction.gasLimit,
gasPrice: transaction.gasPrice,
estimatedFee: this.calculateFee(transaction)
};
}
calculateFee(transaction) {
const gasPrice = parseInt(transaction.gasPrice || '20000000000'); // 20 Gwei
const gasLimit = parseInt(transaction.gasLimit || '21000');
return (gasPrice * gasLimit) / 1e18; // 转换为ETH
}
}
3. 网络和合约安全:
// 网络安全管理
class NetworkSecurity {
constructor() {
this.trustedNetworks = {
'0x1': 'Ethereum Mainnet',
'0x89': 'Polygon Mainnet',
'0x38': 'BSC Mainnet'
};
this.knownScamContracts = new Set([
// 已知的恶意合约地址
'0xScamContract1...',
'0xScamContract2...'
]);
}
// 验证网络安全性
async validateNetwork(chainId) {
// 检查是否为受信任网络
if (!this.trustedNetworks[chainId]) {
return {
safe: false,
warning: '未知网络,请谨慎操作'
};
}
// 获取当前网络状态
const networkStatus = await this.getNetworkStatus(chainId);
return {
safe: networkStatus.healthy,
networkName: this.trustedNetworks[chainId],
status: networkStatus
};
}
// 检查合约安全性
async validateContract(contractAddress) {
// 检查黑名单
if (this.knownScamContracts.has(contractAddress.toLowerCase())) {
return {
safe: false,
risk: 'HIGH',
reason: '已知恶意合约'
};
}
// 验证合约代码
const contractCode = await this.getContractCode(contractAddress);
if (contractCode === '0x') {
return {
safe: false,
risk: 'MEDIUM',
reason: '目标地址不是合约'
};
}
return {
safe: true,
risk: 'LOW',
verified: await this.isVerifiedContract(contractAddress)
};
}
async getContractCode(address) {
try {
return await window.ethereum.request({
method: 'eth_getCode',
params: [address, 'latest']
});
} catch (error) {
console.error('获取合约代码失败:', error);
return '0x';
}
}
async getNetworkStatus(chainId) {
// 模拟网络健康检查
return {
healthy: true,
latency: '< 100ms',
lastBlock: Date.now()
};
}
async isVerifiedContract(address) {
// 检查合约是否在Etherscan等平台验证
// 这里需要调用相应的API
return true; // 简化示例
}
}
4. 用户数据保护:
// 数据保护措施
class DataProtection {
constructor() {
this.sensitiveFields = [
'privateKey', 'mnemonic', 'seed',
'password', 'pin', 'secret'
];
}
// 清理敏感数据
sanitizeData(data) {
const cleaned = { ...data };
this.sensitiveFields.forEach(field => {
if (cleaned[field]) {
delete cleaned[field];
}
});
return cleaned;
}
// 安全存储
secureStore(key, data, temporary = false) {
const sanitizedData = this.sanitizeData(data);
const storage = temporary ? sessionStorage : localStorage;
try {
storage.setItem(key, JSON.stringify(sanitizedData));
} catch (error) {
console.error('存储失败:', error);
}
}
// 清理存储
clearSensitiveStorage() {
// 清理可能包含敏感信息的存储
const keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (this.isSensitiveKey(key)) {
keysToRemove.push(key);
}
}
keysToRemove.forEach(key => {
localStorage.removeItem(key);
});
}
isSensitiveKey(key) {
return this.sensitiveFields.some(field =>
key.toLowerCase().includes(field)
);
}
}
安全最佳实践清单:
How to implement multi-wallet support? Design a universal wallet adapter.
How to implement multi-wallet support? Design a universal wallet adapter.
考察点:架构设计能力。
答案:
多钱包支持需要设计一个统一的适配器架构,将不同钱包的API差异抽象成通用接口。通过适配器模式和策略模式,可以实现对MetaMask、WalletConnect、Coinbase Wallet等多种钱包的统一管理,为上层应用提供一致的调用方式。
核心架构设计:
// 钱包接口定义
interface IWalletAdapter {
name: string;
connect(): Promise<string[]>;
disconnect(): Promise<void>;
getAccounts(): Promise<string[]>;
signMessage(message: string): Promise<string>;
sendTransaction(transaction: any): Promise<string>;
switchNetwork(chainId: string): Promise<boolean>;
addNetwork(network: any): Promise<boolean>;
isConnected(): boolean;
}
// 基础适配器抽象类
abstract class BaseWalletAdapter {
protected name: string;
protected connected: boolean = false;
protected accounts: string[] = [];
protected chainId: string | null = null;
protected listeners: Map<string, Function[]> = new Map();
constructor(name: string) {
this.name = name;
}
// 抽象方法,需要子类实现
abstract connect(): Promise<string[]>;
abstract disconnect(): Promise<void>;
abstract signMessage(message: string): Promise<string>;
abstract sendTransaction(transaction: any): Promise<string>;
// 通用事件管理
on(event: string, listener: Function) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event)!.push(listener);
}
off(event: string, listener: Function) {
const eventListeners = this.listeners.get(event);
if (eventListeners) {
const index = eventListeners.indexOf(listener);
if (index > -1) {
eventListeners.splice(index, 1);
}
}
}
protected emit(event: string, data: any) {
const eventListeners = this.listeners.get(event);
if (eventListeners) {
eventListeners.forEach(listener => listener(data));
}
}
isConnected(): boolean {
return this.connected;
}
getAccounts(): Promise<string[]> {
return Promise.resolve(this.accounts);
}
getCurrentChain(): string | null {
return this.chainId;
}
}
MetaMask适配器实现:
class MetaMaskAdapter extends BaseWalletAdapter {
private provider: any;
constructor() {
super('MetaMask');
this.provider = window.ethereum;
this.setupEventListeners();
}
static isAvailable(): boolean {
return typeof window.ethereum !== 'undefined' &&
window.ethereum.isMetaMask;
}
private setupEventListeners() {
if (!this.provider) return;
this.provider.on('accountsChanged', (accounts: string[]) => {
this.accounts = accounts;
this.connected = accounts.length > 0;
this.emit('accountsChanged', accounts);
});
this.provider.on('chainChanged', (chainId: string) => {
this.chainId = chainId;
this.emit('chainChanged', chainId);
});
this.provider.on('disconnect', () => {
this.connected = false;
this.accounts = [];
this.emit('disconnect', {});
});
}
async connect(): Promise<string[]> {
if (!this.provider) {
throw new Error('MetaMask not installed');
}
try {
const accounts = await this.provider.request({
method: 'eth_requestAccounts'
});
this.accounts = accounts;
this.connected = accounts.length > 0;
// 获取当前网络
this.chainId = await this.provider.request({
method: 'eth_chainId'
});
this.emit('connect', { accounts, chainId: this.chainId });
return accounts;
} catch (error) {
throw new Error(`MetaMask connection failed: ${error.message}`);
}
}
async disconnect(): Promise<void> {
this.connected = false;
this.accounts = [];
this.chainId = null;
this.emit('disconnect', {});
}
async signMessage(message: string): Promise<string> {
if (!this.connected || this.accounts.length === 0) {
throw new Error('Wallet not connected');
}
return await this.provider.request({
method: 'personal_sign',
params: [message, this.accounts[0]]
});
}
async sendTransaction(transaction: any): Promise<string> {
if (!this.connected) {
throw new Error('Wallet not connected');
}
return await this.provider.request({
method: 'eth_sendTransaction',
params: [transaction]
});
}
async switchNetwork(chainId: string): Promise<boolean> {
try {
await this.provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId }]
});
return true;
} catch (error) {
if (error.code === 4902) {
// Network not added
return false;
}
throw error;
}
}
async addNetwork(network: any): Promise<boolean> {
try {
await this.provider.request({
method: 'wallet_addEthereumChain',
params: [network]
});
return true;
} catch (error) {
console.error('Failed to add network:', error);
return false;
}
}
}
WalletConnect适配器实现:
class WalletConnectAdapter extends BaseWalletAdapter {
private connector: any;
constructor() {
super('WalletConnect');
}
static isAvailable(): boolean {
// WalletConnect is always available as it's a protocol
return true;
}
async connect(): Promise<string[]> {
try {
// 动态导入WalletConnect
const { WalletConnect } = await import('@walletconnect/client');
const { QRCodeModal } = await import('@walletconnect/qrcode-modal');
this.connector = new WalletConnect({
bridge: 'https://bridge.walletconnect.org',
qrcodeModal: QRCodeModal
});
this.setupWalletConnectListeners();
if (this.connector.connected) {
this.accounts = this.connector.accounts;
this.chainId = `0x${this.connector.chainId.toString(16)}`;
this.connected = true;
return this.accounts;
}
// Create new session
await this.connector.createSession();
return new Promise((resolve, reject) => {
this.connector.on('connect', (error: any, payload: any) => {
if (error) {
reject(error);
return;
}
const { accounts, chainId } = payload.params[0];
this.accounts = accounts;
this.chainId = `0x${chainId.toString(16)}`;
this.connected = true;
this.emit('connect', { accounts, chainId: this.chainId });
resolve(accounts);
});
});
} catch (error) {
throw new Error(`WalletConnect connection failed: ${error.message}`);
}
}
private setupWalletConnectListeners() {
this.connector.on('session_update', (error: any, payload: any) => {
if (error) return;
const { accounts, chainId } = payload.params[0];
this.accounts = accounts;
this.chainId = `0x${chainId.toString(16)}`;
this.emit('accountsChanged', accounts);
this.emit('chainChanged', this.chainId);
});
this.connector.on('disconnect', () => {
this.connected = false;
this.accounts = [];
this.chainId = null;
this.emit('disconnect', {});
});
}
async disconnect(): Promise<void> {
if (this.connector) {
await this.connector.killSession();
}
this.connected = false;
this.accounts = [];
this.chainId = null;
this.emit('disconnect', {});
}
async signMessage(message: string): Promise<string> {
if (!this.connected) {
throw new Error('Wallet not connected');
}
const msgParams = {
data: message,
from: this.accounts[0]
};
return await this.connector.signPersonalMessage(msgParams);
}
async sendTransaction(transaction: any): Promise<string> {
if (!this.connected) {
throw new Error('Wallet not connected');
}
return await this.connector.sendTransaction(transaction);
}
async switchNetwork(chainId: string): Promise<boolean> {
// WalletConnect doesn't support network switching directly
// This would need to be handled by the connected wallet
console.warn('Network switching not supported in WalletConnect');
return false;
}
async addNetwork(network: any): Promise<boolean> {
console.warn('Adding network not supported in WalletConnect');
return false;
}
}
通用钱包管理器:
class MultiWalletManager {
private adapters: Map<string, BaseWalletAdapter> = new Map();
private currentAdapter: BaseWalletAdapter | null = null;
private listeners: Map<string, Function[]> = new Map();
constructor() {
this.registerAdapters();
}
private registerAdapters() {
// 注册可用的钱包适配器
if (MetaMaskAdapter.isAvailable()) {
this.adapters.set('metamask', new MetaMaskAdapter());
}
// WalletConnect is always available
this.adapters.set('walletconnect', new WalletConnectAdapter());
// Register other wallet adapters...
this.setupAdapterListeners();
}
private setupAdapterListeners() {
this.adapters.forEach((adapter) => {
adapter.on('connect', (data) => {
this.currentAdapter = adapter;
this.emit('walletConnected', {
wallet: adapter.name,
...data
});
});
adapter.on('disconnect', (data) => {
if (this.currentAdapter === adapter) {
this.currentAdapter = null;
}
this.emit('walletDisconnected', {
wallet: adapter.name,
...data
});
});
adapter.on('accountsChanged', (accounts) => {
this.emit('accountsChanged', {
wallet: adapter.name,
accounts
});
});
adapter.on('chainChanged', (chainId) => {
this.emit('chainChanged', {
wallet: adapter.name,
chainId
});
});
});
}
// 获取可用钱包列表
getAvailableWallets(): string[] {
return Array.from(this.adapters.keys());
}
// 连接指定钱包
async connect(walletName: string): Promise<string[]> {
const adapter = this.adapters.get(walletName);
if (!adapter) {
throw new Error(`Wallet ${walletName} not available`);
}
try {
const accounts = await adapter.connect();
this.currentAdapter = adapter;
return accounts;
} catch (error) {
throw new Error(`Failed to connect to ${walletName}: ${error.message}`);
}
}
// 断开当前钱包
async disconnect(): Promise<void> {
if (this.currentAdapter) {
await this.currentAdapter.disconnect();
this.currentAdapter = null;
}
}
// 获取当前连接的钱包
getCurrentWallet(): string | null {
return this.currentAdapter?.name || null;
}
// 获取账户
async getAccounts(): Promise<string[]> {
if (!this.currentAdapter) {
throw new Error('No wallet connected');
}
return await this.currentAdapter.getAccounts();
}
// 签名消息
async signMessage(message: string): Promise<string> {
if (!this.currentAdapter) {
throw new Error('No wallet connected');
}
return await this.currentAdapter.signMessage(message);
}
// 发送交易
async sendTransaction(transaction: any): Promise<string> {
if (!this.currentAdapter) {
throw new Error('No wallet connected');
}
return await this.currentAdapter.sendTransaction(transaction);
}
// 切换网络
async switchNetwork(chainId: string): Promise<boolean> {
if (!this.currentAdapter) {
throw new Error('No wallet connected');
}
return await this.currentAdapter.switchNetwork(chainId);
}
// 事件监听
on(event: string, listener: Function) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event)!.push(listener);
}
off(event: string, listener: Function) {
const eventListeners = this.listeners.get(event);
if (eventListeners) {
const index = eventListeners.indexOf(listener);
if (index > -1) {
eventListeners.splice(index, 1);
}
}
}
private emit(event: string, data: any) {
const eventListeners = this.listeners.get(event);
if (eventListeners) {
eventListeners.forEach(listener => listener(data));
}
}
// 检查连接状态
isConnected(): boolean {
return this.currentAdapter?.isConnected() || false;
}
// 获取当前网络
getCurrentChain(): string | null {
return this.currentAdapter?.getCurrentChain() || null;
}
}
// 使用示例
const walletManager = new MultiWalletManager();
// 设置事件监听
walletManager.on('walletConnected', (data) => {
console.log(`${data.wallet} connected:`, data.accounts);
});
walletManager.on('walletDisconnected', (data) => {
console.log(`${data.wallet} disconnected`);
});
// 连接钱包
async function connectWallet(walletType: string) {
try {
const accounts = await walletManager.connect(walletType);
console.log('Connected accounts:', accounts);
} catch (error) {
console.error('Connection failed:', error.message);
}
}
// 获取可用钱包
const availableWallets = walletManager.getAvailableWallets();
console.log('Available wallets:', availableWallets); // ['metamask', 'walletconnect']
架构优势:
How should wallet connection state be managed in the application?
How should wallet connection state be managed in the application?
考察点:状态管理最佳实践。
答案:
钱包连接状态管理是Web3应用的核心,需要在全局范围内维护连接状态、账户信息、网络状态等。通过状态管理库(如Redux、Zustand)或Context API,可以实现状态的集中管理和响应式更新,确保UI与钱包状态保持同步。
核心状态结构设计:
// 钱包状态接口定义
interface WalletState {
// 连接状态
isConnected: boolean;
isConnecting: boolean;
connectionError: string | null;
// 钱包信息
walletType: string | null;
accounts: string[];
currentAccount: string | null;
// 网络信息
chainId: string | null;
networkName: string | null;
isWrongNetwork: boolean;
// 余额信息
balance: string | null;
tokenBalances: Record<string, string>;
// 交易状态
pendingTransactions: Transaction[];
// 用户偏好
autoConnect: boolean;
preferredWallet: string | null;
}
interface Transaction {
hash: string;
status: 'pending' | 'confirmed' | 'failed';
type: string;
timestamp: number;
from: string;
to: string;
value: string;
}
使用Zustand的状态管理实现:
import { create } from 'zustand';
import { persist, subscribeWithSelector } from 'zustand/middleware';
// 钱包状态管理
interface WalletStore extends WalletState {
// Actions
setConnectionState: (state: Partial<WalletState>) => void;
connectWallet: (walletType: string) => Promise<void>;
disconnectWallet: () => Promise<void>;
updateAccount: (account: string) => void;
updateChain: (chainId: string) => void;
updateBalance: (balance: string) => void;
addTransaction: (transaction: Transaction) => void;
updateTransaction: (hash: string, updates: Partial<Transaction>) => void;
setError: (error: string | null) => void;
clearError: () => void;
reset: () => void;
}
const useWalletStore = create<WalletStore>()(
subscribeWithSelector(
persist(
(set, get) => ({
// 初始状态
isConnected: false,
isConnecting: false,
connectionError: null,
walletType: null,
accounts: [],
currentAccount: null,
chainId: null,
networkName: null,
isWrongNetwork: false,
balance: null,
tokenBalances: {},
pendingTransactions: [],
autoConnect: false,
preferredWallet: null,
// Actions
setConnectionState: (state) => {
set((prevState) => ({
...prevState,
...state
}));
},
connectWallet: async (walletType: string) => {
const { setConnectionState, setError, updateAccount, updateChain } = get();
try {
setConnectionState({
isConnecting: true,
connectionError: null
});
// 使用钱包管理器连接
const accounts = await walletManager.connect(walletType);
if (accounts.length > 0) {
const chainId = await walletManager.getCurrentChain();
setConnectionState({
isConnected: true,
isConnecting: false,
walletType: walletType,
accounts: accounts,
currentAccount: accounts[0],
chainId: chainId,
preferredWallet: walletType
});
// 更新账户相关信息
updateAccount(accounts[0]);
if (chainId) {
updateChain(chainId);
}
}
} catch (error) {
setError(error.message);
setConnectionState({
isConnecting: false,
connectionError: error.message
});
}
},
disconnectWallet: async () => {
try {
await walletManager.disconnect();
set({
isConnected: false,
isConnecting: false,
connectionError: null,
walletType: null,
accounts: [],
currentAccount: null,
chainId: null,
networkName: null,
balance: null,
tokenBalances: {},
pendingTransactions: []
});
} catch (error) {
console.error('Disconnect error:', error);
}
},
updateAccount: async (account: string) => {
set({ currentAccount: account });
// 获取余额
try {
const balance = await getAccountBalance(account);
set({ balance });
} catch (error) {
console.error('Failed to get balance:', error);
}
},
updateChain: (chainId: string) => {
const networkInfo = getNetworkInfo(chainId);
const supportedChains = ['0x1', '0x89', '0x38']; // ETH, Polygon, BSC
set({
chainId,
networkName: networkInfo.name,
isWrongNetwork: !supportedChains.includes(chainId)
});
},
updateBalance: (balance: string) => {
set({ balance });
},
addTransaction: (transaction: Transaction) => {
set((state) => ({
pendingTransactions: [...state.pendingTransactions, transaction]
}));
},
updateTransaction: (hash: string, updates: Partial<Transaction>) => {
set((state) => ({
pendingTransactions: state.pendingTransactions.map(tx =>
tx.hash === hash ? { ...tx, ...updates } : tx
)
}));
},
setError: (error: string | null) => {
set({ connectionError: error });
},
clearError: () => {
set({ connectionError: null });
},
reset: () => {
set({
isConnected: false,
isConnecting: false,
connectionError: null,
walletType: null,
accounts: [],
currentAccount: null,
chainId: null,
networkName: null,
isWrongNetwork: false,
balance: null,
tokenBalances: {},
pendingTransactions: []
});
}
}),
{
name: 'wallet-storage',
partialize: (state) => ({
// 只持久化部分状态
autoConnect: state.autoConnect,
preferredWallet: state.preferredWallet
})
}
)
)
);
// 网络信息辅助函数
function getNetworkInfo(chainId: string) {
const networks = {
'0x1': { name: 'Ethereum Mainnet', symbol: 'ETH' },
'0x89': { name: 'Polygon', symbol: 'MATIC' },
'0x38': { name: 'BSC', symbol: 'BNB' }
};
return networks[chainId] || { name: 'Unknown Network', symbol: 'Unknown' };
}
// 获取账户余额
async function getAccountBalance(account: string): Promise<string> {
try {
const balance = await window.ethereum.request({
method: 'eth_getBalance',
params: [account, 'latest']
});
// 转换为ETH单位
return (parseInt(balance, 16) / 1e18).toFixed(4);
} catch (error) {
console.error('Get balance error:', error);
return '0';
}
}
钱包事件监听和状态同步:
// 钱包事件监听器
class WalletEventHandler {
private store: any;
private unsubscribers: Function[] = [];
constructor(store: any) {
this.store = store;
this.setupEventListeners();
}
setupEventListeners() {
// 监听钱包管理器事件
walletManager.on('walletConnected', this.handleWalletConnected.bind(this));
walletManager.on('walletDisconnected', this.handleWalletDisconnected.bind(this));
walletManager.on('accountsChanged', this.handleAccountsChanged.bind(this));
walletManager.on('chainChanged', this.handleChainChanged.bind(this));
// 监听页面可见性变化
document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
// 监听网络状态
window.addEventListener('online', this.handleNetworkOnline.bind(this));
window.addEventListener('offline', this.handleNetworkOffline.bind(this));
}
handleWalletConnected(data: any) {
const { setConnectionState, updateAccount, updateChain } = this.store.getState();
setConnectionState({
isConnected: true,
isConnecting: false,
walletType: data.wallet,
accounts: data.accounts,
currentAccount: data.accounts[0],
connectionError: null
});
if (data.accounts[0]) {
updateAccount(data.accounts[0]);
}
if (data.chainId) {
updateChain(data.chainId);
}
}
handleWalletDisconnected(data: any) {
const { setConnectionState } = this.store.getState();
setConnectionState({
isConnected: false,
accounts: [],
currentAccount: null,
chainId: null,
networkName: null,
balance: null
});
}
handleAccountsChanged(data: any) {
const { updateAccount, setConnectionState } = this.store.getState();
if (data.accounts.length === 0) {
// 用户断开了连接
setConnectionState({
isConnected: false,
accounts: [],
currentAccount: null
});
} else {
// 用户切换了账户
setConnectionState({
accounts: data.accounts,
currentAccount: data.accounts[0]
});
updateAccount(data.accounts[0]);
}
}
handleChainChanged(data: any) {
const { updateChain } = this.store.getState();
updateChain(data.chainId);
}
handleVisibilityChange() {
if (document.visibilityState === 'visible') {
// 页面重新可见时检查连接状态
this.checkConnectionState();
}
}
handleNetworkOnline() {
// 网络恢复时重新检查状态
this.checkConnectionState();
}
handleNetworkOffline() {
const { setError } = this.store.getState();
setError('网络连接已断开');
}
async checkConnectionState() {
const { isConnected } = this.store.getState();
if (isConnected) {
try {
const accounts = await walletManager.getAccounts();
if (accounts.length === 0) {
// 连接已断开
this.handleWalletDisconnected({});
}
} catch (error) {
console.error('Check connection state error:', error);
}
}
}
destroy() {
// 清理事件监听器
this.unsubscribers.forEach(unsubscribe => unsubscribe());
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
window.removeEventListener('online', this.handleNetworkOnline);
window.removeEventListener('offline', this.handleNetworkOffline);
}
}
// 初始化事件处理器
const eventHandler = new WalletEventHandler(useWalletStore);
React Hook封装:
// 自定义Hook封装钱包操作
import { useEffect, useCallback } from 'react';
export function useWallet() {
const {
isConnected,
isConnecting,
connectionError,
currentAccount,
chainId,
isWrongNetwork,
balance,
connectWallet,
disconnectWallet,
clearError,
autoConnect,
preferredWallet
} = useWalletStore();
// 自动连接逻辑
useEffect(() => {
if (autoConnect && preferredWallet && !isConnected && !isConnecting) {
connectWallet(preferredWallet).catch(console.error);
}
}, [autoConnect, preferredWallet, isConnected, isConnecting]);
// 连接钱包
const connect = useCallback(async (walletType: string) => {
try {
await connectWallet(walletType);
} catch (error) {
console.error('Connect error:', error);
}
}, [connectWallet]);
// 断开连接
const disconnect = useCallback(async () => {
try {
await disconnectWallet();
} catch (error) {
console.error('Disconnect error:', error);
}
}, [disconnectWallet]);
// 切换网络
const switchNetwork = useCallback(async (targetChainId: string) => {
try {
await walletManager.switchNetwork(targetChainId);
} catch (error) {
console.error('Switch network error:', error);
}
}, []);
return {
// 状态
isConnected,
isConnecting,
connectionError,
currentAccount,
chainId,
isWrongNetwork,
balance,
// 操作
connect,
disconnect,
switchNetwork,
clearError
};
}
// 使用示例
function WalletButton() {
const {
isConnected,
isConnecting,
currentAccount,
connect,
disconnect
} = useWallet();
if (isConnecting) {
return <button disabled>连接中...</button>;
}
if (isConnected) {
return (
<div>
<span>{currentAccount?.slice(0, 6)}...{currentAccount?.slice(-4)}</span>
<button onClick={disconnect}>断开连接</button>
</div>
);
}
return (
<button onClick={() => connect('metamask')}>
连接MetaMask
</button>
);
}
状态管理最佳实践:
How to implement automatic wallet reconnection? What factors need to be considered?
How to implement automatic wallet reconnection? What factors need to be considered?
考察点:用户体验优化。
答案:
钱包自动重连功能通过检测连接状态变化、存储连接偏好、实现重连策略等方式,在页面刷新或网络恢复时自动恢复钱包连接。需要考虑用户隐私、安全性、网络状态、重连时机等多个因素,提供流畅的用户体验。
核心重连策略实现:
class WalletAutoReconnect {
private maxRetries = 3;
private retryDelay = 2000;
private currentRetries = 0;
private reconnectTimer: NodeJS.Timeout | null = null;
private isReconnecting = false;
constructor(private walletStore: any) {
this.setupAutoReconnect();
}
setupAutoReconnect() {
// 页面加载时检查自动重连
window.addEventListener('load', this.handlePageLoad.bind(this));
// 网络状态恢复时重连
window.addEventListener('online', this.handleNetworkOnline.bind(this));
// 页面可见性变化时检查连接
document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
// 监听钱包断开事件
this.setupDisconnectionHandler();
}
private async handlePageLoad() {
const { autoConnect, preferredWallet } = this.walletStore.getState();
if (autoConnect && preferredWallet) {
await this.attemptReconnection(preferredWallet, 'PAGE_LOAD');
}
}
private async handleNetworkOnline() {
const { isConnected, preferredWallet } = this.walletStore.getState();
if (!isConnected && preferredWallet) {
await this.attemptReconnection(preferredWallet, 'NETWORK_RESTORED');
}
}
private async handleVisibilityChange() {
if (document.visibilityState === 'visible') {
await this.checkAndReconnect('PAGE_VISIBLE');
}
}
private setupDisconnectionHandler() {
this.walletStore.subscribe((state: any, prevState: any) => {
// 监听连接状态变化
if (prevState.isConnected && !state.isConnected) {
this.handleUnexpectedDisconnection();
}
});
}
private async handleUnexpectedDisconnection() {
const { preferredWallet, autoConnect } = this.walletStore.getState();
if (autoConnect && preferredWallet) {
// 延迟重连,避免立即重连导致的问题
setTimeout(() => {
this.attemptReconnection(preferredWallet, 'UNEXPECTED_DISCONNECT');
}, 1000);
}
}
async attemptReconnection(walletType: string, reason: string) {
if (this.isReconnecting) {
return false; // 避免重复重连
}
this.isReconnecting = true;
this.currentRetries = 0;
console.log(`Starting auto reconnection: ${reason}`);
try {
const success = await this.executeReconnection(walletType);
if (success) {
this.onReconnectionSuccess(walletType, reason);
return true;
} else {
this.onReconnectionFailure(walletType, reason);
return false;
}
} finally {
this.isReconnecting = false;
}
}
private async executeReconnection(walletType: string): Promise<boolean> {
while (this.currentRetries < this.maxRetries) {
try {
this.currentRetries++;
console.log(`Reconnection attempt ${this.currentRetries}/${this.maxRetries}`);
// 检查钱包是否可用
const isAvailable = await this.checkWalletAvailability(walletType);
if (!isAvailable) {
throw new Error('Wallet not available');
}
// 尝试静默连接(不弹窗)
const accounts = await this.silentConnect(walletType);
if (accounts && accounts.length > 0) {
return true;
}
throw new Error('No accounts returned');
} catch (error) {
console.error(`Reconnection attempt ${this.currentRetries} failed:`, error);
if (this.currentRetries < this.maxRetries) {
// 等待后重试
await this.delay(this.retryDelay * this.currentRetries);
}
}
}
return false;
}
private async checkWalletAvailability(walletType: string): Promise<boolean> {
switch (walletType) {
case 'metamask':
return typeof window.ethereum !== 'undefined' &&
window.ethereum.isMetaMask;
case 'walletconnect':
return true; // WalletConnect is always available
default:
return false;
}
}
private async silentConnect(walletType: string): Promise<string[] | null> {
try {
// 先检查是否已有权限
const accounts = await window.ethereum?.request({
method: 'eth_accounts'
});
if (accounts && accounts.length > 0) {
// 更新store状态
const chainId = await window.ethereum.request({
method: 'eth_chainId'
});
this.walletStore.getState().setConnectionState({
isConnected: true,
walletType: walletType,
accounts: accounts,
currentAccount: accounts[0],
chainId: chainId
});
return accounts;
}
return null;
} catch (error) {
console.error('Silent connect failed:', error);
return null;
}
}
private onReconnectionSuccess(walletType: string, reason: string) {
console.log(`Auto reconnection successful: ${walletType} (${reason})`);
// 重置重连计数
this.currentRetries = 0;
// 触发成功事件
this.walletStore.getState().setConnectionState({
connectionError: null
});
// 可以在这里添加用户通知
this.showReconnectionNotification('success', walletType);
}
private onReconnectionFailure(walletType: string, reason: string) {
console.log(`Auto reconnection failed: ${walletType} (${reason})`);
// 更新错误状态
this.walletStore.getState().setError('自动重连失败,请手动连接钱包');
// 显示重连失败通知
this.showReconnectionNotification('failed', walletType);
}
private showReconnectionNotification(type: 'success' | 'failed', walletType: string) {
const messages = {
success: `${walletType} 已自动重新连接`,
failed: `${walletType} 自动重连失败,请手动连接`
};
// 这里可以集成toast通知系统
console.log(messages[type]);
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
async checkAndReconnect(reason: string) {
const { isConnected, preferredWallet, autoConnect } = this.walletStore.getState();
if (!isConnected && autoConnect && preferredWallet) {
// 验证当前连接状态
try {
const accounts = await window.ethereum?.request({
method: 'eth_accounts'
});
if (!accounts || accounts.length === 0) {
await this.attemptReconnection(preferredWallet, reason);
}
} catch (error) {
console.error('Check connection failed:', error);
}
}
}
// 手动触发重连
async manualReconnect(): Promise<boolean> {
const { preferredWallet } = this.walletStore.getState();
if (preferredWallet) {
return await this.attemptReconnection(preferredWallet, 'MANUAL_TRIGGER');
}
return false;
}
// 启用/禁用自动重连
setAutoReconnect(enabled: boolean, walletType?: string) {
this.walletStore.getState().setConnectionState({
autoConnect: enabled,
preferredWallet: walletType || this.walletStore.getState().preferredWallet
});
}
// 清理资源
destroy() {
window.removeEventListener('load', this.handlePageLoad);
window.removeEventListener('online', this.handleNetworkOnline);
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
}
}
}
重连配置和策略管理:
interface ReconnectConfig {
enabled: boolean;
maxRetries: number;
retryDelay: number;
retryBackoff: boolean;
conditions: ReconnectCondition[];
}
type ReconnectCondition =
| 'PAGE_LOAD'
| 'NETWORK_RESTORED'
| 'PAGE_VISIBLE'
| 'UNEXPECTED_DISCONNECT';
class ReconnectConfigManager {
private config: ReconnectConfig = {
enabled: true,
maxRetries: 3,
retryDelay: 2000,
retryBackoff: true,
conditions: ['PAGE_LOAD', 'NETWORK_RESTORED', 'UNEXPECTED_DISCONNECT']
};
updateConfig(updates: Partial<ReconnectConfig>) {
this.config = { ...this.config, ...updates };
this.saveConfig();
}
shouldReconnect(condition: ReconnectCondition): boolean {
return this.config.enabled &&
this.config.conditions.includes(condition);
}
getRetryDelay(attempt: number): number {
if (this.config.retryBackoff) {
return this.config.retryDelay * Math.pow(2, attempt - 1);
}
return this.config.retryDelay;
}
private saveConfig() {
localStorage.setItem('wallet-reconnect-config', JSON.stringify(this.config));
}
private loadConfig() {
const saved = localStorage.getItem('wallet-reconnect-config');
if (saved) {
this.config = { ...this.config, ...JSON.parse(saved) };
}
}
}
What is EIP-1193? How does it standardize wallet interfaces?
What is EIP-1193? How does it standardize wallet interfaces?
考察点:标准协议理解。
答案:
EIP-1193是以太坊改进提案,定义了钱包提供商(Provider)的标准接口。它规范了DApp与钱包之间的通信方式,包括API方法、事件处理、错误码等,使得不同钱包都能提供一致的开发者体验。
EIP-1193核心接口定义:
// EIP-1193 Provider接口
interface EIP1193Provider {
// 核心请求方法
request(args: RequestArguments): Promise<any>;
// 事件监听
on(eventName: string, listener: Function): void;
removeListener(eventName: string, listener: Function): void;
// 连接状态检查
isConnected(): boolean;
}
interface RequestArguments {
method: string;
params?: Array<any> | Record<string, any>;
}
// 标准事件类型
type ProviderEvents =
| 'connect'
| 'disconnect'
| 'accountsChanged'
| 'chainChanged'
| 'message';
EIP-1193标准方法实现:
class EIP1193Provider {
private isConnectedState = false;
private chainId: string | null = null;
private accounts: string[] = [];
private eventEmitter = new EventTarget();
// 核心请求方法
async request(args: RequestArguments): Promise<any> {
const { method, params = [] } = args;
try {
switch (method) {
case 'eth_requestAccounts':
return await this.requestAccounts();
case 'eth_accounts':
return this.accounts;
case 'eth_chainId':
return this.chainId;
case 'eth_getBalance':
return await this.getBalance(params[0], params[1]);
case 'eth_sendTransaction':
return await this.sendTransaction(params[0]);
case 'personal_sign':
return await this.personalSign(params[0], params[1]);
case 'eth_signTypedData_v4':
return await this.signTypedData(params[0], params[1]);
case 'wallet_switchEthereumChain':
return await this.switchChain(params[0]);
case 'wallet_addEthereumChain':
return await this.addChain(params[0]);
default:
throw new ProviderRpcError(
-32601,
`The method ${method} does not exist/is not available`
);
}
} catch (error) {
if (error instanceof ProviderRpcError) {
throw error;
}
throw new ProviderRpcError(-32603, 'Internal error', error);
}
}
// 事件监听实现
on(eventName: string, listener: Function): void {
this.eventEmitter.addEventListener(eventName, listener as EventListener);
}
removeListener(eventName: string, listener: Function): void {
this.eventEmitter.removeEventListener(eventName, listener as EventListener);
}
// 发出事件
private emit(eventName: string, data: any): void {
const event = new CustomEvent(eventName, { detail: data });
this.eventEmitter.dispatchEvent(event);
}
// 连接状态
isConnected(): boolean {
return this.isConnectedState;
}
// 标准方法实现
private async requestAccounts(): Promise<string[]> {
// 实现账户请求逻辑
if (!this.isConnectedState) {
// 模拟用户授权流程
const granted = await this.requestUserPermission();
if (!granted) {
throw new ProviderRpcError(4001, 'User rejected the request');
}
this.isConnectedState = true;
this.accounts = ['0x742d35Cc6327C0532...'];
this.chainId = '0x1';
// 发出连接事件
this.emit('connect', { chainId: this.chainId });
this.emit('accountsChanged', this.accounts);
}
return this.accounts;
}
private async getBalance(address: string, blockTag: string): Promise<string> {
// 实现余额查询
return '0x1bc16d674ec80000'; // 2 ETH in hex
}
private async sendTransaction(transaction: any): Promise<string> {
// 验证交易参数
this.validateTransaction(transaction);
// 显示交易确认界面
const confirmed = await this.showTransactionConfirmation(transaction);
if (!confirmed) {
throw new ProviderRpcError(4001, 'User rejected the request');
}
// 返回交易哈希
return '0x' + Array(64).fill(0).map(() =>
Math.floor(Math.random() * 16).toString(16)
).join('');
}
private async personalSign(message: string, account: string): Promise<string> {
// 验证账户
if (!this.accounts.includes(account)) {
throw new ProviderRpcError(4100, 'The requested account is not available');
}
// 显示签名确认
const confirmed = await this.showSignConfirmation(message, account);
if (!confirmed) {
throw new ProviderRpcError(4001, 'User rejected the request');
}
// 返回签名结果
return '0x' + Array(130).fill(0).map(() =>
Math.floor(Math.random() * 16).toString(16)
).join('');
}
private async switchChain(chainParams: any): Promise<null> {
const { chainId } = chainParams;
if (this.chainId === chainId) {
return null; // 已经在目标网络
}
// 检查网络是否支持
if (!this.isSupportedChain(chainId)) {
throw new ProviderRpcError(4902, 'Unrecognized chain ID');
}
// 切换网络
const previousChainId = this.chainId;
this.chainId = chainId;
// 发出网络变化事件
this.emit('chainChanged', chainId);
return null;
}
private async addChain(chainParams: any): Promise<null> {
const { chainId, chainName, rpcUrls, nativeCurrency } = chainParams;
// 验证参数
if (!chainId || !chainName || !rpcUrls || !nativeCurrency) {
throw new ProviderRpcError(-32602, 'Invalid parameters');
}
// 显示添加网络确认
const confirmed = await this.showAddChainConfirmation(chainParams);
if (!confirmed) {
throw new ProviderRpcError(4001, 'User rejected the request');
}
// 添加网络成功
return null;
}
// 辅助方法
private validateTransaction(tx: any): void {
if (!tx.to && !tx.data) {
throw new ProviderRpcError(-32602, 'Invalid transaction params');
}
}
private async requestUserPermission(): Promise<boolean> {
// 模拟用户授权界面
return new Promise(resolve => {
setTimeout(() => resolve(true), 1000);
});
}
private async showTransactionConfirmation(tx: any): Promise<boolean> {
// 模拟交易确认界面
return new Promise(resolve => {
setTimeout(() => resolve(true), 1000);
});
}
private async showSignConfirmation(message: string, account: string): Promise<boolean> {
// 模拟签名确认界面
return new Promise(resolve => {
setTimeout(() => resolve(true), 1000);
});
}
private async showAddChainConfirmation(chainParams: any): Promise<boolean> {
// 模拟添加网络确认界面
return new Promise(resolve => {
setTimeout(() => resolve(true), 1000);
});
}
private isSupportedChain(chainId: string): boolean {
const supportedChains = ['0x1', '0x89', '0x38'];
return supportedChains.includes(chainId);
}
}
// 标准错误类
class ProviderRpcError extends Error {
public code: number;
public data?: any;
constructor(code: number, message: string, data?: any) {
super(message);
this.code = code;
this.data = data;
this.name = 'ProviderRpcError';
}
}
EIP-1193错误码标准:
// 标准错误码定义
const EIP1193_ERROR_CODES = {
// 用户拒绝请求
USER_REJECTED: 4001,
// 未授权访问请求的方法
UNAUTHORIZED: 4100,
// 不支持的方法
UNSUPPORTED_METHOD: 4200,
// 提供商断开连接
DISCONNECTED: 4900,
// 未识别的链ID
UNRECOGNIZED_CHAIN: 4902,
// RPC错误
PARSE_ERROR: -32700,
INVALID_REQUEST: -32600,
METHOD_NOT_FOUND: -32601,
INVALID_PARAMS: -32602,
INTERNAL_ERROR: -32603
};
// 错误处理工具
class EIP1193ErrorHandler {
static createError(code: number, message?: string, data?: any): ProviderRpcError {
const errorMessages = {
4001: 'User rejected the request',
4100: 'The requested account and/or method has not been authorized',
4200: 'The requested method is not supported',
4900: 'The provider is disconnected from all chains',
4902: 'Unrecognized chain ID',
-32700: 'Parse error',
-32600: 'Invalid Request',
-32601: 'Method not found',
-32602: 'Invalid params',
-32603: 'Internal error'
};
const errorMessage = message || errorMessages[code] || 'Unknown error';
return new ProviderRpcError(code, errorMessage, data);
}
static isUserRejection(error: any): boolean {
return error?.code === 4001;
}
static isMethodNotSupported(error: any): boolean {
return error?.code === 4200 || error?.code === -32601;
}
}
How to handle API differences between different wallets? Implement unified interface abstraction.
How to handle API differences between different wallets? Implement unified interface abstraction.
考察点:接口抽象设计。
答案:
不同钱包在API实现上存在差异,需要通过适配器模式和接口抽象来统一处理。核心思路是定义统一的接口规范,为每个钱包实现具体的适配器,处理API差异、参数格式、错误码等不同之处。
统一接口抽象设计:
// 统一钱包接口定义
interface UnifiedWalletInterface {
// 基础信息
readonly name: string;
readonly type: WalletType;
readonly version?: string;
// 连接管理
connect(): Promise<WalletConnectionResult>;
disconnect(): Promise<void>;
isConnected(): boolean;
// 账户操作
getAccounts(): Promise<string[]>;
getCurrentAccount(): Promise<string | null>;
// 网络操作
getCurrentNetwork(): Promise<NetworkInfo>;
switchNetwork(chainId: string): Promise<boolean>;
addNetwork(network: NetworkConfig): Promise<boolean>;
// 交易操作
sendTransaction(params: TransactionParams): Promise<string>;
signTransaction(params: TransactionParams): Promise<string>;
// 签名操作
signMessage(message: string, account?: string): Promise<string>;
signTypedData(typedData: TypedData, account?: string): Promise<string>;
// 事件监听
on(event: WalletEvent, listener: Function): void;
off(event: WalletEvent, listener: Function): void;
// 能力检测
supports(capability: WalletCapability): boolean;
}
// 类型定义
type WalletType = 'injected' | 'walletconnect' | 'hardware' | 'mobile';
type WalletEvent = 'connect' | 'disconnect' | 'accountsChanged' | 'chainChanged' | 'message';
type WalletCapability = 'signMessage' | 'signTypedData' | 'switchNetwork' | 'addNetwork';
interface WalletConnectionResult {
success: boolean;
accounts: string[];
chainId: string;
error?: string;
}
interface NetworkInfo {
chainId: string;
name: string;
symbol: string;
rpcUrl?: string;
blockExplorer?: string;
}
interface TransactionParams {
to: string;
value?: string;
data?: string;
gas?: string;
gasPrice?: string;
from?: string;
}
API差异处理适配器:
// MetaMask适配器 - 处理标准EIP-1193接口
class MetaMaskWalletAdapter implements UnifiedWalletInterface {
readonly name = 'MetaMask';
readonly type: WalletType = 'injected';
private provider = window.ethereum;
private eventListeners = new Map<string, Function[]>();
async connect(): Promise<WalletConnectionResult> {
try {
const accounts = await this.provider.request({
method: 'eth_requestAccounts'
});
const chainId = await this.provider.request({
method: 'eth_chainId'
});
return {
success: true,
accounts,
chainId
};
} catch (error) {
return {
success: false,
accounts: [],
chainId: '',
error: this.normalizeError(error).message
};
}
}
async signMessage(message: string, account?: string): Promise<string> {
const from = account || await this.getCurrentAccount();
if (!from) throw new Error('No account available');
return await this.provider.request({
method: 'personal_sign',
params: [message, from]
});
}
async signTypedData(typedData: TypedData, account?: string): Promise<string> {
const from = account || await this.getCurrentAccount();
if (!from) throw new Error('No account available');
return await this.provider.request({
method: 'eth_signTypedData_v4',
params: [from, JSON.stringify(typedData)]
});
}
supports(capability: WalletCapability): boolean {
const capabilities = {
'signMessage': true,
'signTypedData': true,
'switchNetwork': true,
'addNetwork': true
};
return capabilities[capability] || false;
}
private normalizeError(error: any): Error {
if (error.code === 4001) {
return new Error('User rejected the request');
}
return new Error(error.message || 'Unknown error');
}
}
// WalletConnect适配器 - 处理WalletConnect协议差异
class WalletConnectAdapter implements UnifiedWalletInterface {
readonly name = 'WalletConnect';
readonly type: WalletType = 'walletconnect';
private connector: any;
private eventListeners = new Map<string, Function[]>();
async connect(): Promise<WalletConnectionResult> {
try {
// WalletConnect v1.x
if (!this.connector) {
const { WalletConnect } = await import('@walletconnect/client');
this.connector = new WalletConnect({
bridge: 'https://bridge.walletconnect.org',
qrcodeModal: await import('@walletconnect/qrcode-modal')
});
}
if (this.connector.connected) {
return {
success: true,
accounts: this.connector.accounts,
chainId: `0x${this.connector.chainId.toString(16)}`
};
}
await this.connector.createSession();
return new Promise((resolve) => {
this.connector.on('connect', (error: any, payload: any) => {
if (error) {
resolve({
success: false,
accounts: [],
chainId: '',
error: error.message
});
} else {
const { accounts, chainId } = payload.params[0];
resolve({
success: true,
accounts,
chainId: `0x${chainId.toString(16)}`
});
}
});
});
} catch (error) {
return {
success: false,
accounts: [],
chainId: '',
error: error.message
};
}
}
async signMessage(message: string, account?: string): Promise<string> {
if (!this.connector?.connected) {
throw new Error('WalletConnect not connected');
}
const from = account || this.connector.accounts[0];
// WalletConnect使用不同的方法名
return await this.connector.signPersonalMessage({
data: message,
from: from
});
}
async signTypedData(typedData: TypedData, account?: string): Promise<string> {
if (!this.connector?.connected) {
throw new Error('WalletConnect not connected');
}
const from = account || this.connector.accounts[0];
// WalletConnect的类型化数据签名
return await this.connector.signTypedData({
data: typedData,
from: from
});
}
async sendTransaction(params: TransactionParams): Promise<string> {
if (!this.connector?.connected) {
throw new Error('WalletConnect not connected');
}
// WalletConnect参数格式适配
const wcParams = {
from: params.from || this.connector.accounts[0],
to: params.to,
value: params.value || '0x0',
data: params.data || '0x',
gas: params.gas,
gasPrice: params.gasPrice
};
return await this.connector.sendTransaction(wcParams);
}
supports(capability: WalletCapability): boolean {
// WalletConnect支持的功能有限
const capabilities = {
'signMessage': true,
'signTypedData': true,
'switchNetwork': false, // 依赖连接的钱包
'addNetwork': false
};
return capabilities[capability] || false;
}
}
// Coinbase Wallet适配器 - 处理Coinbase特有API
class CoinbaseWalletAdapter implements UnifiedWalletInterface {
readonly name = 'Coinbase Wallet';
readonly type: WalletType = 'injected';
private provider: any;
constructor() {
// Coinbase Wallet使用不同的注入对象
this.provider = window.ethereum?.isCoinbaseWallet ?
window.ethereum :
window.coinbaseWalletExtension;
}
async signMessage(message: string, account?: string): Promise<string> {
const from = account || await this.getCurrentAccount();
// Coinbase Wallet可能使用不同的方法
try {
return await this.provider.request({
method: 'personal_sign',
params: [message, from]
});
} catch (error) {
// 降级到eth_sign (不推荐,但作为备选)
if (error.code === -32601) {
return await this.provider.request({
method: 'eth_sign',
params: [from, message]
});
}
throw error;
}
}
async switchNetwork(chainId: string): Promise<boolean> {
try {
await this.provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId }]
});
return true;
} catch (error) {
// Coinbase Wallet特殊错误处理
if (error.code === 4902 || error.code === -32603) {
throw new Error('Network not supported by Coinbase Wallet');
}
throw error;
}
}
}
差异统一处理中心:
class WalletAPIUnifier {
private adapters = new Map<string, UnifiedWalletInterface>();
registerAdapter(name: string, adapter: UnifiedWalletInterface) {
this.adapters.set(name, adapter);
}
// 统一的方法调用处理
async executeMethod<T>(
walletName: string,
method: keyof UnifiedWalletInterface,
...args: any[]
): Promise<T> {
const adapter = this.adapters.get(walletName);
if (!adapter) {
throw new Error(`Wallet ${walletName} not registered`);
}
// 检查方法支持
if (typeof adapter[method] !== 'function') {
throw new Error(`Method ${method} not supported by ${walletName}`);
}
try {
return await (adapter[method] as Function).apply(adapter, args);
} catch (error) {
// 统一错误处理和转换
throw this.normalizeError(error, walletName, method);
}
}
// 批量操作处理
async executeBatch(operations: Array<{
wallet: string;
method: string;
args: any[];
}>) {
const results = await Promise.allSettled(
operations.map(op =>
this.executeMethod(op.wallet, op.method as any, ...op.args)
)
);
return results.map((result, index) => ({
operation: operations[index],
success: result.status === 'fulfilled',
data: result.status === 'fulfilled' ? result.value : undefined,
error: result.status === 'rejected' ? result.reason : undefined
}));
}
// 能力检测和降级处理
async signMessageWithFallback(
walletName: string,
message: string,
account?: string
): Promise<string> {
const adapter = this.adapters.get(walletName);
if (!adapter) {
throw new Error(`Wallet ${walletName} not registered`);
}
// 检查支持的签名方法
if (adapter.supports('signMessage')) {
return await adapter.signMessage(message, account);
} else {
// 降级处理或提供替代方案
throw new Error(`${walletName} does not support message signing`);
}
}
private normalizeError(error: any, walletName: string, method: string): Error {
// 统一不同钱包的错误格式
const normalizedError = new Error();
if (error.code === 4001 || error.code === 'ACTION_REJECTED') {
normalizedError.message = 'User rejected the request';
(normalizedError as any).code = 4001;
} else if (error.code === -32601 || error.message?.includes('not supported')) {
normalizedError.message = `Method ${method} not supported by ${walletName}`;
(normalizedError as any).code = -32601;
} else {
normalizedError.message = error.message || 'Unknown error';
(normalizedError as any).code = error.code || -32603;
}
return normalizedError;
}
}
// 使用示例
const unifier = new WalletAPIUnifier();
unifier.registerAdapter('metamask', new MetaMaskWalletAdapter());
unifier.registerAdapter('walletconnect', new WalletConnectAdapter());
unifier.registerAdapter('coinbase', new CoinbaseWalletAdapter());
// 统一调用
try {
const signature = await unifier.signMessageWithFallback(
'metamask',
'Hello World',
'0x123...'
);
console.log('Signature:', signature);
} catch (error) {
console.error('Sign failed:', error.message);
}
What is EIP-712 typed signing? How to implement structured data signing?
What is EIP-712 typed signing? How to implement structured data signing?
考察点:高级签名机制。
答案:
EIP-712定义了结构化数据的签名标准,允许用户签名复杂的数据结构而非简单字符串。它提供了类型安全、人类可读的签名体验,广泛应用于DeFi协议、NFT市场等场景,如授权交易、投票治理、订单签名等。
EIP-712数据结构定义:
// EIP-712基础结构
interface EIP712Domain {
name?: string; // DApp名称
version?: string; // 合约版本
chainId?: number; // 链ID
verifyingContract?: string; // 验证合约地址
salt?: string; // 随机盐值
}
interface TypedData {
types: Record<string, Array<{name: string, type: string}>>;
primaryType: string; // 主要类型名称
domain: EIP712Domain; // 域分隔符
message: Record<string, any>; // 实际消息数据
}
// 示例:代币授权签名
const permitTypedData: TypedData = {
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' }
],
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' }
]
},
primaryType: 'Permit',
domain: {
name: 'MyToken',
version: '1',
chainId: 1,
verifyingContract: '0x1234567890123456789012345678901234567890'
},
message: {
owner: '0xowner...',
spender: '0xspender...',
value: '1000000000000000000', // 1 token
nonce: 0,
deadline: Math.floor(Date.now() / 1000) + 3600 // 1 hour
}
};
EIP-712签名实现:
class EIP712Signer {
constructor(private provider: any) {}
// 标准EIP-712签名
async signTypedData(typedData: TypedData, account: string): Promise<string> {
try {
// 使用eth_signTypedData_v4方法
const signature = await this.provider.request({
method: 'eth_signTypedData_v4',
params: [account, JSON.stringify(typedData)]
});
return signature;
} catch (error) {
// 降级处理
return await this.fallbackSignTypedData(typedData, account);
}
}
// 降级签名处理
async fallbackSignTypedData(typedData: TypedData, account: string): Promise<string> {
try {
// 尝试v3版本
return await this.provider.request({
method: 'eth_signTypedData_v3',
params: [account, JSON.stringify(typedData)]
});
} catch (error) {
// 最后降级到v1
return await this.provider.request({
method: 'eth_signTypedData',
params: [account, typedData]
});
}
}
// 构建类型化数据
buildTypedData(
domain: EIP712Domain,
types: Record<string, Array<{name: string, type: string}>>,
primaryType: string,
message: Record<string, any>
): TypedData {
// 确保包含EIP712Domain类型
const fullTypes = {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' }
].filter(field => domain[field.name as keyof EIP712Domain] !== undefined),
...types
};
return {
types: fullTypes,
primaryType,
domain,
message
};
}
// 验证类型化数据结构
validateTypedData(typedData: TypedData): boolean {
const { types, primaryType, domain, message } = typedData;
// 检查必需字段
if (!types || !primaryType || !domain || !message) {
return false;
}
// 检查primaryType是否存在于types中
if (!types[primaryType]) {
return false;
}
// 验证消息字段类型
const primaryTypeFields = types[primaryType];
for (const field of primaryTypeFields) {
if (message[field.name] === undefined) {
console.warn(`Missing field: ${field.name}`);
}
}
return true;
}
// 计算类型哈希
encodeType(primaryType: string, types: Record<string, Array<{name: string, type: string}>>): string {
const deps = this.findTypeDependencies(primaryType, types);
const depTypes = deps.sort().filter(type => type !== primaryType);
return primaryType + '(' +
types[primaryType].map(field => `${field.type} ${field.name}`).join(',') +
')' +
depTypes.map(type =>
type + '(' +
types[type].map(field => `${field.type} ${field.name}`).join(',') +
')'
).join('');
}
private findTypeDependencies(
primaryType: string,
types: Record<string, Array<{name: string, type: string}>>,
found: string[] = []
): string[] {
if (found.includes(primaryType) || !types[primaryType]) {
return found;
}
found.push(primaryType);
for (const field of types[primaryType]) {
const typeName = field.type.replace(/\[\]$/, '');
if (types[typeName]) {
this.findTypeDependencies(typeName, types, found);
}
}
return found;
}
}
常用EIP-712应用场景:
// 1. DEX订单签名
class DEXOrderSigner extends EIP712Signer {
async signOrder(orderData: any, account: string): Promise<string> {
const typedData = this.buildTypedData(
{
name: 'DEX Protocol',
version: '1.0',
chainId: 1,
verifyingContract: '0xDEXContract...'
},
{
Order: [
{ name: 'trader', type: 'address' },
{ name: 'baseToken', type: 'address' },
{ name: 'quoteToken', type: 'address' },
{ name: 'baseAmount', type: 'uint256' },
{ name: 'quoteAmount', type: 'uint256' },
{ name: 'expiration', type: 'uint256' },
{ name: 'nonce', type: 'uint256' }
]
},
'Order',
orderData
);
return await this.signTypedData(typedData, account);
}
}
// 2. 治理投票签名
class GovernanceVoteSigner extends EIP712Signer {
async signVote(voteData: any, account: string): Promise<string> {
const typedData = this.buildTypedData(
{
name: 'Governance',
version: '1',
chainId: await this.getCurrentChainId()
},
{
Vote: [
{ name: 'voter', type: 'address' },
{ name: 'proposalId', type: 'uint256' },
{ name: 'support', type: 'uint8' },
{ name: 'weight', type: 'uint256' },
{ name: 'nonce', type: 'uint256' }
]
},
'Vote',
voteData
);
return await this.signTypedData(typedData, account);
}
}
// 3. NFT铸造签名
class NFTMintSigner extends EIP712Signer {
async signMintAuthorization(mintData: any, account: string): Promise<string> {
const typedData = this.buildTypedData(
{
name: 'NFT Collection',
version: '1.0',
chainId: 1,
verifyingContract: '0xNFTContract...'
},
{
MintAuthorization: [
{ name: 'to', type: 'address' },
{ name: 'tokenId', type: 'uint256' },
{ name: 'uri', type: 'string' },
{ name: 'price', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
{ name: 'nonce', type: 'uint256' }
]
},
'MintAuthorization',
mintData
);
return await this.signTypedData(typedData, account);
}
}
签名验证和恢复:
class EIP712Verifier {
// 验证EIP-712签名
async verifySignature(
typedData: TypedData,
signature: string,
expectedSigner: string
): Promise<boolean> {
try {
// 使用ethers.js验证
const { ethers } = await import('ethers');
// 恢复签名者地址
const recoveredAddress = ethers.utils.verifyTypedData(
typedData.domain,
{ [typedData.primaryType]: typedData.types[typedData.primaryType] },
typedData.message,
signature
);
// 比较地址(忽略大小写)
return recoveredAddress.toLowerCase() === expectedSigner.toLowerCase();
} catch (error) {
console.error('签名验证失败:', error);
return false;
}
}
// 从签名恢复地址
async recoverSigner(typedData: TypedData, signature: string): Promise<string> {
const { ethers } = await import('ethers');
return ethers.utils.verifyTypedData(
typedData.domain,
{ [typedData.primaryType]: typedData.types[typedData.primaryType] },
typedData.message,
signature
);
}
// 生成签名哈希
async getSignatureHash(typedData: TypedData): Promise<string> {
const { ethers } = await import('ethers');
return ethers.utils._TypedDataEncoder.hash(
typedData.domain,
{ [typedData.primaryType]: typedData.types[typedData.primaryType] },
typedData.message
);
}
}
完整应用示例:
// 完整的EIP-712签名流程
class EIP712Application {
private signer: EIP712Signer;
private verifier: EIP712Verifier;
constructor(provider: any) {
this.signer = new EIP712Signer(provider);
this.verifier = new EIP712Verifier();
}
// 代币授权流程
async authorizeTokenTransfer(tokenData: any, account: string) {
try {
// 1. 构建类型化数据
const typedData = this.signer.buildTypedData(
{
name: tokenData.name,
version: '1',
chainId: tokenData.chainId,
verifyingContract: tokenData.address
},
{
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' }
]
},
'Permit',
{
owner: account,
spender: tokenData.spender,
value: tokenData.amount,
nonce: tokenData.nonce,
deadline: Math.floor(Date.now() / 1000) + 3600
}
);
// 2. 验证数据结构
if (!this.signer.validateTypedData(typedData)) {
throw new Error('Invalid typed data structure');
}
// 3. 请求用户签名
const signature = await this.signer.signTypedData(typedData, account);
// 4. 验证签名
const isValid = await this.verifier.verifySignature(
typedData,
signature,
account
);
if (!isValid) {
throw new Error('Signature verification failed');
}
return {
typedData,
signature,
hash: await this.verifier.getSignatureHash(typedData)
};
} catch (error) {
console.error('Authorization failed:', error);
throw error;
}
}
}
// 使用示例
const app = new EIP712Application(window.ethereum);
const authResult = await app.authorizeTokenTransfer({
name: 'USDC',
chainId: 1,
address: '0xA0b86a33E6441c6e851E6c287f2E89F4d4aa0e65',
spender: '0xSpenderContract...',
amount: '1000000', // 1 USDC
nonce: 0
}, '0xUserAccount...');
console.log('Authorization signature:', authResult.signature);
EIP-712优势:
How to synchronize wallet connection state across different devices?
How to synchronize wallet connection state across different devices?
考察点:跨设备状态同步。
答案:
跨设备钱包状态同步通过云端状态管理、设备指纹识别、实时通信等技术实现。核心挑战在于保护用户隐私的同时提供无缝体验,需要考虑安全性、一致性和实时性等因素。
同步架构设计:
// 跨设备状态同步管理器
class CrossDeviceWalletSync {
private syncService: SyncService;
private encryptionKey: CryptoKey | null = null;
private deviceId: string;
private userId: string | null = null;
constructor() {
this.syncService = new SyncService();
this.deviceId = this.generateDeviceFingerprint();
this.setupSyncListeners();
}
// 设备指纹生成
private generateDeviceFingerprint(): string {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx?.fillText('Device fingerprint', 10, 10);
const fingerprint = {
userAgent: navigator.userAgent,
language: navigator.language,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
screen: `${screen.width}x${screen.height}`,
canvas: canvas.toDataURL(),
timestamp: Date.now()
};
return btoa(JSON.stringify(fingerprint)).substring(0, 32);
}
// 初始化用户会话
async initializeUserSession(walletAddress: string): Promise<void> {
try {
// 使用钱包地址作为用户标识
this.userId = walletAddress.toLowerCase();
// 生成会话密钥
this.encryptionKey = await this.generateSessionKey(walletAddress);
// 注册设备
await this.registerDevice();
// 拉取远程状态
await this.pullRemoteState();
} catch (error) {
console.error('Initialize user session failed:', error);
}
}
// 生成会话加密密钥
private async generateSessionKey(seed: string): Promise<CryptoKey> {
const encoder = new TextEncoder();
const data = encoder.encode(seed + this.deviceId);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
return await crypto.subtle.importKey(
'raw',
hashBuffer,
{ name: 'AES-GCM' },
false,
['encrypt', 'decrypt']
);
}
// 注册设备到云端
private async registerDevice(): Promise<void> {
const deviceInfo = {
deviceId: this.deviceId,
userId: this.userId,
platform: navigator.platform,
timestamp: Date.now(),
lastActive: Date.now()
};
await this.syncService.registerDevice(deviceInfo);
}
// 同步钱包状态到云端
async syncWalletState(walletState: any): Promise<void> {
if (!this.userId || !this.encryptionKey) {
console.warn('User session not initialized');
return;
}
try {
// 过滤敏感数据
const sanitizedState = this.sanitizeWalletState(walletState);
// 加密状态数据
const encryptedState = await this.encryptData(sanitizedState);
// 上传到云端
await this.syncService.uploadState({
userId: this.userId,
deviceId: this.deviceId,
state: encryptedState,
timestamp: Date.now(),
version: this.generateStateVersion(sanitizedState)
});
} catch (error) {
console.error('Sync wallet state failed:', error);
}
}
// 从云端拉取状态
async pullRemoteState(): Promise<any> {
if (!this.userId || !this.encryptionKey) {
return null;
}
try {
const remoteState = await this.syncService.getState(this.userId);
if (remoteState && remoteState.state) {
// 解密状态数据
const decryptedState = await this.decryptData(remoteState.state);
// 验证状态完整性
if (this.validateStateIntegrity(decryptedState, remoteState.version)) {
return decryptedState;
}
}
return null;
} catch (error) {
console.error('Pull remote state failed:', error);
return null;
}
}
// 数据加密
private async encryptData(data: any): Promise<string> {
if (!this.encryptionKey) {
throw new Error('Encryption key not available');
}
const encoder = new TextEncoder();
const dataBuffer = encoder.encode(JSON.stringify(data));
const iv = crypto.getRandomValues(new Uint8Array(12));
const encryptedBuffer = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
this.encryptionKey,
dataBuffer
);
const combined = new Uint8Array(iv.length + encryptedBuffer.byteLength);
combined.set(iv);
combined.set(new Uint8Array(encryptedBuffer), iv.length);
return btoa(String.fromCharCode(...combined));
}
// 数据解密
private async decryptData(encryptedData: string): Promise<any> {
if (!this.encryptionKey) {
throw new Error('Encryption key not available');
}
const combined = Uint8Array.from(atob(encryptedData), c => c.charCodeAt(0));
const iv = combined.slice(0, 12);
const encrypted = combined.slice(12);
const decryptedBuffer = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv },
this.encryptionKey,
encrypted
);
const decoder = new TextDecoder();
const decryptedText = decoder.decode(decryptedBuffer);
return JSON.parse(decryptedText);
}
// 清理敏感数据
private sanitizeWalletState(state: any): any {
const sensitiveFields = [
'privateKey', 'mnemonic', 'seed', 'password'
];
const sanitized = { ...state };
// 移除敏感字段
sensitiveFields.forEach(field => {
delete sanitized[field];
});
// 只保留必要的状态信息
return {
isConnected: sanitized.isConnected,
walletType: sanitized.walletType,
currentAccount: sanitized.currentAccount,
chainId: sanitized.chainId,
networkName: sanitized.networkName,
lastConnected: Date.now(),
preferences: sanitized.preferences || {}
};
}
// 生成状态版本哈希
private generateStateVersion(state: any): string {
const stateString = JSON.stringify(state, Object.keys(state).sort());
return btoa(stateString).substring(0, 16);
}
// 验证状态完整性
private validateStateIntegrity(state: any, expectedVersion: string): boolean {
const currentVersion = this.generateStateVersion(state);
return currentVersion === expectedVersion;
}
// 设置同步监听器
private setupSyncListeners(): void {
// 监听页面可见性变化
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
this.handlePageVisible();
}
});
// 监听在线状态变化
window.addEventListener('online', () => {
this.handleNetworkOnline();
});
// 定期同步
setInterval(() => {
this.periodicSync();
}, 60000); // 每分钟同步一次
}
private async handlePageVisible(): Promise<void> {
// 页面重新可见时拉取最新状态
await this.pullRemoteState();
}
private async handleNetworkOnline(): Promise<void> {
// 网络恢复时同步状态
await this.pullRemoteState();
}
private async periodicSync(): Promise<void> {
// 定期同步当前状态
const currentState = this.getCurrentWalletState();
if (currentState) {
await this.syncWalletState(currentState);
}
}
private getCurrentWalletState(): any {
// 获取当前钱包状态的接口
// 这里需要与实际的钱包状态管理器集成
return null;
}
}
// 云端同步服务
class SyncService {
private apiEndpoint = 'https://api.walletSync.com';
private apiKey: string;
constructor() {
this.apiKey = process.env.WALLET_SYNC_API_KEY || '';
}
async registerDevice(deviceInfo: any): Promise<void> {
await this.makeRequest('POST', '/devices', deviceInfo);
}
async uploadState(stateData: any): Promise<void> {
await this.makeRequest('POST', '/sync/upload', stateData);
}
async getState(userId: string): Promise<any> {
return await this.makeRequest('GET', `/sync/${userId}`);
}
async getConnectedDevices(userId: string): Promise<any[]> {
return await this.makeRequest('GET', `/devices/${userId}`);
}
private async makeRequest(method: string, endpoint: string, data?: any): Promise<any> {
const response = await fetch(`${this.apiEndpoint}${endpoint}`, {
method,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: data ? JSON.stringify(data) : undefined
});
if (!response.ok) {
throw new Error(`Sync service error: ${response.status}`);
}
return await response.json();
}
}
实时状态同步实现:
// WebSocket实时同步
class RealtimeWalletSync {
private ws: WebSocket | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
async connect(userId: string): Promise<void> {
try {
this.ws = new WebSocket(`wss://sync.walletapp.com/ws/${userId}`);
this.ws.onopen = () => {
console.log('Realtime sync connected');
this.reconnectAttempts = 0;
};
this.ws.onmessage = (event) => {
this.handleSyncMessage(JSON.parse(event.data));
};
this.ws.onclose = () => {
this.handleDisconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
} catch (error) {
console.error('Failed to connect realtime sync:', error);
}
}
private handleSyncMessage(message: any): void {
switch (message.type) {
case 'state_update':
this.handleStateUpdate(message.data);
break;
case 'device_connected':
this.handleDeviceConnected(message.data);
break;
case 'device_disconnected':
this.handleDeviceDisconnected(message.data);
break;
}
}
private handleStateUpdate(stateData: any): void {
// 更新本地状态
console.log('Received state update:', stateData);
// 触发本地状态更新事件
}
private handleDisconnect(): void {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
setTimeout(() => {
this.reconnectAttempts++;
// 重连逻辑
}, Math.pow(2, this.reconnectAttempts) * 1000);
}
}
sendStateUpdate(stateData: any): void {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'state_update',
data: stateData,
timestamp: Date.now()
}));
}
}
}
同步冲突解决:
// 状态冲突解决器
class StateMerger {
// 合并冲突状态
mergeStates(localState: any, remoteState: any): any {
// 基于时间戳的简单合并策略
const merged = { ...localState };
Object.keys(remoteState).forEach(key => {
if (this.shouldUseRemoteValue(localState[key], remoteState[key])) {
merged[key] = remoteState[key];
}
});
return merged;
}
private shouldUseRemoteValue(local: any, remote: any): boolean {
// 如果本地没有值,使用远程值
if (local === undefined || local === null) {
return true;
}
// 如果远程值更新,使用远程值
if (remote?.lastUpdated > local?.lastUpdated) {
return true;
}
return false;
}
}
隐私和安全考虑:
答案:
跨设备钱包状态同步通过云端状态管理、设备指纹识别、实时通信等技术实现。核心挑战在于保护用户隐私的同时提供无缝体验,需要考虑安全性、一致性和实时性等因素。
What are the best practices for mobile wallet integration? How to handle deep links?
What are the best practices for mobile wallet integration? How to handle deep links?
考察点:移动端适配策略。
答案:
移动端钱包集成需要考虑设备限制、用户体验和安全性等因素。深链接(Deep Link)是连接DApp和钱包应用的关键机制,通过URL Scheme或Universal Links实现应用间跳转和数据传递,提供无缝的移动端Web3体验。
深链接处理实现:
// 移动端深链接管理器
class MobileWalletDeepLink {
private supportedWallets: Map<string, WalletConfig> = new Map();
private currentPlatform: 'ios' | 'android' | 'web';
constructor() {
this.currentPlatform = this.detectPlatform();
this.initializeSupportedWallets();
this.setupDeepLinkHandlers();
}
// 检测平台
private detectPlatform(): 'ios' | 'android' | 'web' {
const userAgent = navigator.userAgent;
if (/iPad|iPhone|iPod/.test(userAgent)) return 'ios';
if (/Android/.test(userAgent)) return 'android';
return 'web';
}
// 初始化支持的钱包配置
private initializeSupportedWallets() {
this.supportedWallets.set('metamask', {
name: 'MetaMask',
schemes: {
ios: 'metamask://',
android: 'metamask://',
universalLink: 'https://metamask.app.link/'
},
appStore: 'https://apps.apple.com/app/metamask/id1438144202',
playStore: 'https://play.google.com/store/apps/details?id=io.metamask',
fallbackUrl: 'https://metamask.io/download/'
});
this.supportedWallets.set('trust', {
name: 'Trust Wallet',
schemes: {
ios: 'trust://',
android: 'trust://',
universalLink: 'https://link.trustwallet.com/'
},
appStore: 'https://apps.apple.com/app/trust-crypto-bitcoin-wallet/id1288339409',
playStore: 'https://play.google.com/store/apps/details?id=com.wallet.crypto.trustapp'
});
this.supportedWallets.set('coinbase', {
name: 'Coinbase Wallet',
schemes: {
ios: 'cbwallet://',
android: 'cbwallet://',
universalLink: 'https://go.cb-w.com/'
},
appStore: 'https://apps.apple.com/app/coinbase-wallet/id1278383455',
playStore: 'https://play.google.com/store/apps/details?id=org.toshi'
});
}
// 生成钱包调用链接
generateWalletLink(walletType: string, action: string, params: any): string {
const walletConfig = this.supportedWallets.get(walletType);
if (!walletConfig) {
throw new Error(`Unsupported wallet: ${walletType}`);
}
const baseUrl = this.getWalletBaseUrl(walletConfig);
const actionPath = this.buildActionPath(action, params);
return `${baseUrl}${actionPath}`;
}
// 获取钱包基础URL
private getWalletBaseUrl(config: WalletConfig): string {
// 优先使用Universal Link(更可靠)
if (config.schemes.universalLink) {
return config.schemes.universalLink;
}
// 降级到平台特定的URL Scheme
return config.schemes[this.currentPlatform] || config.schemes.ios;
}
// 构建操作路径
private buildActionPath(action: string, params: any): string {
const queryParams = new URLSearchParams();
switch (action) {
case 'connect':
queryParams.set('action', 'connect');
if (params.dappUrl) queryParams.set('dappUrl', params.dappUrl);
if (params.chainId) queryParams.set('chainId', params.chainId);
break;
case 'sign':
queryParams.set('action', 'sign');
queryParams.set('message', params.message);
queryParams.set('address', params.address);
break;
case 'transaction':
queryParams.set('action', 'send');
queryParams.set('to', params.to);
queryParams.set('value', params.value || '0');
if (params.data) queryParams.set('data', params.data);
if (params.gas) queryParams.set('gas', params.gas);
break;
}
// 添加回调URL
if (params.callbackUrl) {
queryParams.set('callback', params.callbackUrl);
}
return `dapp?${queryParams.toString()}`;
}
// 连接钱包
async connectWallet(walletType: string): Promise<void> {
if (!this.isWalletInstalled(walletType)) {
await this.promptWalletInstallation(walletType);
return;
}
const connectUrl = this.generateWalletLink(walletType, 'connect', {
dappUrl: window.location.origin,
chainId: '1',
callbackUrl: `${window.location.origin}/wallet-callback`
});
await this.openWalletApp(connectUrl, walletType);
}
// 检查钱包是否已安装
private isWalletInstalled(walletType: string): boolean {
const walletConfig = this.supportedWallets.get(walletType);
if (!walletConfig) return false;
// 在移动端,通过尝试打开URL Scheme来检测
if (this.currentPlatform !== 'web') {
return true; // 假设已安装,让系统处理未安装的情况
}
// 在桌面端,检查注入的钱包对象
return this.checkInjectedWallet(walletType);
}
// 检查注入钱包
private checkInjectedWallet(walletType: string): boolean {
switch (walletType) {
case 'metamask':
return !!(window as any).ethereum?.isMetaMask;
case 'coinbase':
return !!(window as any).ethereum?.isCoinbaseWallet;
default:
return false;
}
}
// 打开钱包应用
private async openWalletApp(url: string, walletType: string): Promise<void> {
const startTime = Date.now();
try {
// 尝试打开深链接
window.location.href = url;
// 检测是否成功打开应用
const checkAppOpened = () => {
const elapsed = Date.now() - startTime;
// 如果页面在短时间内失去焦点,说明应用可能已打开
return elapsed < 2000 && document.hidden;
};
// 等待一段时间检查应用是否打开
await new Promise(resolve => setTimeout(resolve, 1500));
if (!checkAppOpened()) {
// 应用可能未打开,显示备用选项
this.showWalletOptions(walletType);
}
} catch (error) {
console.error('Failed to open wallet app:', error);
this.showWalletOptions(walletType);
}
}
// 提示安装钱包
private async promptWalletInstallation(walletType: string): Promise<void> {
const walletConfig = this.supportedWallets.get(walletType);
if (!walletConfig) return;
const installUrl = this.getInstallUrl(walletConfig);
const shouldInstall = confirm(
`${walletConfig.name} is not installed. Would you like to install it?`
);
if (shouldInstall) {
window.open(installUrl, '_blank');
}
}
// 获取安装URL
private getInstallUrl(config: WalletConfig): string {
switch (this.currentPlatform) {
case 'ios':
return config.appStore || config.fallbackUrl;
case 'android':
return config.playStore || config.fallbackUrl;
default:
return config.fallbackUrl;
}
}
// 显示钱包选项
private showWalletOptions(walletType: string): void {
const walletConfig = this.supportedWallets.get(walletType);
if (!walletConfig) return;
// 创建选项对话框
const modal = this.createWalletModal(walletConfig);
document.body.appendChild(modal);
}
// 创建钱包选择模态框
private createWalletModal(config: WalletConfig): HTMLElement {
const modal = document.createElement('div');
modal.className = 'wallet-modal';
modal.innerHTML = `
<div class="wallet-modal-content">
<h3>Open ${config.name}</h3>
<p>Choose how to continue:</p>
<button class="wallet-option" data-action="retry">
Try Again
</button>
<button class="wallet-option" data-action="install">
Install ${config.name}
</button>
<button class="wallet-option" data-action="copy">
Copy Link
</button>
<button class="wallet-close">Close</button>
</div>
`;
// 添加事件监听
modal.addEventListener('click', (e) => {
const target = e.target as HTMLElement;
if (target.classList.contains('wallet-close')) {
modal.remove();
} else if (target.classList.contains('wallet-option')) {
const action = target.dataset.action;
this.handleModalAction(action, config);
modal.remove();
}
});
return modal;
}
// 处理模态框操作
private handleModalAction(action: string | undefined, config: WalletConfig): void {
switch (action) {
case 'retry':
// 重试打开应用
break;
case 'install':
window.open(this.getInstallUrl(config), '_blank');
break;
case 'copy':
// 复制链接到剪贴板
break;
}
}
// 设置深链接处理器
private setupDeepLinkHandlers(): void {
// 监听页面可见性变化(应用回到前台)
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
this.handleAppResume();
}
});
// 监听URL参数(钱包回调)
this.handleWalletCallback();
}
// 处理应用恢复
private handleAppResume(): void {
// 检查是否有待处理的钱包操作
const pendingAction = localStorage.getItem('pendingWalletAction');
if (pendingAction) {
try {
const actionData = JSON.parse(pendingAction);
this.processPendingAction(actionData);
localStorage.removeItem('pendingWalletAction');
} catch (error) {
console.error('Failed to process pending action:', error);
}
}
}
// 处理钱包回调
private handleWalletCallback(): void {
const urlParams = new URLSearchParams(window.location.search);
const walletResponse = urlParams.get('walletResponse');
if (walletResponse) {
try {
const response = JSON.parse(decodeURIComponent(walletResponse));
this.processWalletResponse(response);
} catch (error) {
console.error('Failed to process wallet response:', error);
}
}
}
// 处理钱包响应
private processWalletResponse(response: any): void {
// 触发相应的事件
const event = new CustomEvent('walletResponse', {
detail: response
});
window.dispatchEvent(event);
}
// 处理待处理操作
private processPendingAction(actionData: any): void {
// 根据操作类型处理相应逻辑
console.log('Processing pending action:', actionData);
}
}
// 钱包配置接口
interface WalletConfig {
name: string;
schemes: {
ios: string;
android: string;
universalLink?: string;
};
appStore?: string;
playStore?: string;
fallbackUrl?: string;
}
移动端适配策略:
// 移动端钱包适配器
class MobileWalletAdapter {
private deepLinkManager: MobileWalletDeepLink;
private fallbackProvider: any = null;
constructor() {
this.deepLinkManager = new MobileWalletDeepLink();
this.setupMobileOptimizations();
}
// 移动端优化设置
private setupMobileOptimizations(): void {
// 禁用移动端的缩放
const viewport = document.querySelector('meta[name="viewport"]');
if (viewport) {
viewport.setAttribute('content',
'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'
);
}
// 优化触摸体验
document.body.style.touchAction = 'manipulation';
// 监听网络状态
if ('onLine' in navigator) {
window.addEventListener('online', this.handleNetworkOnline.bind(this));
window.addEventListener('offline', this.handleNetworkOffline.bind(this));
}
}
// 网络状态处理
private handleNetworkOnline(): void {
console.log('Network restored');
// 重新尝试连接钱包
}
private handleNetworkOffline(): void {
console.log('Network lost');
// 显示离线提示
this.showOfflineMessage();
}
// 显示离线消息
private showOfflineMessage(): void {
const message = document.createElement('div');
message.className = 'offline-message';
message.textContent = 'No internet connection. Please check your network.';
document.body.appendChild(message);
setTimeout(() => {
message.remove();
}, 5000);
}
// 智能钱包检测
async detectAvailableWallets(): Promise<string[]> {
const availableWallets: string[] = [];
// 检测注入的钱包(WebView内)
if ((window as any).ethereum) {
if ((window as any).ethereum.isMetaMask) {
availableWallets.push('metamask');
}
if ((window as any).ethereum.isCoinbaseWallet) {
availableWallets.push('coinbase');
}
if ((window as any).ethereum.isTrustWallet) {
availableWallets.push('trust');
}
}
// 在移动端,假设流行钱包都可用(通过深链接)
if (availableWallets.length === 0 && this.isMobileDevice()) {
availableWallets.push('metamask', 'trust', 'coinbase');
}
return availableWallets;
}
// 检测是否为移动设备
private isMobileDevice(): boolean {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
.test(navigator.userAgent);
}
// 智能连接策略
async smartConnect(preferredWallet?: string): Promise<any> {
const availableWallets = await this.detectAvailableWallets();
if (availableWallets.length === 0) {
throw new Error('No wallets available');
}
// 优先使用用户首选钱包
if (preferredWallet && availableWallets.includes(preferredWallet)) {
return await this.connectSpecificWallet(preferredWallet);
}
// 如果在钱包WebView内,直接使用注入的provider
if ((window as any).ethereum && !this.isMobileDevice()) {
return await this.connectInjectedWallet();
}
// 移动端使用深链接连接
if (this.isMobileDevice()) {
return await this.connectViaDeepLink(availableWallets[0]);
}
throw new Error('Unable to connect wallet');
}
// 连接特定钱包
private async connectSpecificWallet(walletType: string): Promise<any> {
if ((window as any).ethereum) {
return await this.connectInjectedWallet();
} else {
return await this.connectViaDeepLink(walletType);
}
}
// 连接注入钱包
private async connectInjectedWallet(): Promise<any> {
try {
const accounts = await (window as any).ethereum.request({
method: 'eth_requestAccounts'
});
return {
accounts,
provider: (window as any).ethereum
};
} catch (error) {
throw new Error('Failed to connect injected wallet');
}
}
// 通过深链接连接
private async connectViaDeepLink(walletType: string): Promise<any> {
await this.deepLinkManager.connectWallet(walletType);
// 等待钱包响应
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Wallet connection timeout'));
}, 30000);
window.addEventListener('walletResponse', (event: any) => {
clearTimeout(timeout);
const response = event.detail;
if (response.success) {
resolve(response);
} else {
reject(new Error(response.error || 'Connection failed'));
}
}, { once: true });
});
}
}
最佳实践要点:
How to implement permission management for wallet connections? Control access to different features.
How to implement permission management for wallet connections? Control access to different features.
考察点:权限控制设计。
答案:
钱包权限管理通过细粒度的权限控制系统,管理DApp对钱包功能的访问权限。包括账户访问、交易权限、签名权限、网络切换等,确保用户资产安全的同时提供良好的使用体验。
权限系统架构设计:
// 钱包权限管理系统
class WalletPermissionManager {
private permissions: Map<string, PermissionSet> = new Map();
private sessionPermissions: Map<string, SessionPermission> = new Map();
private permissionCallbacks: Map<string, Function[]> = new Map();
constructor() {
this.initializeDefaultPermissions();
this.setupPermissionStorage();
}
// 初始化默认权限
private initializeDefaultPermissions(): void {
const defaultPermissions: PermissionDefinition[] = [
{
id: 'account_access',
name: 'Account Access',
description: 'Access to wallet accounts',
category: 'basic',
required: true,
autoGrant: false
},
{
id: 'balance_read',
name: 'Balance Reading',
description: 'Read account balances',
category: 'basic',
required: false,
autoGrant: true
},
{
id: 'transaction_send',
name: 'Send Transactions',
description: 'Send blockchain transactions',
category: 'transaction',
required: false,
autoGrant: false
},
{
id: 'message_sign',
name: 'Message Signing',
description: 'Sign messages and typed data',
category: 'signature',
required: false,
autoGrant: false
},
{
id: 'network_switch',
name: 'Network Switching',
description: 'Switch blockchain networks',
category: 'network',
required: false,
autoGrant: false
},
{
id: 'token_approval',
name: 'Token Approvals',
description: 'Approve token spending',
category: 'transaction',
required: false,
autoGrant: false
}
];
this.registerPermissions(defaultPermissions);
}
// 注册权限定义
registerPermissions(permissions: PermissionDefinition[]): void {
permissions.forEach(permission => {
// 存储权限定义
this.storePermissionDefinition(permission);
});
}
// 请求权限
async requestPermissions(
origin: string,
requestedPermissions: string[],
context?: PermissionContext
): Promise<PermissionGrantResult> {
try {
// 验证请求来源
if (!this.isValidOrigin(origin)) {
throw new Error('Invalid origin');
}
// 获取现有权限
const existingPermissions = this.permissions.get(origin) || {
origin,
grants: new Map(),
createdAt: Date.now(),
lastUsed: Date.now()
};
const result: PermissionGrantResult = {
granted: [],
denied: [],
pending: []
};
// 处理每个权限请求
for (const permissionId of requestedPermissions) {
const grantResult = await this.processPermissionRequest(
origin,
permissionId,
existingPermissions,
context
);
switch (grantResult.status) {
case 'granted':
result.granted.push(permissionId);
break;
case 'denied':
result.denied.push(permissionId);
break;
case 'pending':
result.pending.push(permissionId);
break;
}
}
// 保存更新后的权限
this.permissions.set(origin, existingPermissions);
this.savePermissions();
return result;
} catch (error) {
throw new Error(`Permission request failed: ${error.message}`);
}
}
// 处理单个权限请求
private async processPermissionRequest(
origin: string,
permissionId: string,
permissionSet: PermissionSet,
context?: PermissionContext
): Promise<{ status: 'granted' | 'denied' | 'pending' }> {
// 检查是否已有权限
const existingGrant = permissionSet.grants.get(permissionId);
if (existingGrant && existingGrant.status === 'granted' &&
!this.isPermissionExpired(existingGrant)) {
return { status: 'granted' };
}
// 获取权限定义
const permission = this.getPermissionDefinition(permissionId);
if (!permission) {
return { status: 'denied' };
}
// 自动授权的权限
if (permission.autoGrant) {
permissionSet.grants.set(permissionId, {
permissionId,
status: 'granted',
grantedAt: Date.now(),
expiresAt: Date.now() + (24 * 60 * 60 * 1000), // 24小时
conditions: []
});
return { status: 'granted' };
}
// 需要用户确认的权限
const userResponse = await this.requestUserApproval(
origin,
permission,
context
);
const grant: PermissionGrant = {
permissionId,
status: userResponse.approved ? 'granted' : 'denied',
grantedAt: Date.now(),
expiresAt: userResponse.approved ?
(userResponse.duration ? Date.now() + userResponse.duration : null) :
null,
conditions: userResponse.conditions || []
};
permissionSet.grants.set(permissionId, grant);
return { status: grant.status };
}
// 请求用户授权
private async requestUserApproval(
origin: string,
permission: PermissionDefinition,
context?: PermissionContext
): Promise<UserApprovalResponse> {
return new Promise((resolve) => {
// 创建权限请求对话框
const modal = this.createPermissionModal(origin, permission, context);
// 处理用户响应
const handleResponse = (approved: boolean, options: any = {}) => {
modal.remove();
resolve({
approved,
duration: options.duration,
conditions: options.conditions
});
};
// 添加事件监听
modal.addEventListener('permission-response', (event: any) => {
handleResponse(event.detail.approved, event.detail.options);
});
document.body.appendChild(modal);
});
}
// 创建权限请求模态框
private createPermissionModal(
origin: string,
permission: PermissionDefinition,
context?: PermissionContext
): HTMLElement {
const modal = document.createElement('div');
modal.className = 'permission-modal';
modal.innerHTML = `
<div class="permission-modal-content">
<div class="permission-header">
<h3>Permission Request</h3>
<div class="origin-info">${this.formatOrigin(origin)}</div>
</div>
<div class="permission-details">
<div class="permission-icon">🔐</div>
<h4>${permission.name}</h4>
<p>${permission.description}</p>
${context ? `
<div class="permission-context">
<strong>Context:</strong> ${context.description}
</div>
` : ''}
</div>
<div class="permission-options">
<div class="duration-options">
<label>
<input type="radio" name="duration" value="session" checked>
This session only
</label>
<label>
<input type="radio" name="duration" value="24h">
24 hours
</label>
<label>
<input type="radio" name="duration" value="forever">
Forever (until revoked)
</label>
</div>
${this.renderPermissionConditions(permission)}
</div>
<div class="permission-actions">
<button class="btn-deny">Deny</button>
<button class="btn-allow">Allow</button>
</div>
</div>
`;
// 添加事件处理
this.setupModalEvents(modal);
return modal;
}
// 设置模态框事件
private setupModalEvents(modal: HTMLElement): void {
const allowBtn = modal.querySelector('.btn-allow') as HTMLButtonElement;
const denyBtn = modal.querySelector('.btn-deny') as HTMLButtonElement;
const durationInputs = modal.querySelectorAll('input[name="duration"]');
allowBtn.addEventListener('click', () => {
const selectedDuration = (modal.querySelector('input[name="duration"]:checked') as HTMLInputElement)?.value;
const duration = this.parseDuration(selectedDuration);
const event = new CustomEvent('permission-response', {
detail: {
approved: true,
options: { duration }
}
});
modal.dispatchEvent(event);
});
denyBtn.addEventListener('click', () => {
const event = new CustomEvent('permission-response', {
detail: { approved: false }
});
modal.dispatchEvent(event);
});
}
// 解析持续时间
private parseDuration(duration: string): number | null {
switch (duration) {
case 'session':
return null; // 会话级权限
case '24h':
return 24 * 60 * 60 * 1000;
case 'forever':
return null;
default:
return 60 * 60 * 1000; // 默认1小时
}
}
// 检查权限
checkPermission(origin: string, permissionId: string): boolean {
const permissionSet = this.permissions.get(origin);
if (!permissionSet) return false;
const grant = permissionSet.grants.get(permissionId);
if (!grant || grant.status !== 'granted') return false;
// 检查是否过期
if (this.isPermissionExpired(grant)) {
// 移除过期权限
permissionSet.grants.delete(permissionId);
this.savePermissions();
return false;
}
// 更新最后使用时间
permissionSet.lastUsed = Date.now();
return true;
}
// 权限拦截器
createPermissionInterceptor(origin: string): PermissionInterceptor {
return {
// 账户访问拦截
requestAccounts: async () => {
if (!this.checkPermission(origin, 'account_access')) {
const result = await this.requestPermissions(origin, ['account_access']);
if (!result.granted.includes('account_access')) {
throw new Error('Account access denied');
}
}
return this.getAuthorizedAccounts(origin);
},
// 交易发送拦截
sendTransaction: async (txData: any) => {
if (!this.checkPermission(origin, 'transaction_send')) {
const result = await this.requestPermissions(origin, ['transaction_send'], {
type: 'transaction',
description: `Send transaction to ${txData.to}`,
metadata: txData
});
if (!result.granted.includes('transaction_send')) {
throw new Error('Transaction permission denied');
}
}
return this.executeTransaction(origin, txData);
},
// 消息签名拦截
signMessage: async (message: string) => {
if (!this.checkPermission(origin, 'message_sign')) {
const result = await this.requestPermissions(origin, ['message_sign'], {
type: 'signature',
description: 'Sign message',
metadata: { message }
});
if (!result.granted.includes('message_sign')) {
throw new Error('Signature permission denied');
}
}
return this.executeSignature(origin, message);
},
// 网络切换拦截
switchNetwork: async (chainId: string) => {
if (!this.checkPermission(origin, 'network_switch')) {
const result = await this.requestPermissions(origin, ['network_switch'], {
type: 'network',
description: `Switch to network ${chainId}`,
metadata: { chainId }
});
if (!result.granted.includes('network_switch')) {
throw new Error('Network switch permission denied');
}
}
return this.executeNetworkSwitch(origin, chainId);
}
};
}
// 撤销权限
revokePermission(origin: string, permissionId?: string): void {
const permissionSet = this.permissions.get(origin);
if (!permissionSet) return;
if (permissionId) {
// 撤销特定权限
permissionSet.grants.delete(permissionId);
} else {
// 撤销所有权限
permissionSet.grants.clear();
}
this.savePermissions();
this.notifyPermissionChange(origin, permissionId);
}
// 获取权限列表
getPermissions(origin: string): PermissionInfo[] {
const permissionSet = this.permissions.get(origin);
if (!permissionSet) return [];
const result: PermissionInfo[] = [];
permissionSet.grants.forEach((grant, permissionId) => {
const definition = this.getPermissionDefinition(permissionId);
if (definition) {
result.push({
id: permissionId,
name: definition.name,
description: definition.description,
category: definition.category,
status: grant.status,
grantedAt: grant.grantedAt,
expiresAt: grant.expiresAt,
lastUsed: permissionSet.lastUsed
});
}
});
return result;
}
// 权限过期检查
private isPermissionExpired(grant: PermissionGrant): boolean {
if (!grant.expiresAt) return false;
return Date.now() > grant.expiresAt;
}
// 清理过期权限
cleanupExpiredPermissions(): void {
this.permissions.forEach((permissionSet, origin) => {
let hasChanges = false;
permissionSet.grants.forEach((grant, permissionId) => {
if (this.isPermissionExpired(grant)) {
permissionSet.grants.delete(permissionId);
hasChanges = true;
}
});
if (hasChanges) {
this.notifyPermissionChange(origin);
}
});
if (this.permissions.size > 0) {
this.savePermissions();
}
}
// 权限变更通知
private notifyPermissionChange(origin: string, permissionId?: string): void {
const callbacks = this.permissionCallbacks.get(origin) || [];
callbacks.forEach(callback => {
try {
callback({ origin, permissionId, type: 'revoked' });
} catch (error) {
console.error('Permission callback error:', error);
}
});
}
// 辅助方法
private isValidOrigin(origin: string): boolean {
try {
new URL(origin);
return true;
} catch {
return false;
}
}
private formatOrigin(origin: string): string {
try {
const url = new URL(origin);
return url.hostname;
} catch {
return origin;
}
}
private getPermissionDefinition(permissionId: string): PermissionDefinition | null {
// 从存储中获取权限定义
return null; // 实现省略
}
private storePermissionDefinition(permission: PermissionDefinition): void {
// 存储权限定义到本地存储
}
private setupPermissionStorage(): void {
// 从本地存储加载权限
this.loadPermissions();
// 定期清理过期权限
setInterval(() => {
this.cleanupExpiredPermissions();
}, 60000); // 每分钟检查一次
}
private loadPermissions(): void {
const stored = localStorage.getItem('walletPermissions');
if (stored) {
try {
const data = JSON.parse(stored);
this.permissions = new Map(Object.entries(data));
} catch (error) {
console.error('Failed to load permissions:', error);
}
}
}
private savePermissions(): void {
const data = Object.fromEntries(this.permissions);
localStorage.setItem('walletPermissions', JSON.stringify(data));
}
// 占位实现
private async getAuthorizedAccounts(origin: string): Promise<string[]> { return []; }
private async executeTransaction(origin: string, txData: any): Promise<string> { return ''; }
private async executeSignature(origin: string, message: string): Promise<string> { return ''; }
private async executeNetworkSwitch(origin: string, chainId: string): Promise<void> { }
private renderPermissionConditions(permission: PermissionDefinition): string { return ''; }
}
// 类型定义
interface PermissionDefinition {
id: string;
name: string;
description: string;
category: 'basic' | 'transaction' | 'signature' | 'network';
required: boolean;
autoGrant: boolean;
}
interface PermissionSet {
origin: string;
grants: Map<string, PermissionGrant>;
createdAt: number;
lastUsed: number;
}
interface PermissionGrant {
permissionId: string;
status: 'granted' | 'denied';
grantedAt: number;
expiresAt: number | null;
conditions: string[];
}
interface PermissionGrantResult {
granted: string[];
denied: string[];
pending: string[];
}
interface PermissionContext {
type: string;
description: string;
metadata?: any;
}
interface UserApprovalResponse {
approved: boolean;
duration?: number | null;
conditions?: string[];
}
interface PermissionInterceptor {
requestAccounts(): Promise<string[]>;
sendTransaction(txData: any): Promise<string>;
signMessage(message: string): Promise<string>;
switchNetwork(chainId: string): Promise<void>;
}
interface PermissionInfo {
id: string;
name: string;
description: string;
category: string;
status: 'granted' | 'denied';
grantedAt: number;
expiresAt: number | null;
lastUsed: number;
}
interface SessionPermission {
origin: string;
permissions: string[];
expiresAt: number;
}
权限控制最佳实践:
What is Account Abstraction? What impact does it have on wallet integration?
What is Account Abstraction? What impact does it have on wallet integration?
考察点:新技术趋势理解。
答案:
账户抽象(Account Abstraction,AA)是以太坊的重要升级,通过EIP-4337实现,将外部拥有账户(EOA)的功能抽象到智能合约层。它允许用户使用智能合约作为钱包账户,实现可编程的账户逻辑,支持Gas代付、多签验证、社交恢复等高级功能。
账户抽象核心概念:
// 账户抽象钱包接口
interface AbstractAccountWallet {
// 基础账户信息
getAccountAddress(): string;
getAccountType(): 'eoa' | 'smart-account';
// 智能账户特有功能
executeUserOperation(userOp: UserOperation): Promise<string>;
simulateUserOperation(userOp: UserOperation): Promise<SimulationResult>;
// Gas抽象
estimateUserOperationGas(userOp: UserOperation): Promise<GasEstimate>;
getSponsoredUserOperation(userOp: UserOperation): Promise<UserOperation>;
// 批量操作
createBatchUserOperation(calls: Call[]): Promise<UserOperation>;
// 权限管理
addSessionKey(sessionKey: SessionKey): Promise<void>;
revokeSessionKey(keyId: string): Promise<void>;
}
// UserOperation结构
interface UserOperation {
sender: string; // 智能账户地址
nonce: bigint; // 防重放随机数
initCode: string; // 账户创建代码(首次使用)
callData: string; // 执行数据
callGasLimit: bigint; // 执行Gas限制
verificationGasLimit: bigint; // 验证Gas限制
preVerificationGas: bigint; // 预验证Gas
maxFeePerGas: bigint; // 最大Gas费用
maxPriorityFeePerGas: bigint; // 最大优先费用
paymasterAndData: string; // Paymaster数据
signature: string; // 签名数据
}
// 会话密钥配置
interface SessionKey {
keyAddress: string;
permissions: KeyPermission[];
validUntil: number;
validAfter: number;
}
interface KeyPermission {
target: string; // 目标合约
selector: string; // 函数选择器
rules: PermissionRule[]; // 权限规则
}
智能账户钱包实现:
class SmartAccountWallet implements AbstractAccountWallet {
private provider: JsonRpcProvider;
private bundlerUrl: string;
private paymasterUrl: string;
private accountFactory: string;
private entryPoint: string;
private ownerKey: string;
private accountAddress: string | null = null;
constructor(config: SmartAccountConfig) {
this.provider = new JsonRpcProvider(config.rpcUrl);
this.bundlerUrl = config.bundlerUrl;
this.paymasterUrl = config.paymasterUrl;
this.accountFactory = config.accountFactory;
this.entryPoint = config.entryPoint;
this.ownerKey = config.ownerKey;
}
// 获取账户地址(计算确定性地址)
getAccountAddress(): string {
if (!this.accountAddress) {
this.accountAddress = this.calculateAccountAddress();
}
return this.accountAddress;
}
getAccountType(): 'eoa' | 'smart-account' {
return 'smart-account';
}
// 计算智能账户地址
private calculateAccountAddress(): string {
const initCode = this.getInitCode();
const salt = keccak256(this.ownerKey);
// 使用CREATE2计算地址
return getCreate2Address(
this.accountFactory,
salt,
keccak256(initCode)
);
}
// 获取初始化代码
private getInitCode(): string {
const factoryInterface = new Interface([
'function createAccount(address owner, uint256 salt) returns (address)'
]);
return concat([
this.accountFactory,
factoryInterface.encodeFunctionData('createAccount', [
this.ownerKey,
keccak256(this.ownerKey)
])
]);
}
// 执行UserOperation
async executeUserOperation(userOp: UserOperation): Promise<string> {
try {
// 1. 填充UserOperation
const filledUserOp = await this.fillUserOperation(userOp);
// 2. 签名UserOperation
const signedUserOp = await this.signUserOperation(filledUserOp);
// 3. 提交到Bundler
const userOpHash = await this.submitUserOperation(signedUserOp);
// 4. 等待执行结果
const receipt = await this.waitForUserOperationReceipt(userOpHash);
return receipt.transactionHash;
} catch (error) {
throw new Error(`UserOperation execution failed: ${error.message}`);
}
}
// 填充UserOperation
private async fillUserOperation(userOp: Partial<UserOperation>): Promise<UserOperation> {
const accountAddress = this.getAccountAddress();
// 检查账户是否已部署
const code = await this.provider.getCode(accountAddress);
const isDeployed = code !== '0x';
const filledUserOp: UserOperation = {
sender: accountAddress,
nonce: userOp.nonce || await this.getNonce(),
initCode: isDeployed ? '0x' : this.getInitCode(),
callData: userOp.callData || '0x',
callGasLimit: userOp.callGasLimit || 0n,
verificationGasLimit: userOp.verificationGasLimit || 0n,
preVerificationGas: userOp.preVerificationGas || 0n,
maxFeePerGas: userOp.maxFeePerGas || 0n,
maxPriorityFeePerGas: userOp.maxPriorityFeePerGas || 0n,
paymasterAndData: userOp.paymasterAndData || '0x',
signature: '0x'
};
// 估算Gas
if (filledUserOp.callGasLimit === 0n) {
const gasEstimate = await this.estimateUserOperationGas(filledUserOp);
filledUserOp.callGasLimit = gasEstimate.callGasLimit;
filledUserOp.verificationGasLimit = gasEstimate.verificationGasLimit;
filledUserOp.preVerificationGas = gasEstimate.preVerificationGas;
}
// 设置Gas价格
if (filledUserOp.maxFeePerGas === 0n) {
const feeData = await this.provider.getFeeData();
filledUserOp.maxFeePerGas = feeData.maxFeePerGas || parseUnits('20', 'gwei');
filledUserOp.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas || parseUnits('1', 'gwei');
}
return filledUserOp;
}
// 签名UserOperation
private async signUserOperation(userOp: UserOperation): Promise<UserOperation> {
const userOpHash = this.getUserOperationHash(userOp);
const signature = await this.signMessage(userOpHash);
return {
...userOp,
signature
};
}
// 计算UserOperation哈希
private getUserOperationHash(userOp: UserOperation): string {
const packedUserOp = packUserOperation(userOp);
const encoded = AbiCoder.defaultAbiCoder().encode(
['bytes32', 'address', 'uint256'],
[keccak256(packedUserOp), this.entryPoint, this.provider.network.chainId]
);
return keccak256(encoded);
}
// 提交UserOperation到Bundler
private async submitUserOperation(userOp: UserOperation): Promise<string> {
const response = await fetch(this.bundlerUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'eth_sendUserOperation',
params: [userOp, this.entryPoint]
})
});
const result = await response.json();
if (result.error) {
throw new Error(result.error.message);
}
return result.result;
}
// 等待UserOperation执行结果
private async waitForUserOperationReceipt(userOpHash: string): Promise<UserOperationReceipt> {
const maxRetries = 30;
const retryInterval = 2000;
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(this.bundlerUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'eth_getUserOperationReceipt',
params: [userOpHash]
})
});
const result = await response.json();
if (result.result) {
return result.result;
}
await new Promise(resolve => setTimeout(resolve, retryInterval));
} catch (error) {
if (i === maxRetries - 1) {
throw error;
}
}
}
throw new Error('UserOperation receipt timeout');
}
// Gas代付功能
async getSponsoredUserOperation(userOp: UserOperation): Promise<UserOperation> {
if (!this.paymasterUrl) {
return userOp;
}
try {
const response = await fetch(this.paymasterUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'pm_sponsorUserOperation',
params: [userOp, { type: 'payg' }] // Pay-as-you-go
})
});
const result = await response.json();
if (result.error) {
console.warn('Paymaster sponsorship failed:', result.error.message);
return userOp;
}
return {
...userOp,
paymasterAndData: result.result.paymasterAndData,
callGasLimit: BigInt(result.result.callGasLimit),
verificationGasLimit: BigInt(result.result.verificationGasLimit),
preVerificationGas: BigInt(result.result.preVerificationGas)
};
} catch (error) {
console.warn('Paymaster request failed:', error);
return userOp;
}
}
// 批量操作
async createBatchUserOperation(calls: Call[]): Promise<UserOperation> {
const accountInterface = new Interface([
'function executeBatch(address[] targets, uint256[] values, bytes[] calldatas)'
]);
const targets = calls.map(call => call.to);
const values = calls.map(call => call.value || 0);
const calldatas = calls.map(call => call.data);
const callData = accountInterface.encodeFunctionData('executeBatch', [
targets,
values,
calldatas
]);
return await this.fillUserOperation({ callData });
}
// 会话密钥管理
async addSessionKey(sessionKey: SessionKey): Promise<void> {
const accountInterface = new Interface([
'function addSessionKey(address key, uint48 validUntil, uint48 validAfter, bytes32[] permissions)'
]);
const permissionHashes = sessionKey.permissions.map(perm =>
keccak256(AbiCoder.defaultAbiCoder().encode(
['address', 'bytes4'],
[perm.target, perm.selector]
))
);
const callData = accountInterface.encodeFunctionData('addSessionKey', [
sessionKey.keyAddress,
sessionKey.validUntil,
sessionKey.validAfter,
permissionHashes
]);
const userOp = await this.fillUserOperation({ callData });
await this.executeUserOperation(userOp);
}
// 辅助方法
private async getNonce(): Promise<bigint> {
const entryPointInterface = new Interface([
'function getNonce(address sender, uint192 key) view returns (uint256)'
]);
const entryPointContract = new Contract(this.entryPoint, entryPointInterface, this.provider);
return await entryPointContract.getNonce(this.getAccountAddress(), 0);
}
private async signMessage(message: string): Promise<string> {
const wallet = new Wallet(this.ownerKey);
return await wallet.signMessage(arrayify(message));
}
async estimateUserOperationGas(userOp: UserOperation): Promise<GasEstimate> {
// Gas估算实现
return {
callGasLimit: parseUnits('100000', 'wei'),
verificationGasLimit: parseUnits('100000', 'wei'),
preVerificationGas: parseUnits('21000', 'wei')
};
}
async simulateUserOperation(userOp: UserOperation): Promise<SimulationResult> {
// 模拟执行实现
return {
success: true,
returnData: '0x',
gasUsed: 0n
};
}
}
// 智能账户钱包管理器
class SmartAccountWalletManager {
private wallets: Map<string, SmartAccountWallet> = new Map();
// 创建智能账户钱包
async createSmartWallet(config: SmartAccountConfig): Promise<SmartAccountWallet> {
const wallet = new SmartAccountWallet(config);
const address = wallet.getAccountAddress();
this.wallets.set(address, wallet);
return wallet;
}
// 检测钱包类型
async detectWalletType(address: string): Promise<'eoa' | 'smart-account'> {
const code = await this.provider.getCode(address);
return code === '0x' ? 'eoa' : 'smart-account';
}
// 适配不同账户类型
async createUniversalTransaction(params: TransactionParams): Promise<any> {
const walletType = await this.detectWalletType(params.from);
if (walletType === 'smart-account') {
// 使用UserOperation
const wallet = this.wallets.get(params.from);
if (!wallet) throw new Error('Smart wallet not found');
return await wallet.executeUserOperation({
sender: params.from,
callData: params.data,
// ... 其他参数
} as UserOperation);
} else {
// 使用传统交易
return await this.sendLegacyTransaction(params);
}
}
private async sendLegacyTransaction(params: TransactionParams): Promise<string> {
// 传统EOA交易实现
return '';
}
}
// 类型定义补充
interface SmartAccountConfig {
rpcUrl: string;
bundlerUrl: string;
paymasterUrl?: string;
accountFactory: string;
entryPoint: string;
ownerKey: string;
}
interface Call {
to: string;
value?: bigint;
data: string;
}
interface GasEstimate {
callGasLimit: bigint;
verificationGasLimit: bigint;
preVerificationGas: bigint;
}
interface SimulationResult {
success: boolean;
returnData: string;
gasUsed: bigint;
}
interface UserOperationReceipt {
userOpHash: string;
transactionHash: string;
blockNumber: number;
success: boolean;
}
interface TransactionParams {
from: string;
to: string;
value?: bigint;
data?: string;
gas?: bigint;
}
interface PermissionRule {
ruleType: 'allowlist' | 'gas-limit' | 'value-limit';
data: string;
}
// 辅助函数(占位实现)
function packUserOperation(userOp: UserOperation): string { return '0x'; }
function getCreate2Address(factory: string, salt: string, codeHash: string): string { return '0x'; }
function keccak256(data: string): string { return '0x'; }
function concat(arrays: string[]): string { return '0x'; }
function parseUnits(value: string, unit: string): bigint { return 0n; }
function arrayify(data: string): Uint8Array { return new Uint8Array(); }
对钱包集成的影响:
How to optimize the user experience of wallet interactions? Reduce user operation steps.
How to optimize the user experience of wallet interactions? Reduce user operation steps.
考察点:用户体验设计。
答案:
钱包交互用户体验优化通过简化流程、智能预判、批量操作、状态反馈等方式,减少用户操作步骤和认知负担。关键在于平衡安全性与便利性,提供直观、流畅的Web3交互体验。
用户体验优化策略:
// 钱包用户体验优化管理器
class WalletUXOptimizer {
private transactionBatcher: TransactionBatcher;
private gasOptimizer: GasOptimizer;
private stateManager: UXStateManager;
private preferencesManager: UserPreferencesManager;
constructor() {
this.transactionBatcher = new TransactionBatcher();
this.gasOptimizer = new GasOptimizer();
this.stateManager = new UXStateManager();
this.preferencesManager = new UserPreferencesManager();
}
// 智能交互流程优化
async optimizeInteractionFlow(interaction: UserInteraction): Promise<OptimizedFlow> {
const userPrefs = await this.preferencesManager.getUserPreferences();
const context = await this.analyzeInteractionContext(interaction);
return {
steps: await this.minimizeSteps(interaction, context, userPrefs),
batching: await this.identifyBatchingOpportunities(interaction),
gasOptimization: await this.optimizeGasUsage(interaction),
preApprovals: await this.suggestPreApprovals(interaction, userPrefs),
shortcuts: await this.createShortcuts(interaction, context)
};
}
// 步骤最小化
private async minimizeSteps(
interaction: UserInteraction,
context: InteractionContext,
preferences: UserPreferences
): Promise<OptimizedStep[]> {
const steps: OptimizedStep[] = [];
switch (interaction.type) {
case 'token_swap':
steps.push(...await this.optimizeSwapFlow(interaction, context, preferences));
break;
case 'nft_purchase':
steps.push(...await this.optimizeNFTFlow(interaction, context, preferences));
break;
case 'defi_interaction':
steps.push(...await this.optimizeDeFiFlow(interaction, context, preferences));
break;
default:
steps.push(...await this.optimizeGenericFlow(interaction, context, preferences));
}
return steps;
}
// 代币交换流程优化
private async optimizeSwapFlow(
interaction: UserInteraction,
context: InteractionContext,
preferences: UserPreferences
): Promise<OptimizedStep[]> {
const steps: OptimizedStep[] = [];
const swapParams = interaction.params as SwapParams;
// 1. 智能滑点设置
if (!swapParams.slippage && preferences.autoSlippage) {
swapParams.slippage = await this.calculateOptimalSlippage(swapParams);
}
// 2. 自动批准优化
const approvalStep = await this.optimizeApprovalStep(swapParams, preferences);
if (approvalStep) steps.push(approvalStep);
// 3. 交换执行
steps.push({
type: 'transaction',
title: `Swap ${swapParams.amountIn} ${swapParams.tokenIn.symbol} for ${swapParams.tokenOut.symbol}`,
description: 'Execute the token swap',
autoConfirm: preferences.autoConfirmSwaps && swapParams.amountUSD < preferences.autoConfirmLimit,
estimatedTime: 15,
gasEstimate: await this.gasOptimizer.estimateSwapGas(swapParams)
});
return steps;
}
// 审批步骤优化
private async optimizeApprovalStep(
params: SwapParams,
preferences: UserPreferences
): Promise<OptimizedStep | null> {
const currentAllowance = await this.checkTokenAllowance(
params.tokenIn.address,
params.spender
);
if (currentAllowance >= params.amountIn) {
return null; // 不需要审批
}
// 智能审批额度
const approvalAmount = preferences.infiniteApproval ?
ethers.constants.MaxUint256 :
params.amountIn * BigInt(preferences.approvalMultiplier || 2);
return {
type: 'approval',
title: `Approve ${params.tokenIn.symbol}`,
description: preferences.infiniteApproval ?
'Approve unlimited spending (one-time setup)' :
`Approve ${formatUnits(approvalAmount)} ${params.tokenIn.symbol}`,
autoConfirm: preferences.autoApprove && approvalAmount <= preferences.autoApprovalLimit,
estimatedTime: 10,
gasEstimate: await this.gasOptimizer.estimateApprovalGas(params.tokenIn.address),
params: { token: params.tokenIn, amount: approvalAmount, spender: params.spender }
};
}
// 批量操作识别
async identifyBatchingOpportunities(interaction: UserInteraction): Promise<BatchingOpportunity[]> {
const opportunities: BatchingOpportunity[] = [];
const pendingOperations = this.stateManager.getPendingOperations();
// 检查相同类型操作
const similarOps = pendingOperations.filter(op =>
op.type === interaction.type &&
op.targetContract === interaction.targetContract
);
if (similarOps.length > 0) {
opportunities.push({
type: 'same_contract',
operations: [interaction, ...similarOps],
gasSavings: await this.calculateBatchGasSavings(similarOps),
description: `Batch ${similarOps.length + 1} operations to save gas`
});
}
// 检查相关操作(如 approve + swap)
const relatedOps = await this.findRelatedOperations(interaction, pendingOperations);
if (relatedOps.length > 0) {
opportunities.push({
type: 'related_operations',
operations: [interaction, ...relatedOps],
gasSavings: await this.calculateBatchGasSavings(relatedOps),
description: 'Combine related operations in one transaction'
});
}
return opportunities;
}
// Gas使用优化
async optimizeGasUsage(interaction: UserInteraction): Promise<GasOptimization> {
const gasEstimate = await this.gasOptimizer.estimateGas(interaction);
const gasPrice = await this.gasOptimizer.getOptimalGasPrice();
const alternativeRoutes = await this.findAlternativeRoutes(interaction);
return {
standard: gasEstimate,
optimized: await this.gasOptimizer.optimizeGasUsage(interaction),
alternatives: alternativeRoutes,
recommendations: await this.generateGasRecommendations(interaction, gasEstimate)
};
}
// 预批准建议
async suggestPreApprovals(
interaction: UserInteraction,
preferences: UserPreferences
): Promise<PreApprovalSuggestion[]> {
if (!preferences.enablePreApprovals) return [];
const suggestions: PreApprovalSuggestion[] = [];
const userHistory = await this.preferencesManager.getUserInteractionHistory();
// 分析常用合约
const frequentContracts = this.analyzeFrequentContracts(userHistory);
for (const contract of frequentContracts) {
const tokens = await this.getTokensUsedWithContract(contract, userHistory);
for (const token of tokens) {
const currentAllowance = await this.checkTokenAllowance(token.address, contract.address);
if (currentAllowance === BigInt(0)) {
suggestions.push({
token,
spender: contract,
reason: `You frequently use ${token.symbol} with ${contract.name}`,
estimatedGasSavings: await this.calculateApprovalGasSavings(token, contract),
riskLevel: this.assessApprovalRisk(token, contract)
});
}
}
}
return suggestions;
}
}
// 交互流程简化器
class InteractionFlowSimplifier {
// 一键操作封装
async createOneClickOperation(params: OneClickParams): Promise<OneClickOperation> {
const steps = await this.analyzeRequiredSteps(params);
const optimizedSteps = await this.optimizeStepOrder(steps);
const batchedSteps = await this.batchCompatibleSteps(optimizedSteps);
return {
id: this.generateOperationId(),
title: params.title,
description: params.description,
steps: batchedSteps,
totalEstimatedTime: batchedSteps.reduce((sum, step) => sum + step.estimatedTime, 0),
totalGasEstimate: batchedSteps.reduce((sum, step) => sum + step.gasEstimate.total, BigInt(0)),
riskAssessment: await this.assessOperationRisk(batchedSteps)
};
}
// 智能默认值填充
async fillSmartDefaults(params: Partial<TransactionParams>): Promise<TransactionParams> {
const userPrefs = await this.getUserPreferences();
const marketData = await this.getMarketData();
return {
...params,
gasLimit: params.gasLimit || await this.estimateGasLimit(params),
gasPrice: params.gasPrice || await this.getOptimalGasPrice(userPrefs.gasPriceStrategy),
slippage: params.slippage || this.calculateDefaultSlippage(params, marketData),
deadline: params.deadline || (Date.now() + userPrefs.defaultDeadline),
maxFeePerGas: params.maxFeePerGas || await this.calculateMaxFee(userPrefs),
maxPriorityFeePerGas: params.maxPriorityFeePerGas || await this.calculatePriorityFee(userPrefs)
};
}
// 渐进式披露
createProgressiveDisclosure(operation: OneClickOperation): ProgressiveDisclosure {
return {
summary: {
title: operation.title,
description: operation.description,
estimatedTime: operation.totalEstimatedTime,
estimatedCost: operation.totalGasEstimate
},
basic: {
steps: operation.steps.map(step => ({
title: step.title,
description: step.description,
status: 'pending'
}))
},
advanced: {
gasDetails: operation.steps.map(step => step.gasEstimate),
riskAnalysis: operation.riskAssessment,
technicalDetails: operation.steps.map(step => step.technicalDetails)
},
expert: {
rawTransactionData: operation.steps.map(step => step.rawData),
contractInteractions: operation.steps.map(step => step.contractDetails),
securityAudit: operation.riskAssessment.detailedAnalysis
}
};
}
}
// 状态和进度管理
class UXStateManager {
private operationStates = new Map<string, OperationState>();
private progressCallbacks = new Map<string, Function[]>();
// 操作状态跟踪
trackOperation(operation: OneClickOperation): OperationTracker {
const tracker: OperationTracker = {
id: operation.id,
startTime: Date.now(),
currentStep: 0,
totalSteps: operation.steps.length,
status: 'pending',
onProgress: (callback: ProgressCallback) => {
const callbacks = this.progressCallbacks.get(operation.id) || [];
callbacks.push(callback);
this.progressCallbacks.set(operation.id, callbacks);
},
updateProgress: (stepIndex: number, stepStatus: StepStatus, data?: any) => {
const state = this.operationStates.get(operation.id) || {
operation,
currentStep: 0,
stepStates: new Map(),
startTime: Date.now()
};
state.currentStep = stepIndex;
state.stepStates.set(stepIndex, { status: stepStatus, data, timestamp: Date.now() });
this.operationStates.set(operation.id, state);
// 通知进度回调
const callbacks = this.progressCallbacks.get(operation.id) || [];
callbacks.forEach(callback => {
try {
callback({
operationId: operation.id,
currentStep: stepIndex,
totalSteps: operation.steps.length,
stepStatus,
progress: (stepIndex + 1) / operation.steps.length,
data
});
} catch (error) {
console.error('Progress callback error:', error);
}
});
},
complete: (success: boolean, result?: any) => {
const state = this.operationStates.get(operation.id);
if (state) {
state.status = success ? 'completed' : 'failed';
state.endTime = Date.now();
state.result = result;
}
// 清理回调
this.progressCallbacks.delete(operation.id);
}
};
return tracker;
}
// 获取待处理操作
getPendingOperations(): UserInteraction[] {
const pending: UserInteraction[] = [];
this.operationStates.forEach((state, operationId) => {
if (state.status === 'pending' || state.status === 'in_progress') {
pending.push(state.operation as any); // 类型转换简化
}
});
return pending;
}
}
// 用户偏好管理
class UserPreferencesManager {
private preferences: UserPreferences;
constructor() {
this.loadPreferences();
}
async getUserPreferences(): Promise<UserPreferences> {
return this.preferences;
}
async updatePreferences(updates: Partial<UserPreferences>): Promise<void> {
this.preferences = { ...this.preferences, ...updates };
await this.savePreferences();
}
// 学习用户行为
async learnFromUserBehavior(interaction: CompletedInteraction): Promise<void> {
// 分析用户选择模式
const patterns = this.analyzeUserPatterns([interaction]);
// 更新自动化偏好
if (patterns.consistentGasChoice) {
this.preferences.gasPriceStrategy = patterns.preferredGasStrategy;
}
if (patterns.consistentSlippage) {
this.preferences.defaultSlippage = patterns.averageSlippage;
}
// 更新风险偏好
if (patterns.riskTolerance) {
this.preferences.riskTolerance = patterns.riskTolerance;
}
await this.savePreferences();
}
private loadPreferences(): void {
const stored = localStorage.getItem('wallet-ux-preferences');
this.preferences = stored ? JSON.parse(stored) : this.getDefaultPreferences();
}
private async savePreferences(): Promise<void> {
localStorage.setItem('wallet-ux-preferences', JSON.stringify(this.preferences));
}
private getDefaultPreferences(): UserPreferences {
return {
autoSlippage: true,
autoConfirmSwaps: false,
autoConfirmLimit: 100, // USD
autoApprove: false,
autoApprovalLimit: BigInt(1000),
infiniteApproval: false,
approvalMultiplier: 2,
gasPriceStrategy: 'standard',
defaultDeadline: 20 * 60 * 1000, // 20 minutes
enablePreApprovals: true,
riskTolerance: 'medium',
defaultSlippage: 0.5 // 0.5%
};
}
async getUserInteractionHistory(): Promise<InteractionHistory[]> {
// 从本地存储或服务器获取交互历史
return [];
}
}
用户体验最佳实践:
How to handle network delays and timeouts in wallet integration?
How to handle network delays and timeouts in wallet integration?
考察点:网络异常处理。
答案:
钱包集成的网络延迟和超时处理通过多层次的策略实现:超时配置、重试机制、降级策略、用户反馈等。关键在于平衡响应速度与可靠性,为用户提供稳定的Web3交互体验。
网络异常处理架构:
// 网络延迟和超时处理管理器
class NetworkResilienceManager {
private config: NetworkConfig;
private retryPolicy: RetryPolicy;
private timeoutManager: TimeoutManager;
private networkMonitor: NetworkMonitor;
private fallbackProviders: Map<string, FallbackProvider>;
constructor(config: NetworkConfig) {
this.config = config;
this.retryPolicy = new RetryPolicy(config.retry);
this.timeoutManager = new TimeoutManager(config.timeouts);
this.networkMonitor = new NetworkMonitor();
this.fallbackProviders = new Map();
this.setupNetworkMonitoring();
}
// 带超时的请求处理
async executeWithTimeout<T>(
operation: () => Promise<T>,
timeout: number,
operationType: string
): Promise<T> {
const operationId = this.generateOperationId();
try {
// 设置超时控制
const timeoutPromise = this.timeoutManager.createTimeoutPromise(timeout, operationType);
// 执行操作
const result = await Promise.race([
operation(),
timeoutPromise
]);
// 记录成功
this.networkMonitor.recordSuccess(operationType, Date.now());
return result;
} catch (error) {
// 记录失败
this.networkMonitor.recordFailure(operationType, error);
// 判断是否为超时错误
if (this.isTimeoutError(error)) {
throw new WalletTimeoutError(
`Operation ${operationType} timed out after ${timeout}ms`,
operationType,
timeout
);
}
throw error;
}
}
// 带重试的网络请求
async requestWithRetry<T>(
requestFn: () => Promise<T>,
options: RequestOptions
): Promise<T> {
const { operationType, timeout, retryConfig } = options;
const effectiveRetryConfig = retryConfig || this.retryPolicy.getConfig(operationType);
let lastError: Error;
let attempt = 0;
while (attempt <= effectiveRetryConfig.maxRetries) {
try {
// 应用退避延迟
if (attempt > 0) {
const delay = this.calculateBackoffDelay(attempt, effectiveRetryConfig);
await this.delay(delay);
}
// 执行请求
const result = await this.executeWithTimeout(
requestFn,
timeout || this.config.timeouts.default,
operationType
);
return result;
} catch (error) {
lastError = error;
attempt++;
// 检查是否应该重试
if (!this.shouldRetry(error, attempt, effectiveRetryConfig)) {
break;
}
console.warn(`Request ${operationType} failed (attempt ${attempt}/${effectiveRetryConfig.maxRetries}):`, error.message);
}
}
throw new WalletRetryExhaustedError(
`Request ${operationType} failed after ${attempt} attempts`,
lastError,
attempt
);
}
// 智能Provider选择
async executeWithFallback<T>(
primaryOperation: () => Promise<T>,
operationType: string,
options: FallbackOptions = {}
): Promise<T> {
const providers = await this.getAvailableProviders(operationType);
for (let i = 0; i < providers.length; i++) {
const provider = providers[i];
try {
// 使用当前Provider执行操作
const result = await this.executeWithProvider(
primaryOperation,
provider,
operationType,
options
);
// 记录成功的Provider
this.networkMonitor.recordProviderSuccess(provider.url, operationType);
return result;
} catch (error) {
// 记录Provider失败
this.networkMonitor.recordProviderFailure(provider.url, operationType, error);
// 如果是最后一个Provider,抛出错误
if (i === providers.length - 1) {
throw new WalletFallbackExhaustedError(
`All providers failed for operation ${operationType}`,
error,
providers.length
);
}
console.warn(`Provider ${provider.url} failed, trying next provider:`, error.message);
}
}
}
// 网络状态监控
private setupNetworkMonitoring(): void {
// 监听网络状态变化
if (typeof window !== 'undefined') {
window.addEventListener('online', this.handleNetworkOnline.bind(this));
window.addEventListener('offline', this.handleNetworkOffline.bind(this));
}
// 定期健康检查
setInterval(() => {
this.performHealthCheck();
}, this.config.healthCheckInterval || 30000);
}
// 网络恢复处理
private async handleNetworkOnline(): Promise<void> {
console.log('Network restored, performing recovery operations');
// 重新连接钱包
await this.attemptWalletReconnection();
// 恢复待处理操作
await this.resumePendingOperations();
// 触发网络恢复事件
this.emitNetworkEvent('network-restored');
}
// 网络断开处理
private handleNetworkOffline(): void {
console.warn('Network connection lost');
// 暂停所有网络操作
this.pauseNetworkOperations();
// 显示离线提示
this.showOfflineNotification();
// 触发网络断开事件
this.emitNetworkEvent('network-lost');
}
// 健康检查
private async performHealthCheck(): Promise<void> {
const providers = Array.from(this.fallbackProviders.values());
const healthChecks = providers.map(async (provider) => {
try {
const startTime = Date.now();
await this.pingProvider(provider);
const responseTime = Date.now() - startTime;
this.networkMonitor.recordProviderHealth(provider.url, {
status: 'healthy',
responseTime,
timestamp: Date.now()
});
} catch (error) {
this.networkMonitor.recordProviderHealth(provider.url, {
status: 'unhealthy',
error: error.message,
timestamp: Date.now()
});
}
});
await Promise.allSettled(healthChecks);
}
// 超时管理器
createTimeoutPromise(timeout: number, operationType: string): Promise<never> {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new WalletTimeoutError(
`Operation ${operationType} timed out after ${timeout}ms`,
operationType,
timeout
));
}, timeout);
});
}
// 重试策略判断
private shouldRetry(error: Error, attempt: number, config: RetryConfig): boolean {
if (attempt > config.maxRetries) {
return false;
}
// 不重试的错误类型
const nonRetryableErrors = [
'UserRejectedRequestError',
'UnauthorizedError',
'InvalidParameterError'
];
if (nonRetryableErrors.includes(error.constructor.name)) {
return false;
}
// 网络相关错误可以重试
const retryableErrors = [
'NetworkError',
'TimeoutError',
'ConnectionError',
'ServerError'
];
return retryableErrors.some(errorType =>
error.message.includes(errorType) ||
error.constructor.name.includes(errorType)
);
}
// 退避延迟计算
private calculateBackoffDelay(attempt: number, config: RetryConfig): number {
const baseDelay = config.baseDelay || 1000;
const maxDelay = config.maxDelay || 30000;
switch (config.strategy) {
case 'exponential':
return Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
case 'linear':
return Math.min(baseDelay * attempt, maxDelay);
case 'fixed':
return baseDelay;
default:
// 带抖动的指数退避
const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
const jitter = Math.random() * 0.1 * exponentialDelay;
return Math.min(exponentialDelay + jitter, maxDelay);
}
}
// Provider排序
private async getAvailableProviders(operationType: string): Promise<FallbackProvider[]> {
const allProviders = Array.from(this.fallbackProviders.values());
// 根据健康状态和性能排序
const providersWithScore = await Promise.all(
allProviders.map(async (provider) => {
const health = this.networkMonitor.getProviderHealth(provider.url);
const performance = this.networkMonitor.getProviderPerformance(provider.url, operationType);
let score = 0;
// 健康状态评分
if (health.status === 'healthy') score += 50;
if (health.responseTime && health.responseTime < 1000) score += 20;
// 性能评分
if (performance.successRate > 0.9) score += 20;
if (performance.avgResponseTime < 2000) score += 10;
return { provider, score };
})
);
// 按评分排序
providersWithScore.sort((a, b) => b.score - a.score);
return providersWithScore.map(item => item.provider);
}
// 延迟工具函数
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 错误类型判断
private isTimeoutError(error: Error): boolean {
return error instanceof WalletTimeoutError ||
error.name === 'TimeoutError' ||
error.message.includes('timeout') ||
error.message.includes('timed out');
}
// 生成操作ID
private generateOperationId(): string {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
// 占位实现
private async executeWithProvider<T>(operation: () => Promise<T>, provider: FallbackProvider, operationType: string, options: FallbackOptions): Promise<T> {
// 切换到指定Provider并执行操作
return operation();
}
private async pingProvider(provider: FallbackProvider): Promise<void> {
// 对Provider进行健康检查
}
private async attemptWalletReconnection(): Promise<void> {
// 尝试重新连接钱包
}
private async resumePendingOperations(): Promise<void> {
// 恢复待处理的操作
}
private pauseNetworkOperations(): void {
// 暂停所有网络操作
}
private showOfflineNotification(): void {
// 显示离线通知
}
private emitNetworkEvent(eventType: string): void {
// 触发网络事件
}
}
// 网络监控器
class NetworkMonitor {
private operationStats = new Map<string, OperationStats>();
private providerStats = new Map<string, ProviderStats>();
private networkHealth = new Map<string, ProviderHealth>();
recordSuccess(operationType: string, responseTime: number): void {
const stats = this.getOrCreateOperationStats(operationType);
stats.totalRequests++;
stats.successCount++;
stats.totalResponseTime += responseTime;
stats.lastSuccess = Date.now();
}
recordFailure(operationType: string, error: Error): void {
const stats = this.getOrCreateOperationStats(operationType);
stats.totalRequests++;
stats.failureCount++;
stats.lastFailure = Date.now();
stats.recentErrors.push({
error: error.message,
timestamp: Date.now()
});
// 保持错误历史在合理范围内
if (stats.recentErrors.length > 10) {
stats.recentErrors = stats.recentErrors.slice(-10);
}
}
recordProviderSuccess(providerUrl: string, operationType: string): void {
const stats = this.getOrCreateProviderStats(providerUrl);
const opStats = stats.operationStats.get(operationType) || { success: 0, failure: 0, totalTime: 0 };
opStats.success++;
stats.operationStats.set(operationType, opStats);
stats.lastSuccess = Date.now();
}
recordProviderFailure(providerUrl: string, operationType: string, error: Error): void {
const stats = this.getOrCreateProviderStats(providerUrl);
const opStats = stats.operationStats.get(operationType) || { success: 0, failure: 0, totalTime: 0 };
opStats.failure++;
stats.operationStats.set(operationType, opStats);
stats.lastFailure = Date.now();
}
recordProviderHealth(providerUrl: string, health: ProviderHealth): void {
this.networkHealth.set(providerUrl, health);
}
getProviderHealth(providerUrl: string): ProviderHealth {
return this.networkHealth.get(providerUrl) || {
status: 'unknown',
timestamp: 0
};
}
getProviderPerformance(providerUrl: string, operationType: string): ProviderPerformance {
const stats = this.providerStats.get(providerUrl);
if (!stats) {
return { successRate: 0, avgResponseTime: Infinity, totalRequests: 0 };
}
const opStats = stats.operationStats.get(operationType);
if (!opStats) {
return { successRate: 0, avgResponseTime: Infinity, totalRequests: 0 };
}
const totalRequests = opStats.success + opStats.failure;
const successRate = totalRequests > 0 ? opStats.success / totalRequests : 0;
const avgResponseTime = opStats.success > 0 ? opStats.totalTime / opStats.success : Infinity;
return { successRate, avgResponseTime, totalRequests };
}
private getOrCreateOperationStats(operationType: string): OperationStats {
let stats = this.operationStats.get(operationType);
if (!stats) {
stats = {
totalRequests: 0,
successCount: 0,
failureCount: 0,
totalResponseTime: 0,
recentErrors: [],
lastSuccess: 0,
lastFailure: 0
};
this.operationStats.set(operationType, stats);
}
return stats;
}
private getOrCreateProviderStats(providerUrl: string): ProviderStats {
let stats = this.providerStats.get(providerUrl);
if (!stats) {
stats = {
operationStats: new Map(),
lastSuccess: 0,
lastFailure: 0
};
this.providerStats.set(providerUrl, stats);
}
return stats;
}
}
// 超时管理器
class TimeoutManager {
private timeouts: TimeoutConfig;
constructor(timeouts: TimeoutConfig) {
this.timeouts = timeouts;
}
createTimeoutPromise(timeout: number, operationType: string): Promise<never> {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new WalletTimeoutError(
`Operation ${operationType} timed out after ${timeout}ms`,
operationType,
timeout
));
}, timeout);
});
}
getTimeoutForOperation(operationType: string): number {
const operationTimeouts: Record<string, number> = {
'wallet_connect': 30000,
'transaction_send': 120000,
'balance_query': 10000,
'token_approval': 60000,
'network_switch': 15000,
'sign_message': 60000
};
return operationTimeouts[operationType] || this.timeouts.default;
}
}
// 自定义错误类
class WalletTimeoutError extends Error {
constructor(message: string, public operationType: string, public timeout: number) {
super(message);
this.name = 'WalletTimeoutError';
}
}
class WalletRetryExhaustedError extends Error {
constructor(message: string, public lastError: Error, public attempts: number) {
super(message);
this.name = 'WalletRetryExhaustedError';
}
}
class WalletFallbackExhaustedError extends Error {
constructor(message: string, public lastError: Error, public providersCount: number) {
super(message);
this.name = 'WalletFallbackExhaustedError';
}
}
// 类型定义
interface NetworkConfig {
timeouts: TimeoutConfig;
retry: RetryConfig;
healthCheckInterval: number;
maxConcurrentRequests: number;
}
interface TimeoutConfig {
default: number;
connect: number;
transaction: number;
query: number;
}
interface RetryConfig {
maxRetries: number;
baseDelay: number;
maxDelay: number;
strategy: 'exponential' | 'linear' | 'fixed';
}
interface RequestOptions {
operationType: string;
timeout?: number;
retryConfig?: RetryConfig;
}
interface FallbackOptions {
maxProviders?: number;
requireHealthy?: boolean;
}
interface FallbackProvider {
url: string;
weight: number;
timeout: number;
}
interface OperationStats {
totalRequests: number;
successCount: number;
failureCount: number;
totalResponseTime: number;
recentErrors: Array<{ error: string; timestamp: number }>;
lastSuccess: number;
lastFailure: number;
}
interface ProviderStats {
operationStats: Map<string, { success: number; failure: number; totalTime: number }>;
lastSuccess: number;
lastFailure: number;
}
interface ProviderHealth {
status: 'healthy' | 'unhealthy' | 'unknown';
responseTime?: number;
error?: string;
timestamp: number;
}
interface ProviderPerformance {
successRate: number;
avgResponseTime: number;
totalRequests: number;
}
网络异常处理最佳实践:
What are social recovery wallets? How do they differ from traditional wallets?
What are social recovery wallets? How do they differ from traditional wallets?
考察点:新型钱包机制。
答案:
社交恢复钱包是一种基于智能合约的新型钱包机制,通过多个受信任联系人(监护人)的集体确认来实现账户恢复。它解决了传统钱包助记词丢失无法找回的痛点,在安全性和可用性之间提供了更好的平衡。
社交恢复钱包架构:
// 社交恢复钱包合约接口
interface SocialRecoveryWallet {
// 基础钱包功能
execute(to: string, value: bigint, data: string): Promise<string>;
executeBatch(transactions: Transaction[]): Promise<string>;
// 监护人管理
addGuardian(guardian: string): Promise<void>;
removeGuardian(guardian: string): Promise<void>;
getGuardians(): Promise<string[]>;
// 恢复流程
initiateRecovery(newOwner: string): Promise<void>;
supportRecovery(recoveryId: string): Promise<void>;
executeRecovery(recoveryId: string): Promise<void>;
cancelRecovery(recoveryId: string): Promise<void>;
// 配置管理
setRecoveryThreshold(threshold: number): Promise<void>;
setRecoveryPeriod(period: number): Promise<void>;
}
// 社交恢复钱包实现
class SocialRecoveryWalletImpl implements SocialRecoveryWallet {
private contract: Contract;
private provider: Provider;
private owner: string;
private guardians: Set<string> = new Set();
private recoveryThreshold: number = 3;
private recoveryPeriod: number = 7 * 24 * 60 * 60; // 7天
constructor(contractAddress: string, provider: Provider, owner: string) {
this.contract = new Contract(contractAddress, SOCIAL_RECOVERY_ABI, provider);
this.provider = provider;
this.owner = owner;
this.loadConfiguration();
}
// 执行交易
async execute(to: string, value: bigint, data: string): Promise<string> {
const tx = await this.contract.execute(to, value, data);
return tx.hash;
}
// 批量执行交易
async executeBatch(transactions: Transaction[]): Promise<string> {
const targets = transactions.map(tx => tx.to);
const values = transactions.map(tx => tx.value || 0n);
const calldatas = transactions.map(tx => tx.data || '0x');
const tx = await this.contract.executeBatch(targets, values, calldatas);
return tx.hash;
}
// 添加监护人
async addGuardian(guardian: string): Promise<void> {
if (this.guardians.has(guardian)) {
throw new Error('Guardian already exists');
}
if (!this.isValidAddress(guardian)) {
throw new Error('Invalid guardian address');
}
const tx = await this.contract.addGuardian(guardian);
await tx.wait();
this.guardians.add(guardian);
this.emitEvent('GuardianAdded', { guardian });
}
// 移除监护人
async removeGuardian(guardian: string): Promise<void> {
if (!this.guardians.has(guardian)) {
throw new Error('Guardian not found');
}
if (this.guardians.size <= this.recoveryThreshold) {
throw new Error('Cannot remove guardian: would fall below threshold');
}
const tx = await this.contract.removeGuardian(guardian);
await tx.wait();
this.guardians.delete(guardian);
this.emitEvent('GuardianRemoved', { guardian });
}
// 获取监护人列表
async getGuardians(): Promise<string[]> {
const guardians = await this.contract.getGuardians();
return guardians;
}
// 发起恢复流程
async initiateRecovery(newOwner: string): Promise<void> {
if (!this.isValidAddress(newOwner)) {
throw new Error('Invalid new owner address');
}
// 验证恢复请求的合法性
await this.validateRecoveryRequest(newOwner);
const tx = await this.contract.initiateRecovery(newOwner);
await tx.wait();
const recoveryId = await this.getRecoveryId(newOwner);
this.emitEvent('RecoveryInitiated', { newOwner, recoveryId });
}
// 支持恢复
async supportRecovery(recoveryId: string): Promise<void> {
const recovery = await this.getRecoveryDetails(recoveryId);
if (!recovery) {
throw new Error('Recovery not found');
}
if (recovery.executed) {
throw new Error('Recovery already executed');
}
const tx = await this.contract.supportRecovery(recoveryId);
await tx.wait();
this.emitEvent('RecoverySupported', { recoveryId });
}
// 执行恢复
async executeRecovery(recoveryId: string): Promise<void> {
const recovery = await this.getRecoveryDetails(recoveryId);
if (!recovery) {
throw new Error('Recovery not found');
}
// 检查是否满足执行条件
if (recovery.supportCount < this.recoveryThreshold) {
throw new Error('Insufficient guardian support');
}
if (Date.now() < recovery.executeAfter) {
throw new Error('Recovery period not yet elapsed');
}
const tx = await this.contract.executeRecovery(recoveryId);
await tx.wait();
this.owner = recovery.newOwner;
this.emitEvent('RecoveryExecuted', { recoveryId, newOwner: recovery.newOwner });
}
// 取消恢复
async cancelRecovery(recoveryId: string): Promise<void> {
const tx = await this.contract.cancelRecovery(recoveryId);
await tx.wait();
this.emitEvent('RecoveryCancelled', { recoveryId });
}
// 设置恢复阈值
async setRecoveryThreshold(threshold: number): Promise<void> {
if (threshold <= 0 || threshold > this.guardians.size) {
throw new Error('Invalid threshold');
}
const tx = await this.contract.setRecoveryThreshold(threshold);
await tx.wait();
this.recoveryThreshold = threshold;
this.emitEvent('ThresholdChanged', { threshold });
}
// 设置恢复期间
async setRecoveryPeriod(period: number): Promise<void> {
if (period < 24 * 60 * 60) { // 最少24小时
throw new Error('Recovery period too short');
}
const tx = await this.contract.setRecoveryPeriod(period);
await tx.wait();
this.recoveryPeriod = period;
this.emitEvent('PeriodChanged', { period });
}
// 监护人推荐系统
async recommendGuardians(userProfile: UserProfile): Promise<GuardianRecommendation[]> {
const recommendations: GuardianRecommendation[] = [];
// 基于社交网络的推荐
if (userProfile.socialAccounts) {
const socialContacts = await this.getSocialContacts(userProfile.socialAccounts);
for (const contact of socialContacts) {
if (await this.hasWallet(contact.address)) {
recommendations.push({
address: contact.address,
name: contact.name,
relationship: contact.relationship,
trustScore: contact.trustScore,
source: 'social'
});
}
}
}
// 基于交易历史的推荐
const frequentCounterparties = await this.getFrequentCounterparties(this.owner);
for (const counterparty of frequentCounterparties) {
recommendations.push({
address: counterparty.address,
relationship: 'frequent_contact',
trustScore: counterparty.interactionCount / 100,
source: 'onchain'
});
}
// 专业监护服务推荐
const professionalGuardians = await this.getProfessionalGuardians();
recommendations.push(...professionalGuardians);
// 按信任度排序
recommendations.sort((a, b) => b.trustScore - a.trustScore);
return recommendations.slice(0, 10); // 返回前10个推荐
}
// 恢复流程管理
async manageRecoveryProcess(): Promise<RecoveryManager> {
return new RecoveryManager(this);
}
// 私有方法
private async loadConfiguration(): Promise<void> {
const [guardians, threshold, period] = await Promise.all([
this.contract.getGuardians(),
this.contract.getRecoveryThreshold(),
this.contract.getRecoveryPeriod()
]);
this.guardians = new Set(guardians);
this.recoveryThreshold = threshold;
this.recoveryPeriod = period;
}
private async validateRecoveryRequest(newOwner: string): Promise<void> {
// 验证新所有者不是当前所有者
if (newOwner.toLowerCase() === this.owner.toLowerCase()) {
throw new Error('New owner cannot be the same as current owner');
}
// 验证新所有者不是监护人
if (this.guardians.has(newOwner.toLowerCase())) {
throw new Error('New owner cannot be a guardian');
}
}
private async getRecoveryId(newOwner: string): Promise<string> {
const filter = this.contract.filters.RecoveryInitiated(null, newOwner);
const events = await this.contract.queryFilter(filter, -100);
return events[events.length - 1].args.recoveryId;
}
private async getRecoveryDetails(recoveryId: string): Promise<RecoveryDetails | null> {
try {
const details = await this.contract.getRecovery(recoveryId);
return {
newOwner: details.newOwner,
supportCount: details.supportCount,
executeAfter: details.executeAfter * 1000,
executed: details.executed
};
} catch {
return null;
}
}
private isValidAddress(address: string): boolean {
return /^0x[a-fA-F0-9]{40}$/.test(address);
}
private emitEvent(eventName: string, data: any): void {
// 触发事件通知
console.log(`Event: ${eventName}`, data);
}
// 占位实现
private async getSocialContacts(accounts: SocialAccount[]): Promise<SocialContact[]> { return []; }
private async hasWallet(address: string): Promise<boolean> { return false; }
private async getFrequentCounterparties(owner: string): Promise<Counterparty[]> { return []; }
private async getProfessionalGuardians(): Promise<GuardianRecommendation[]> { return []; }
}
// 恢复流程管理器
class RecoveryManager {
private wallet: SocialRecoveryWalletImpl;
private activeRecoveries = new Map<string, RecoveryProcess>();
constructor(wallet: SocialRecoveryWalletImpl) {
this.wallet = wallet;
this.setupEventListeners();
}
// 启动恢复向导
async startRecoveryWizard(): Promise<RecoveryWizard> {
return new RecoveryWizard(this.wallet);
}
// 监控恢复进度
async trackRecoveryProgress(recoveryId: string): Promise<RecoveryStatus> {
const process = this.activeRecoveries.get(recoveryId);
if (!process) {
throw new Error('Recovery process not found');
}
const details = await this.wallet.getRecoveryDetails(recoveryId);
const guardians = await this.wallet.getGuardians();
const threshold = await this.wallet.recoveryThreshold;
return {
recoveryId,
progress: details.supportCount / threshold,
supportCount: details.supportCount,
requiredSupport: threshold,
timeRemaining: Math.max(0, details.executeAfter - Date.now()),
canExecute: details.supportCount >= threshold && Date.now() >= details.executeAfter,
supportingGuardians: await this.getSupportingGuardians(recoveryId)
};
}
// 通知监护人
async notifyGuardians(recoveryId: string, message: string): Promise<void> {
const guardians = await this.wallet.getGuardians();
const notifications = guardians.map(async (guardian) => {
return await this.sendRecoveryNotification(guardian, recoveryId, message);
});
await Promise.allSettled(notifications);
}
private setupEventListeners(): void {
// 监听恢复相关事件
}
private async getSupportingGuardians(recoveryId: string): Promise<string[]> {
// 获取已支持恢复的监护人列表
return [];
}
private async sendRecoveryNotification(guardian: string, recoveryId: string, message: string): Promise<void> {
// 发送恢复通知到监护人
}
}
// 恢复向导
class RecoveryWizard {
private wallet: SocialRecoveryWalletImpl;
private steps: RecoveryStep[];
private currentStep = 0;
constructor(wallet: SocialRecoveryWalletImpl) {
this.wallet = wallet;
this.initializeSteps();
}
async nextStep(): Promise<RecoveryStep | null> {
if (this.currentStep >= this.steps.length) {
return null;
}
const step = this.steps[this.currentStep];
this.currentStep++;
return step;
}
async executeStep(stepData: any): Promise<StepResult> {
const step = this.steps[this.currentStep - 1];
return await step.execute(stepData);
}
private initializeSteps(): void {
this.steps = [
{
id: 'verify_identity',
title: '验证身份',
description: '确认你是钱包的合法所有者',
execute: async (data) => this.verifyIdentity(data)
},
{
id: 'select_new_owner',
title: '设置新密钥',
description: '生成或导入新的钱包密钥',
execute: async (data) => this.setupNewOwner(data)
},
{
id: 'contact_guardians',
title: '联系监护人',
description: '通知监护人支持恢复请求',
execute: async (data) => this.contactGuardians(data)
},
{
id: 'wait_for_support',
title: '等待支持',
description: '等待足够多的监护人确认',
execute: async (data) => this.waitForSupport(data)
},
{
id: 'execute_recovery',
title: '执行恢复',
description: '完成钱包恢复流程',
execute: async (data) => this.executeRecovery(data)
}
];
}
private async verifyIdentity(data: any): Promise<StepResult> {
// 身份验证逻辑
return { success: true, data: {} };
}
private async setupNewOwner(data: any): Promise<StepResult> {
// 设置新所有者逻辑
return { success: true, data: {} };
}
private async contactGuardians(data: any): Promise<StepResult> {
// 联系监护人逻辑
return { success: true, data: {} };
}
private async waitForSupport(data: any): Promise<StepResult> {
// 等待支持逻辑
return { success: true, data: {} };
}
private async executeRecovery(data: any): Promise<StepResult> {
// 执行恢复逻辑
return { success: true, data: {} };
}
}
与传统钱包的核心区别:
How to implement security auditing for wallet connections? Detect malicious behavior.
How to implement security auditing for wallet connections? Detect malicious behavior.
考察点:安全监控机制。
答案:
钱包连接安全审计通过多层次监控体系实现恶意行为检测,包括实时行为分析、异常模式识别、威胁情报集成等。核心目标是在保护用户资产安全的同时,最小化对正常操作的影响。
安全审计架构设计:
// 钱包安全审计系统
class WalletSecurityAuditor {
private behaviorAnalyzer: BehaviorAnalyzer;
private threatDetector: ThreatDetector;
private riskAssessment: RiskAssessment;
private alertSystem: AlertSystem;
private auditLogger: AuditLogger;
constructor(config: SecurityConfig) {
this.behaviorAnalyzer = new BehaviorAnalyzer(config.behavior);
this.threatDetector = new ThreatDetector(config.threats);
this.riskAssessment = new RiskAssessment(config.risk);
this.alertSystem = new AlertSystem(config.alerts);
this.auditLogger = new AuditLogger(config.logging);
}
// 连接安全审计
async auditConnection(connectionAttempt: ConnectionAttempt): Promise<SecurityVerdict> {
const auditSession = this.auditLogger.startAuditSession('wallet_connection');
try {
// 1. 基础安全检查
const basicChecks = await this.performBasicSecurityChecks(connectionAttempt);
auditSession.log('basic_checks', basicChecks);
if (basicChecks.criticalIssues.length > 0) {
return this.createSecurityVerdict('BLOCKED', basicChecks.criticalIssues);
}
// 2. 行为分析
const behaviorAnalysis = await this.behaviorAnalyzer.analyze(connectionAttempt);
auditSession.log('behavior_analysis', behaviorAnalysis);
// 3. 威胁检测
const threatAnalysis = await this.threatDetector.detectThreats(connectionAttempt);
auditSession.log('threat_detection', threatAnalysis);
// 4. 风险评估
const riskScore = await this.riskAssessment.calculateRisk({
connection: connectionAttempt,
behavior: behaviorAnalysis,
threats: threatAnalysis
});
auditSession.log('risk_assessment', { score: riskScore });
// 5. 生成审计结果
const verdict = await this.generateVerdict(riskScore, threatAnalysis, behaviorAnalysis);
auditSession.log('verdict', verdict);
return verdict;
} catch (error) {
auditSession.logError('audit_error', error);
// 在错误情况下,采用保守策略
return this.createSecurityVerdict('BLOCKED', ['Audit system error']);
} finally {
auditSession.end();
}
}
// 实时交易监控
async monitorTransaction(transaction: TransactionAttempt): Promise<TransactionSecurityResult> {
const monitor = this.auditLogger.startTransactionMonitor(transaction.hash);
try {
// 交易内容分析
const contentAnalysis = await this.analyzeTransactionContent(transaction);
monitor.log('content_analysis', contentAnalysis);
// gas异常检测
const gasAnalysis = await this.analyzeGasPatterns(transaction);
monitor.log('gas_analysis', gasAnalysis);
// 合约安全检查
const contractSecurity = await this.checkContractSecurity(transaction.to);
monitor.log('contract_security', contractSecurity);
// MEV攻击检测
const mevAnalysis = await this.detectMEVAttacks(transaction);
monitor.log('mev_analysis', mevAnalysis);
// 综合风险评分
const riskScore = this.calculateTransactionRisk({
content: contentAnalysis,
gas: gasAnalysis,
contract: contractSecurity,
mev: mevAnalysis
});
const result: TransactionSecurityResult = {
allowed: riskScore < 0.7,
riskScore,
warnings: this.generateWarnings(contentAnalysis, gasAnalysis, contractSecurity),
recommendations: this.generateRecommendations(riskScore, mevAnalysis)
};
monitor.log('final_result', result);
return result;
} finally {
monitor.end();
}
}
// 基础安全检查
private async performBasicSecurityChecks(attempt: ConnectionAttempt): Promise<BasicSecurityChecks> {
const checks: BasicSecurityChecks = {
criticalIssues: [],
warnings: [],
info: []
};
// 检查DApp域名
const domainCheck = await this.checkDomainSecurity(attempt.origin);
if (domainCheck.isPhishing) {
checks.criticalIssues.push(`Phishing domain detected: ${attempt.origin}`);
}
if (domainCheck.isSuspicious) {
checks.warnings.push(`Suspicious domain: ${domainCheck.reason}`);
}
// 检查SSL证书
const sslCheck = await this.checkSSLSecurity(attempt.origin);
if (!sslCheck.valid) {
checks.criticalIssues.push('Invalid SSL certificate');
}
if (sslCheck.expiringSoon) {
checks.warnings.push('SSL certificate expiring soon');
}
// 检查请求频率
const rateCheck = this.checkRequestRate(attempt.origin, attempt.userAgent);
if (rateCheck.isAbusive) {
checks.criticalIssues.push('Abusive request rate detected');
}
// 检查用户代理
const uaCheck = this.checkUserAgent(attempt.userAgent);
if (uaCheck.isSuspicious) {
checks.warnings.push(`Suspicious user agent: ${uaCheck.reason}`);
}
return checks;
}
// 交易内容分析
private async analyzeTransactionContent(tx: TransactionAttempt): Promise<ContentAnalysis> {
const analysis: ContentAnalysis = {
riskLevel: 'low',
issues: [],
patterns: []
};
// 检查交易值异常
if (tx.value && this.isUnusualValue(tx.value)) {
analysis.issues.push({
type: 'unusual_value',
severity: 'medium',
description: `Unusual transaction value: ${tx.value}`
});
}
// 检查gas设置异常
if (this.isUnusualGasSettings(tx.gas, tx.gasPrice)) {
analysis.issues.push({
type: 'unusual_gas',
severity: 'low',
description: 'Unusual gas settings detected'
});
}
// 检查调用数据
if (tx.data && tx.data !== '0x') {
const dataAnalysis = await this.analyzeCallData(tx.data, tx.to);
if (dataAnalysis.hasRiskyPatterns) {
analysis.issues.push({
type: 'risky_calldata',
severity: 'high',
description: dataAnalysis.description
});
}
}
// 检查批量转账模式
const batchPattern = this.detectBatchTransferPattern(tx);
if (batchPattern.detected) {
analysis.patterns.push({
type: 'batch_transfer',
confidence: batchPattern.confidence,
description: 'Batch transfer pattern detected'
});
}
// 设置整体风险级别
const highSeverityIssues = analysis.issues.filter(issue => issue.severity === 'high');
if (highSeverityIssues.length > 0) {
analysis.riskLevel = 'high';
} else if (analysis.issues.length > 2) {
analysis.riskLevel = 'medium';
}
return analysis;
}
// 恶意行为检测
async detectMaliciousBehavior(sessionData: SessionData): Promise<MaliciousBehaviorReport> {
const detectors: MaliciousBehaviorDetector[] = [
new PhishingDetector(),
new DrainerDetector(),
new MEVBotDetector(),
new SandwichAttackDetector(),
new FlashLoanAttackDetector(),
new RugPullDetector()
];
const results = await Promise.all(
detectors.map(detector => detector.analyze(sessionData))
);
const report: MaliciousBehaviorReport = {
overallRisk: 0,
detectedAttacks: [],
recommendations: [],
immediateActions: []
};
// 聚合检测结果
for (const result of results) {
report.overallRisk = Math.max(report.overallRisk, result.riskScore);
if (result.detected) {
report.detectedAttacks.push({
type: result.attackType,
confidence: result.confidence,
description: result.description,
evidence: result.evidence
});
}
report.recommendations.push(...result.recommendations);
report.immediateActions.push(...result.immediateActions);
}
return report;
}
}
// 行为分析器
class BehaviorAnalyzer {
private userProfiles = new Map<string, UserBehaviorProfile>();
private sessionPatterns = new Map<string, SessionPattern>();
async analyze(connectionAttempt: ConnectionAttempt): Promise<BehaviorAnalysis> {
const userProfile = await this.getUserBehaviorProfile(connectionAttempt.userAddress);
const sessionPattern = this.analyzeSessionPattern(connectionAttempt);
return {
userRiskScore: this.calculateUserRiskScore(userProfile),
sessionRiskScore: this.calculateSessionRiskScore(sessionPattern),
anomalies: this.detectAnomalies(userProfile, sessionPattern),
recommendations: this.generateBehaviorRecommendations(userProfile, sessionPattern)
};
}
private async getUserBehaviorProfile(userAddress: string): Promise<UserBehaviorProfile> {
let profile = this.userProfiles.get(userAddress);
if (!profile) {
profile = await this.buildUserProfile(userAddress);
this.userProfiles.set(userAddress, profile);
}
return profile;
}
private async buildUserProfile(userAddress: string): Promise<UserBehaviorProfile> {
// 从区块链数据构建用户行为档案
const transactionHistory = await this.getTransactionHistory(userAddress);
const interactionPatterns = this.analyzeInteractionPatterns(transactionHistory);
return {
address: userAddress,
firstSeen: transactionHistory[0]?.timestamp || Date.now(),
totalTransactions: transactionHistory.length,
averageValue: this.calculateAverageValue(transactionHistory),
preferredDApps: this.extractPreferredDApps(transactionHistory),
riskIndicators: this.identifyRiskIndicators(transactionHistory),
interactionPatterns
};
}
private calculateUserRiskScore(profile: UserBehaviorProfile): number {
let risk = 0;
// 新用户风险
const accountAge = Date.now() - profile.firstSeen;
if (accountAge < 7 * 24 * 60 * 60 * 1000) { // 7天
risk += 0.2;
}
// 交易历史风险
if (profile.totalTransactions < 10) {
risk += 0.15;
}
// 风险指标检查
risk += profile.riskIndicators.length * 0.1;
// 异常模式检查
if (profile.interactionPatterns.hasAnomalous) {
risk += 0.3;
}
return Math.min(risk, 1.0);
}
// 占位实现
private analyzeSessionPattern(attempt: ConnectionAttempt): SessionPattern { return {} as SessionPattern; }
private calculateSessionRiskScore(pattern: SessionPattern): number { return 0; }
private detectAnomalies(profile: UserBehaviorProfile, pattern: SessionPattern): Anomaly[] { return []; }
private generateBehaviorRecommendations(profile: UserBehaviorProfile, pattern: SessionPattern): string[] { return []; }
private async getTransactionHistory(address: string): Promise<any[]> { return []; }
private analyzeInteractionPatterns(history: any[]): any { return {}; }
private calculateAverageValue(history: any[]): number { return 0; }
private extractPreferredDApps(history: any[]): string[] { return []; }
private identifyRiskIndicators(history: any[]): string[] { return []; }
}
// 威胁检测器
class ThreatDetector {
private threatIntelligence: ThreatIntelligence;
private maliciousContracts: Set<string>;
private phishingDomains: Set<string>;
constructor(config: ThreatConfig) {
this.threatIntelligence = new ThreatIntelligence(config.intelligence);
this.maliciousContracts = new Set(config.maliciousContracts);
this.phishingDomains = new Set(config.phishingDomains);
}
async detectThreats(attempt: ConnectionAttempt): Promise<ThreatAnalysis> {
const threats: DetectedThreat[] = [];
// 检查恶意域名
if (this.phishingDomains.has(attempt.origin)) {
threats.push({
type: 'phishing_domain',
severity: 'critical',
confidence: 0.95,
description: `Known phishing domain: ${attempt.origin}`
});
}
// 检查威胁情报
const intelThreats = await this.threatIntelligence.checkThreats(attempt);
threats.push(...intelThreats);
// 检查DApp信誉
const reputationCheck = await this.checkDAppReputation(attempt.origin);
if (reputationCheck.isMalicious) {
threats.push({
type: 'malicious_dapp',
severity: 'high',
confidence: reputationCheck.confidence,
description: `Malicious DApp detected: ${reputationCheck.reason}`
});
}
return {
threatCount: threats.length,
maxSeverity: this.getMaxSeverity(threats),
threats,
overallRisk: this.calculateThreatRisk(threats)
};
}
private getMaxSeverity(threats: DetectedThreat[]): 'low' | 'medium' | 'high' | 'critical' {
if (threats.some(t => t.severity === 'critical')) return 'critical';
if (threats.some(t => t.severity === 'high')) return 'high';
if (threats.some(t => t.severity === 'medium')) return 'medium';
return 'low';
}
private calculateThreatRisk(threats: DetectedThreat[]): number {
return threats.reduce((risk, threat) => {
const severityWeight = {
'low': 0.1,
'medium': 0.3,
'high': 0.7,
'critical': 1.0
};
return risk + severityWeight[threat.severity] * threat.confidence;
}, 0) / threats.length;
}
private async checkDAppReputation(origin: string): Promise<ReputationCheck> {
// 检查DApp信誉
return {
isMalicious: false,
confidence: 0,
reason: ''
};
}
}
// 专用恶意行为检测器
class PhishingDetector implements MaliciousBehaviorDetector {
async analyze(sessionData: SessionData): Promise<DetectionResult> {
const indicators = [];
// 检查域名相似性
if (this.checkDomainSimilarity(sessionData.origin)) {
indicators.push('domain_similarity');
}
// 检查SSL证书
if (await this.checkSuspiciousSSL(sessionData.origin)) {
indicators.push('suspicious_ssl');
}
// 检查页面内容
if (await this.checkPhishingContent(sessionData.pageContent)) {
indicators.push('phishing_content');
}
const riskScore = indicators.length / 3;
const detected = riskScore > 0.5;
return {
detected,
attackType: 'phishing',
riskScore,
confidence: detected ? Math.min(riskScore + 0.2, 1.0) : 0,
description: detected ? 'Potential phishing attack detected' : 'No phishing indicators found',
evidence: indicators,
recommendations: detected ? ['Verify website authenticity', 'Check URL carefully'] : [],
immediateActions: detected ? ['Block connection', 'Show phishing warning'] : []
};
}
private checkDomainSimilarity(origin: string): boolean {
// 检查域名是否与知名DApp相似
return false;
}
private async checkSuspiciousSSL(origin: string): Promise<boolean> {
// 检查SSL证书是否可疑
return false;
}
private async checkPhishingContent(content: string): Promise<boolean> {
// 检查页面内容是否包含钓鱼特征
return false;
}
}
// 更多占位实现和类型定义
class DrainerDetector implements MaliciousBehaviorDetector {
async analyze(sessionData: SessionData): Promise<DetectionResult> {
// Drainer检测逻辑
return {
detected: false,
attackType: 'drainer',
riskScore: 0,
confidence: 0,
description: '',
evidence: [],
recommendations: [],
immediateActions: []
};
}
}
// 类型定义
interface SecurityConfig {
behavior: BehaviorConfig;
threats: ThreatConfig;
risk: RiskConfig;
alerts: AlertConfig;
logging: LoggingConfig;
}
interface ConnectionAttempt {
origin: string;
userAddress: string;
userAgent: string;
timestamp: number;
requestedPermissions: string[];
}
interface SecurityVerdict {
decision: 'ALLOWED' | 'BLOCKED' | 'WARNING';
riskScore: number;
reasons: string[];
recommendations: string[];
}
interface MaliciousBehaviorDetector {
analyze(sessionData: SessionData): Promise<DetectionResult>;
}
interface DetectionResult {
detected: boolean;
attackType: string;
riskScore: number;
confidence: number;
description: string;
evidence: string[];
recommendations: string[];
immediateActions: string[];
}
// 更多类型定义省略...
interface BehaviorConfig {}
interface ThreatConfig { intelligence: any; maliciousContracts: string[]; phishingDomains: string[]; }
interface RiskConfig {}
interface AlertConfig {}
interface LoggingConfig {}
interface TransactionAttempt { hash: string; to: string; value?: bigint; data?: string; gas?: bigint; gasPrice?: bigint; }
interface TransactionSecurityResult { allowed: boolean; riskScore: number; warnings: string[]; recommendations: string[]; }
interface BasicSecurityChecks { criticalIssues: string[]; warnings: string[]; info: string[]; }
interface ContentAnalysis { riskLevel: string; issues: any[]; patterns: any[]; }
interface MaliciousBehaviorReport { overallRisk: number; detectedAttacks: any[]; recommendations: string[]; immediateActions: string[]; }
interface BehaviorAnalysis { userRiskScore: number; sessionRiskScore: number; anomalies: Anomaly[]; recommendations: string[]; }
interface UserBehaviorProfile { address: string; firstSeen: number; totalTransactions: number; averageValue: number; preferredDApps: string[]; riskIndicators: string[]; interactionPatterns: any; }
interface SessionPattern {}
interface Anomaly {}
interface ThreatAnalysis { threatCount: number; maxSeverity: string; threats: DetectedThreat[]; overallRisk: number; }
interface DetectedThreat { type: string; severity: string; confidence: number; description: string; }
interface ReputationCheck { isMalicious: boolean; confidence: number; reason: string; }
interface SessionData { origin: string; pageContent: string; }
interface ThreatIntelligence { checkThreats(attempt: ConnectionAttempt): Promise<DetectedThreat[]>; }
interface AuditLogger { startAuditSession(type: string): any; startTransactionMonitor(hash: string): any; }
interface AlertSystem {}
interface RiskAssessment { calculateRisk(data: any): Promise<number>; }
// 占位类实现
class MEVBotDetector implements MaliciousBehaviorDetector {
async analyze(sessionData: SessionData): Promise<DetectionResult> { return {} as DetectionResult; }
}
class SandwichAttackDetector implements MaliciousBehaviorDetector {
async analyze(sessionData: SessionData): Promise<DetectionResult> { return {} as DetectionResult; }
}
class FlashLoanAttackDetector implements MaliciousBehaviorDetector {
async analyze(sessionData: SessionData): Promise<DetectionResult> { return {} as DetectionResult; }
}
class RugPullDetector implements MaliciousBehaviorDetector {
async analyze(sessionData: SessionData): Promise<DetectionResult> { return {} as DetectionResult; }
}
安全监控最佳实践:
What challenges does cross-chain wallet integration face? How to design unified cross-chain interfaces?
What challenges does cross-chain wallet integration face? How to design unified cross-chain interfaces?
考察点:跨链技术挑战。
答案:
跨链钱包集成面临网络差异性、状态同步、安全性验证等多重挑战。统一跨链接口需要设计抽象层来屏蔽不同区块链的差异,提供一致的用户体验和开发接口。
跨链集成主要挑战:
统一跨链接口架构:
// 跨链钱包管理器
class CrossChainWalletManager {
private chainAdapters = new Map<string, ChainAdapter>();
private bridgeManager: CrossChainBridgeManager;
private stateManager: CrossChainStateManager;
private assetManager: CrossChainAssetManager;
private eventBus: CrossChainEventBus;
constructor(config: CrossChainConfig) {
this.bridgeManager = new CrossChainBridgeManager(config.bridges);
this.stateManager = new CrossChainStateManager(config.state);
this.assetManager = new CrossChainAssetManager(config.assets);
this.eventBus = new CrossChainEventBus();
this.initializeChainAdapters(config.chains);
}
// 注册链适配器
registerChain(chainId: string, adapter: ChainAdapter): void {
this.chainAdapters.set(chainId, adapter);
this.eventBus.emit('chainRegistered', { chainId, adapter });
}
// 统一连接接口
async connectWallet(chainId: string, options?: ConnectOptions): Promise<CrossChainWalletConnection> {
const adapter = this.getChainAdapter(chainId);
try {
const connection = await adapter.connect(options);
// 创建跨链连接包装器
const crossChainConnection = new CrossChainWalletConnection({
chainId,
adapter,
connection,
stateManager: this.stateManager,
assetManager: this.assetManager,
bridgeManager: this.bridgeManager
});
// 同步账户状态
await this.syncAccountState(crossChainConnection);
// 注册状态监听
this.registerConnectionEventListeners(crossChainConnection);
return crossChainConnection;
} catch (error) {
throw new CrossChainError(`Failed to connect to ${chainId}: ${error.message}`);
}
}
// 统一资产查询
async getAssetBalance(address: string, chainId?: string): Promise<CrossChainAssetBalance> {
if (chainId) {
return await this.getSingleChainBalance(address, chainId);
}
// 获取所有链上的资产
const balancePromises = Array.from(this.chainAdapters.keys()).map(async (id) => {
try {
const balance = await this.getSingleChainBalance(address, id);
return { chainId: id, balance };
} catch (error) {
console.warn(`Failed to get balance for ${id}:`, error);
return { chainId: id, balance: null, error: error.message };
}
});
const results = await Promise.all(balancePromises);
return this.aggregateAssetBalances(results);
}
// 跨链转账
async crossChainTransfer(params: CrossChainTransferParams): Promise<CrossChainTransaction> {
const { from, to, asset, amount, fromChain, toChain } = params;
// 验证跨链转账参数
await this.validateCrossChainTransfer(params);
// 选择最优跨链路径
const bridgeRoute = await this.bridgeManager.findOptimalRoute(fromChain, toChain, asset);
if (!bridgeRoute) {
throw new CrossChainError(`No bridge route found from ${fromChain} to ${toChain}`);
}
// 执行跨链转账
const transaction = await this.executeCrossChainTransfer(params, bridgeRoute);
// 启动状态监控
this.startTransactionMonitoring(transaction);
return transaction;
}
// 网络状态监控
async monitorNetworkStatus(): Promise<CrossChainNetworkStatus> {
const networkPromises = Array.from(this.chainAdapters.entries()).map(async ([chainId, adapter]) => {
try {
const status = await adapter.getNetworkStatus();
return { chainId, status, healthy: true };
} catch (error) {
return {
chainId,
status: null,
healthy: false,
error: error.message
};
}
});
const results = await Promise.all(networkPromises);
return {
timestamp: Date.now(),
networks: results,
overallHealth: results.every(r => r.healthy)
};
}
private getChainAdapter(chainId: string): ChainAdapter {
const adapter = this.chainAdapters.get(chainId);
if (!adapter) {
throw new CrossChainError(`Chain adapter not found for ${chainId}`);
}
return adapter;
}
private async syncAccountState(connection: CrossChainWalletConnection): Promise<void> {
const accountData = await connection.getAccountData();
await this.stateManager.updateAccountState(connection.chainId, accountData);
}
private registerConnectionEventListeners(connection: CrossChainWalletConnection): void {
connection.on('accountChanged', (newAccount) => {
this.eventBus.emit('accountChanged', {
chainId: connection.chainId,
newAccount
});
});
connection.on('chainChanged', (newChainId) => {
this.eventBus.emit('chainChanged', {
oldChainId: connection.chainId,
newChainId
});
});
connection.on('disconnect', () => {
this.eventBus.emit('disconnect', {
chainId: connection.chainId
});
});
}
}
// 链适配器接口
interface ChainAdapter {
readonly chainId: string;
readonly chainName: string;
readonly nativeCurrency: Currency;
connect(options?: ConnectOptions): Promise<WalletConnection>;
disconnect(): Promise<void>;
getNetworkStatus(): Promise<NetworkStatus>;
getBalance(address: string, tokenAddress?: string): Promise<AssetBalance>;
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
estimateGas(transaction: TransactionRequest): Promise<GasEstimate>;
getTransactionReceipt(txHash: string): Promise<TransactionReceipt>;
subscribeToBlocks(callback: (block: Block) => void): () => void;
subscribeToLogs(filter: LogFilter, callback: (log: Log) => void): () => void;
}
// 以太坊适配器实现
class EthereumChainAdapter implements ChainAdapter {
readonly chainId = '1';
readonly chainName = 'Ethereum Mainnet';
readonly nativeCurrency: Currency = {
name: 'Ethereum',
symbol: 'ETH',
decimals: 18
};
private provider: ethers.providers.Web3Provider | null = null;
private signer: ethers.Signer | null = null;
async connect(options?: ConnectOptions): Promise<WalletConnection> {
if (typeof window.ethereum === 'undefined') {
throw new Error('Ethereum provider not found');
}
try {
// 请求连接
await window.ethereum.request({ method: 'eth_requestAccounts' });
// 切换到正确网络
await this.switchToNetwork();
this.provider = new ethers.providers.Web3Provider(window.ethereum);
this.signer = this.provider.getSigner();
const address = await this.signer.getAddress();
return new EthereumWalletConnection({
address,
provider: this.provider,
signer: this.signer,
chainId: this.chainId
});
} catch (error) {
throw new Error(`Failed to connect to Ethereum: ${error.message}`);
}
}
async getBalance(address: string, tokenAddress?: string): Promise<AssetBalance> {
if (!this.provider) {
throw new Error('Provider not initialized');
}
if (!tokenAddress) {
// 获取ETH余额
const balance = await this.provider.getBalance(address);
return {
address,
symbol: 'ETH',
decimals: 18,
balance: balance.toString(),
formattedBalance: ethers.utils.formatEther(balance)
};
} else {
// 获取ERC-20代币余额
const contract = new ethers.Contract(
tokenAddress,
['function balanceOf(address) view returns (uint256)', 'function decimals() view returns (uint8)', 'function symbol() view returns (string)'],
this.provider
);
const [balance, decimals, symbol] = await Promise.all([
contract.balanceOf(address),
contract.decimals(),
contract.symbol()
]);
return {
address,
symbol,
decimals,
balance: balance.toString(),
formattedBalance: ethers.utils.formatUnits(balance, decimals),
tokenAddress
};
}
}
async sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
if (!this.signer) {
throw new Error('Signer not initialized');
}
try {
const tx = await this.signer.sendTransaction({
to: transaction.to,
value: transaction.value,
data: transaction.data,
gasLimit: transaction.gasLimit,
gasPrice: transaction.gasPrice
});
return {
hash: tx.hash,
from: tx.from,
to: tx.to,
value: tx.value?.toString(),
gasLimit: tx.gasLimit?.toString(),
gasPrice: tx.gasPrice?.toString(),
nonce: tx.nonce,
chainId: this.chainId,
wait: () => tx.wait()
};
} catch (error) {
throw new Error(`Transaction failed: ${error.message}`);
}
}
private async switchToNetwork(): Promise<void> {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x1' }] // Ethereum mainnet
});
} catch (error) {
// 如果网络不存在,尝试添加
if (error.code === 4902) {
await this.addNetwork();
} else {
throw error;
}
}
}
private async addNetwork(): Promise<void> {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: '0x1',
chainName: 'Ethereum Mainnet',
nativeCurrency: {
name: 'Ethereum',
symbol: 'ETH',
decimals: 18
},
rpcUrls: ['https://mainnet.infura.io/v3/YOUR_API_KEY'],
blockExplorerUrls: ['https://etherscan.io']
}]
});
}
// 其他方法的占位实现
async disconnect(): Promise<void> { /* 实现断开连接逻辑 */ }
async getNetworkStatus(): Promise<NetworkStatus> { return {} as NetworkStatus; }
async estimateGas(transaction: TransactionRequest): Promise<GasEstimate> { return {} as GasEstimate; }
async getTransactionReceipt(txHash: string): Promise<TransactionReceipt> { return {} as TransactionReceipt; }
subscribeToBlocks(callback: (block: Block) => void): () => void { return () => {}; }
subscribeToLogs(filter: LogFilter, callback: (log: Log) => void): () => void { return () => {}; }
}
// BSC适配器实现
class BSCChainAdapter implements ChainAdapter {
readonly chainId = '56';
readonly chainName = 'Binance Smart Chain';
readonly nativeCurrency: Currency = {
name: 'Binance Coin',
symbol: 'BNB',
decimals: 18
};
async connect(options?: ConnectOptions): Promise<WalletConnection> {
// BSC特定的连接逻辑
if (typeof window.ethereum === 'undefined') {
throw new Error('Ethereum provider not found');
}
try {
await window.ethereum.request({ method: 'eth_requestAccounts' });
await this.switchToBSC();
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const address = await signer.getAddress();
return new BSCWalletConnection({
address,
provider,
signer,
chainId: this.chainId
});
} catch (error) {
throw new Error(`Failed to connect to BSC: ${error.message}`);
}
}
private async switchToBSC(): Promise<void> {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x38' }] // BSC mainnet
});
} catch (error) {
if (error.code === 4902) {
await this.addBSCNetwork();
} else {
throw error;
}
}
}
private async addBSCNetwork(): Promise<void> {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: '0x38',
chainName: 'Binance Smart Chain Mainnet',
nativeCurrency: {
name: 'Binance Coin',
symbol: 'BNB',
decimals: 18
},
rpcUrls: ['https://bsc-dataseed1.binance.org/'],
blockExplorerUrls: ['https://bscscan.com']
}]
});
}
// 其他方法的占位实现
async disconnect(): Promise<void> {}
async getNetworkStatus(): Promise<NetworkStatus> { return {} as NetworkStatus; }
async getBalance(address: string, tokenAddress?: string): Promise<AssetBalance> { return {} as AssetBalance; }
async sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> { return {} as TransactionResponse; }
async estimateGas(transaction: TransactionRequest): Promise<GasEstimate> { return {} as GasEstimate; }
async getTransactionReceipt(txHash: string): Promise<TransactionReceipt> { return {} as TransactionReceipt; }
subscribeToBlocks(callback: (block: Block) => void): () => void { return () => {}; }
subscribeToLogs(filter: LogFilter, callback: (log: Log) => void): () => void { return () => {}; }
}
// 跨链状态管理器
class CrossChainStateManager {
private accountStates = new Map<string, Map<string, AccountState>>();
private transactionCache = new Map<string, CrossChainTransaction>();
private syncWorkers = new Map<string, StateSync>();
constructor(private config: StateConfig) {
this.initializeSyncWorkers();
}
async updateAccountState(chainId: string, accountData: AccountData): Promise<void> {
if (!this.accountStates.has(chainId)) {
this.accountStates.set(chainId, new Map());
}
const chainStates = this.accountStates.get(chainId)!;
chainStates.set(accountData.address, {
address: accountData.address,
balance: accountData.balance,
nonce: accountData.nonce,
lastUpdated: Date.now(),
chainId
});
// 触发状态同步事件
this.emit('stateUpdated', { chainId, address: accountData.address });
}
async getAccountState(chainId: string, address: string): Promise<AccountState | null> {
const chainStates = this.accountStates.get(chainId);
return chainStates?.get(address) || null;
}
async getAllAccountStates(address: string): Promise<Map<string, AccountState>> {
const result = new Map<string, AccountState>();
for (const [chainId, chainStates] of this.accountStates) {
const accountState = chainStates.get(address);
if (accountState) {
result.set(chainId, accountState);
}
}
return result;
}
private initializeSyncWorkers(): void {
for (const chainId of this.config.supportedChains) {
const syncWorker = new StateSync(chainId, this.config.sync);
this.syncWorkers.set(chainId, syncWorker);
syncWorker.on('stateChange', (change) => {
this.handleStateChange(chainId, change);
});
}
}
private handleStateChange(chainId: string, change: StateChange): void {
// 处理链状态变化
this.emit('chainStateChange', { chainId, change });
}
private emit(event: string, data: any): void {
// 事件发射实现
}
}
// 跨链资产管理器
class CrossChainAssetManager {
private assetRegistry = new Map<string, AssetInfo>();
private bridgeRegistry = new Map<string, BridgeInfo>();
constructor(private config: AssetConfig) {
this.loadAssetRegistry();
this.loadBridgeRegistry();
}
async getAssetInfo(chainId: string, tokenAddress: string): Promise<AssetInfo | null> {
const key = `${chainId}:${tokenAddress}`;
return this.assetRegistry.get(key) || null;
}
async aggregateAssetBalances(balances: ChainBalance[]): Promise<AggregatedBalance[]> {
const aggregated = new Map<string, AggregatedBalance>();
for (const chainBalance of balances) {
if (!chainBalance.balance) continue;
for (const asset of chainBalance.balance.assets) {
const assetId = this.getAssetId(asset);
if (!aggregated.has(assetId)) {
aggregated.set(assetId, {
assetId,
symbol: asset.symbol,
totalBalance: '0',
chainBalances: []
});
}
const agg = aggregated.get(assetId)!;
agg.chainBalances.push({
chainId: chainBalance.chainId,
balance: asset.balance,
formattedBalance: asset.formattedBalance
});
// 累加总余额(需要处理精度)
agg.totalBalance = this.addBalances(agg.totalBalance, asset.balance, asset.decimals);
}
}
return Array.from(aggregated.values());
}
private getAssetId(asset: AssetBalance): string {
// 生成资产唯一标识
if (asset.tokenAddress) {
return `token:${asset.symbol}`;
}
return `native:${asset.symbol}`;
}
private addBalances(balance1: string, balance2: string, decimals: number): string {
// 安全的大数加法实现
const bn1 = ethers.BigNumber.from(balance1 || '0');
const bn2 = ethers.BigNumber.from(balance2 || '0');
return bn1.add(bn2).toString();
}
private loadAssetRegistry(): void {
// 加载资产注册表
}
private loadBridgeRegistry(): void {
// 加载跨链桥注册表
}
}
// 使用示例
async function initializeCrossChainWallet() {
const config: CrossChainConfig = {
chains: {
ethereum: { rpc: 'https://mainnet.infura.io/v3/YOUR_API_KEY' },
bsc: { rpc: 'https://bsc-dataseed1.binance.org/' },
polygon: { rpc: 'https://polygon-rpc.com/' }
},
bridges: {
// 跨链桥配置
},
state: {
supportedChains: ['1', '56', '137'],
sync: { interval: 30000 }
},
assets: {
// 资产配置
}
};
const walletManager = new CrossChainWalletManager(config);
// 注册链适配器
walletManager.registerChain('1', new EthereumChainAdapter());
walletManager.registerChain('56', new BSCChainAdapter());
// 连接到多个链
const ethConnection = await walletManager.connectWallet('1');
const bscConnection = await walletManager.connectWallet('56');
// 获取跨链资产余额
const address = '0x742d35Cc6635C0532925a3b8D8fEAB4D7E7C3278';
const crossChainBalance = await walletManager.getAssetBalance(address);
console.log('Cross-chain balance:', crossChainBalance);
// 执行跨链转账
const transferResult = await walletManager.crossChainTransfer({
from: address,
to: '0xRecipientAddress',
asset: 'USDT',
amount: '100',
fromChain: '1',
toChain: '56'
});
console.log('Cross-chain transfer:', transferResult);
}
// 类型定义
interface CrossChainConfig {
chains: Record<string, ChainConfig>;
bridges: Record<string, BridgeConfig>;
state: StateConfig;
assets: AssetConfig;
}
interface ChainConfig {
rpc: string;
}
interface StateConfig {
supportedChains: string[];
sync: { interval: number };
}
interface AssetConfig {}
interface BridgeConfig {}
interface ConnectOptions {}
interface Currency { name: string; symbol: string; decimals: number; }
interface WalletConnection {}
interface NetworkStatus {}
interface AssetBalance {
address: string;
symbol: string;
decimals: number;
balance: string;
formattedBalance: string;
tokenAddress?: string;
}
interface TransactionRequest {
to?: string;
value?: any;
data?: string;
gasLimit?: any;
gasPrice?: any;
}
interface TransactionResponse {
hash: string;
from?: string;
to?: string;
value?: string;
gasLimit?: string;
gasPrice?: string;
nonce?: number;
chainId: string;
wait: () => Promise<any>;
}
interface GasEstimate {}
interface TransactionReceipt {}
interface Block {}
interface LogFilter {}
interface Log {}
interface CrossChainWalletConnection {}
interface CrossChainAssetBalance {}
interface CrossChainTransferParams {
from: string;
to: string;
asset: string;
amount: string;
fromChain: string;
toChain: string;
}
interface CrossChainTransaction {}
interface CrossChainNetworkStatus {
timestamp: number;
networks: any[];
overallHealth: boolean;
}
interface CrossChainError extends Error {}
interface AccountData { address: string; balance: any; nonce: number; }
interface AccountState {
address: string;
balance: any;
nonce: number;
lastUpdated: number;
chainId: string;
}
interface StateChange {}
interface AssetInfo {}
interface BridgeInfo {}
interface ChainBalance { chainId: string; balance?: { assets: AssetBalance[] }; }
interface AggregatedBalance {
assetId: string;
symbol: string;
totalBalance: string;
chainBalances: { chainId: string; balance: string; formattedBalance: string; }[];
}
// 占位类实现
class CrossChainBridgeManager {
constructor(config: any) {}
async findOptimalRoute(fromChain: string, toChain: string, asset: string): Promise<any> { return null; }
}
class CrossChainEventBus {
emit(event: string, data: any): void {}
}
class EthereumWalletConnection {
constructor(config: any) {}
}
class BSCWalletConnection {
constructor(config: any) {}
}
class StateSync {
constructor(chainId: string, config: any) {}
on(event: string, handler: (data: any) => void): void {}
}
跨链集成关键技术:
最佳实践建议: