Xinmin's Blog
AdminRSS
全部AIDeFiSolidity以太坊公链实战区块链行业观察
Admin
RSS

【以太坊】EIP-7702:让你的钱包秒变智能合约

以太坊·2025年3月21日·1 次阅读·约 23 分钟
© 2026·

关注我,带你转型Web3开发,分享我从2018年入行区块链以来的开发经验(DeFi/GameFi/NFT/DAO/OTC/钱包/Trading Bot/MEME Launch)

说在前面

嘿,你有没有想过,自己的普通钱包有一天能像智能合约那样“聪明”起来?我最近花了两天时间研究了一下EIP-7702的实现原理,从钱包怎么发交易到EVM怎么执行,总算搞明白了。写下来分享给你,希望你也能跟我一样,每天进步一点点!

EIP-7702是以太坊账户抽象领域的一个新玩法,由V神(Vitalik Buterin)提出,已经被敲定放进以太坊的下一次大升级——Pectra里(2025年3月5日,测试网络Sepolia已经升级)。

简单来说,它能让你的普通钱包(也就是EOA,外部拥有账户)在发一笔交易时,临时“变身”成智能合约,干点复杂的事儿,用完就变回去。这听起来是不是挺酷?不仅用起来方便,还能给以太坊生态带来更多可能性。

EIP-7702是啥东东?

在EIP-7702之前,以太坊的账户分两种:一种是你用私钥控制的EOA,比如你常用的钱包地址,只能转转账、调调合约;另一种是合约账户,部署了代码,能自己跑逻辑。EOA虽然简单,但功能有限,想干点高级的事儿就得靠合约账户。而EIP-7702来了之后,你的EOA也能临时“升级”,比如:

  • 批量操作:一次交易搞定好几件事。 比如你在DeFi里想用代币A换代币B,通常得先“批准”(approve),再“交换”(swap),两步走又费Gas又麻烦。现在用EIP-7702,一步到位,省心省钱。
  • 临时授权:让别人用你的账户,又不用给私钥。 比如你想让朋友拿你的NFT玩一天游戏,EIP-7702能给个临时权限,过期就失效,安全得很。
  • Gas赞助:没ETH也能玩。 假如你是新手,没ETH付Gas,DApp可以用EIP-7702帮你垫付,上手门槛一下子就低了。
  • 找回钱包:私钥丢了也不慌。 你可以提前设几个信任好友,私钥丢了他们能帮你把资产转到新地址,社交恢复就是这么方便。

这些功能是不是听着就很实用?接下来我带你看看EIP-7702是怎么实现的,别担心,我会尽量讲得简单点。

它是怎么工作的?

大致流程:从你发交易到执行完

EIP-7702执行流程.png

EIP-7702的核心就是让你的EOA通过一笔特殊交易临时跑智能合约代码。整个过程是这样的:

  1. 交易创建:你在钱包里弄一笔EIP-7702交易,类型是0x4(别急,后面会细说);
  2. 签名验证:用你的私钥签名,钱包把交易数据打包好;
  3. 广播交易:钱包把交易发到以太坊网络上;
  4. EVM处理:
    • EVM(以太坊虚拟机)收到后,检查签名,确保是你发的
    • 从交易的data字段里拿出要执行的合约代码
    • 通过交易中的authorization字段,EVM让你的EOA在执行时临时委托(类似delegatecall)到指定的合约代码,跑完逻辑后恢复原状
    • 跑一遍代码里的逻辑,然后恢复EOA原状
  5. Gas费用处理:Gas可以你自己付,也可以通过合约逻辑找个‘赞助商’帮忙垫付。
  6. 清理和结果返回:交易跑完,EVM把临时加的代码清掉,你的EOA变回普通账户,然后告诉你结果——成功还是失败,外加日志。

关键就在这个“临时”上,你的钱包在这笔交易里能跑代码,用完就恢复原样,既灵活又不失安全性。

新交易类型:0x4

EIP-7702利用EIP-2718的交易类型框架,引入了新的0x4交易类型,geth等客户端也支持了它。

你可能知道,以太坊之前有传统交易0x00,还有EIP-1559的动态手续费交易0x02,现在0x4是EIP-7702的专属标识。在代码里大概长这样:

const (
    LegacyTxType     = 0x00  // 老式交易
    AccessListTxType = 0x01  // 访问列表交易
    DynamicFeeTxType = 0x02  // 动态手续费交易
    BlobTxType       = 0x03  // Blob交易
    SetCodeTxType    = 0x04  // EIP-7702的新玩法
)

你的钱包得支持造这种0x4交易,EVM看到这个类型就知道该怎么处理了。

钱包怎么造这笔交易

假设你有个需求:从Alice发1 ETH给Bob,转100个MMT代币给Bob,再送一个NFT(MyDog,ID为1)给Frank。

简单来说,钱包把你的三件事打包成一段代码,塞进0x4交易发出去。

先看个简单例子:

// 你想干的三件事
const calls = [
  { to: 'Bob地址', value: '1 ETH', data: '转账' },           // 转1 ETH
  { to: 'MMT代币地址', value: 0, data: '转100 MMT给Bob' },   // 转代币
  { to: 'NFT地址', value: 0, data: '送NFT给Frank' }          // 转NFT
]

// 钱包把这些打包成一笔0x4交易
const tx = {
  type: '0x4',
  to: '你的地址',
  data: '上面那堆事的代码',
  authorization: '你的签名'
}

再具体一些是这样的:

// 导入必要的库
import { createWalletClient, http, parseEther, encodeFunctionData } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { eip7702Actions } from 'viem/experimental'
import { mainnet } from 'viem/chains'

// 创建账户和客户端
const aliceAccount = privateKeyToAccount('0xAlicePrivateKey')
const walletClient = createWalletClient({
  account: aliceAccount,
  chain: mainnet,
  transport: http()
}).extend(eip7702Actions())

// 批量调用合约地址(假设的)
const BATCH_CALL_ADDRESS = '0x1234567890123456789012345678901234567890'

// 目标地址和合约地址
const BOB_ADDRESS = '0xBobAddress'
const FRANK_ADDRESS = '0xFrankAddress'
const MMT_TOKEN_ADDRESS = '0xMMTTokenAddress'
const MYDOG_NFT_ADDRESS = '0xMyDogNFTAddress'

// 1. 创建授权对象
const batchCallAuthorization = await walletClient.signAuthorization({
  contractAddress: BATCH_CALL_ADDRESS
})

// 2. 构建批量调用数据
// ETH转账
const ethTransferCall = {
  to: BOB_ADDRESS,
  value: parseEther('1'),  // 1 ETH
  data: '0x'  // 普通转账不用额外数据
}

// 代币转账
const tokenTransferCall = {
  to: MMT_TOKEN_ADDRESS,
  value: 0n,  // 不发ETH
  data: encodeFunctionData({
    abi: [{ name: 'transfer', type: 'function', inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }], outputs: [{ type: 'bool' }] }],
    functionName: 'transfer',
    args: [BOB_ADDRESS, parseEther('100')]  // 转100个MMT
  })
}

// NFT转移
const nftTransferCall = {
  to: MYDOG_NFT_ADDRESS,
  value: 0n,
  data: encodeFunctionData({
    abi: [{ name: 'transferFrom', type: 'function', inputs: [{ name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'tokenId', type: 'uint256' }], outputs: [] }],
    functionName: 'transferFrom',
    args: [aliceAccount.address, FRANK_ADDRESS, 1n]  // 转ID=1的NFT
  })
}

// 3. 打包批量调用
const batchCallData = encodeFunctionData({
  abi: [{ name: 'execute', type: 'function', inputs: [{ name: 'calls', type: 'tuple[]', components: [{ name: 'to', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'data', type: 'bytes' }] }], outputs: [] }],
  functionName: 'execute',
  args: [[ethTransferCall, tokenTransferCall, nftTransferCall]]
})

// 4. 发送EIP-7702交易
try {
  const txHash = await walletClient.sendTransaction({
    authorizationList: [batchCallAuthorization],
    batch: [{
      to: aliceAccount.address,  // 指向自己
      data: batchCallData,       // 批量调用数据
      value: parseEther('1')     // 包含1 ETH用于转账
    }]
  })
  console.log('交易已发送,哈希:', txHash)
} catch (error) {
  console.error('交易失败:', error)
}

这段代码干了啥?

  1. 用Alice的账户创建了一个批量调用,包含三步操作。
  2. 签了个授权给批量调用合约。
  3. 把所有操作打包成一笔EIP-7702交易发出去。

交易发到链上后,EVM会让Alice的EOA临时变成合约账户,跑这段批量调用的代码,执行完再变回来。

完整代码可以去我的仓库瞅瞅: https://github.com/vsiryxm/eip-7702-Study/。

EVM怎么处理

交易发到EVM这边时,会经历几个步骤,我们结合Alice的例子仔细聊聊。

1. 验证交易签名

EVM先检查签名,确保这交易真是Alice发的。交易结构大概是这样的:

struct EIP7702Transaction {
    uint8 txType;     // 固定为0x4
    address to;       // EOA地址
    uint256 gas;      // Gas上限
    uint256 gasPrice; // Gas价格
    uint256 value;    // 转账金额
    bytes data;       // 合约代码
    uint256 nonce;    // 交易序号
    bytes signature;  // 签名
}

EVM会拿Alice的签名和公钥比对,确认无误才继续。不然直接拒了,安全性第一。

2. 加载委托合约代码

签名对了,EVM从data字段里掏出要跑的代码。在Alice的例子中,这就是批量调用合约的代码,里面写了发ETH、转代币、送NFT的逻辑。这段代码是个字节码,待会儿会被临时关联到Alice的账户上。

3. 将合约代码关联到EOA

EVM根据交易中的authorization字段(有委托合约地址),临时将指定的委托合约代码与Alice的EOA关联,让她的账户在执行时能跑这段代码,账户地址不变。

4. 执行临时合约逻辑

接下来,EVM开始跑代码,Alice的批量操作依次执行:

  • 发1 ETH给Bob:

    (bool success,) = BOB_ADDRESS.call{value: 1 ether}("");
    require(success, "ETH transfer failed");
    
    
  • 转100个MMT代币给Bob:

    IERC20(MMT_TOKEN_ADDRESS).transfer(BOB_ADDRESS, 100 * 10**18);
    
    
  • 送NFT给Frank:

    IERC721(MYDOG_NFT_ADDRESS).transferFrom(msg.sender, FRANK_ADDRESS, 1);
    
    

以上是用Solidity写的示例逻辑,实际执行时会编译成字节码。

这三步是一个整体,要么全成功,要么全失败,原子性保证了操作的可靠性。

5. 清理临时合约并恢复状态

跑完后,EVM把Alice账户里的临时代码清掉,她又变回普通EOA。状态变化(ETH、代币、NFT的转移)会保留,但账户本身恢复原样。Alice这边,交易结束,Bob拿到了ETH和代币,Frank拿到了NFT,皆大欢喜。

总结

EIP-7702是个挺酷的提案,它让EOA和合约账户的界限变模糊了,普通用户也能轻松玩转智能合约的功能。但我个人感觉也带来一个麻烦,后端监听合约事件变得复杂了。

通过新的0x4交易类型,EOA可以临时跑代码,支持批量交易、授权、Gas赞助、账户恢复等玩法。钱包负责构建交易,EVM负责执行和清理,整个流程既强大又灵活。

对于开发者来说,EIP-7702提供了一种新的账户抽象方式,能设计出更灵活、更用户友好的DApp。 随着Pectra升级上线,我觉得以后会有更多好玩的DApp冒出来。

你觉得怎么样?欢迎在评论区分享你的看法或疑问,也可以加我微信聊聊!

附EVM处理源代码

1. 验证交易签名 在 github.com/ethereum/go-ethereum/core/types/transaction.go 文件中,相关的代码包括:

  • func sanityCheckSignature(v *big.Int, r *big.Int, s *big.Int, maybeProtected bool) error:这个函数用于检查签名的有效性,确保 v, r, s 值是合法的。
  • func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error):这个方法返回一个带有给定签名的新交易。

2. 加载委托代码 在 github.com/ethereum/go-ethereum/core/types/transaction.go 文件中,加载委托代码的相关代码包括:

  • func (tx *Transaction) Data() []byte:这个方法返回交易的输入数据。
  • func (tx *Transaction) DecodeRLP(s *rlp.Stream) error 和 func (tx *Transaction) UnmarshalBinary(b []byte) error:这些方法用于解码交易数据,包括 EIP-2718 类型的交易数据。

3. 创建临时合约账户 在 github.com/ethereum/go-ethereum/core/types/transaction.go 文件中,创建临时合约账户的相关代码包括:

  • func (tx *Transaction) Type() uint8 和 func (tx *Transaction) decodeTyped(b []byte) (TxData, error):这些方法用于识别和解码交易类型,其中 SetCodeTxType 对应 0x4 类型交易。
  • func (tx *Transaction) setDecoded(inner TxData, size uint64):这个方法在解码后设置交易的内部数据。

4. 执行临时合约逻辑 a. 在 github.com/ethereum/go-ethereum/core/vm/eips.go 文件中,有关执行临时合约逻辑的代码包括:

  • func enable7702(jt *JumpTable):这个函数应用 EIP-7702 的更改,支持委托设计器。
  • 相关的操作码定义和执行函数,如 opDelegateCall、opCallCode 等。

b. 在 github.com/ethereum/go-ethereum/core/vm/operations_acl.go 文件中,有关执行临时合约逻辑的代码包括:

  • func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc:这个函数创建了用于 EIP-7702 的动态 gas 计算函数。
  • 具体的 gas 计算和访问列表检查函数,如 gasDelegateCallEIP7702、gasCallEIP7702 等。

5. 清理临时合约并恢复状态 a. 在 github.com/ethereum/go-ethereum/core/types/transaction.go 文件中,清理临时合约并恢复状态的相关代码包括:

  • func (tx *Transaction) WithoutBlobTxSidecar() *Transaction 和 func (tx *Transaction) WithBlobTxSidecar(sideCar *BlobTxSidecar) *Transaction:这些方法用于处理 blob 交易的 sidecar 数据,以确保交易状态的正确性。
  • func (tx *Transaction) SetCodeAuthorizations() []SetCodeAuthorization 和 func (tx *Transaction) SetCodeAuthorities() []common.Address:这些方法返回和处理 0x4 类型交易的授权数据。

b. 在 github.com/ethereum/go-ethereum/core/blockchain_test.go 文件中,有关清理临时合约并恢复状态的测试代码包括:

  • func TestEIP7702(t *testing.T):这个测试函数部署并调用了两个委托设计,并验证其执行的正确性。

sunny_yang_green.png

我的产品体系: 《Web3开发转型陪跑计划》 《数字货币投资工具使用教学》 《AI编程教学》 本人技术栈: Solidity/Golang/Rust/NodeJS/Python/AI编程 项目合作,欢迎你来链接我! 都看到这里了,不关注一下嘛👇