# EIP 7702

By [zoie](https://paragraph.com/@zoie) · 2025-06-09

---

> EIP-7702 是一项以太坊改进提案，旨在通过引入一种新的交易类型，使外部拥有账户（EOA）能够临时像智能合约账户一样运作。

> 这项提案的目的是简化账户抽象，允许普通的 Web3 钱包能够实现智能钱包的某些功能。

### 账户模型

evm账户模型大概如下

    address => {
       Nonce    uint64
       Balance  *uint256.Int
       Code []byte
    }
    

对于eoa账户，code里无代码，不具备合约功能

使用7702交易，eoa授权合约后, eoa账户下也会有code。跟eoa地址交互，是跟他的合约交互。

比如授权后，他的合约里没提供fallback方法，就无法收bnb

比如授权合约是一个工具类转出token，那其实每个人都可以调用他的方法转出token

### 具体实现

#### 7702交易

授权合约/取消授权合约，都需要通过7702的交易类型；

7702交易类型，为新增的一个交易类型。

    // SetCodeTx implements the EIP-7702 transaction type which temporarily installs
    // the code at the signer's address.
    type SetCodeTx struct {
       ChainID    *uint256.Int
       Nonce      uint64
       GasTipCap  *uint256.Int // a.k.a. maxPriorityFeePerGas
       GasFeeCap  *uint256.Int // a.k.a. maxFeePerGas
       Gas        uint64
       To         common.Address
       Value      *uint256.Int
       Data       []byte
       AccessList AccessList
       AuthList   []SetCodeAuthorization
    
       // Signature values
       V *uint256.Int
       R *uint256.Int
       S *uint256.Int
    }
    
    // SetCodeAuthorization is an authorization from an account to deploy code at its address.
    type SetCodeAuthorization struct {
       ChainID uint256.Int    `json:"chainId" gencodec:"required"`
       Address common.Address `json:"address" gencodec:"required"`
       Nonce   uint64         `json:"nonce" gencodec:"required"`
       V       uint8          `json:"yParity" gencodec:"required"`
       R       uint256.Int    `json:"r" gencodec:"required"`
       S       uint256.Int    `json:"s" gencodec:"required"`
    }
    

用户授权，需要对合约地址和用户nonce， chainid进行签名即可。

### EVM

在执行交易前，先查看SetCodeAuthorizations, 设置code到用户地址下，再进行执行交易；

啊这里要注意点，要是applyAuthorization有error，这里是忽略error的

    if contractCreation {
       ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value)
    } else {
       // Increment the nonce for the next transaction.
       st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)
    
       // Apply EIP-7702 authorizations.
       if msg.SetCodeAuthorizations != nil {
          for _, auth := range msg.SetCodeAuthorizations {
             // Note errors are ignored, we simply skip invalid authorizations here.
             st.applyAuthorization(&auth)
          }
       }
    
       // Perform convenience warming of sender's delegation target. Although the
       // sender is already warmed in Prepare(..), it's possible a delegation to
       // the account was deployed during this transaction. To handle correctly,
       // simply wait until the final state of delegations is determined before
       // performing the resolution and warming.
       if addr, ok := types.ParseDelegation(st.state.GetCode(*msg.To)); ok {
          st.state.AddAddressToAccessList(addr)
       }
    
       // Execute the transaction's call.
       ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
    }
    

查询某账户是否是有授权

    //eth_getCode
    
    返回值如果是 以 0xef010051510973ba7c1cc5a5e48e180c68b2ea4b9ec7df 0xef0100开头，那就是授权合约
    合约地址就把前缀去掉就行
    

要取消，就签一笔address = common.Address{}的授权就行

### 回到交易

如果是自己签授权/取消授权，因为evm 里msg.from nonce先生效，所以auth里的nonce记得要➕1

最后放一个例子

    func TestCccD(t *testing.T) {
       ctx := context.Background()
       client, _ := ethclient.DialContext(ctx, "https://data-seed-prebsc-1-s1.bnbchain.org:8545")
       feeKey, _ := crypto.HexToECDSA("")
       feeAddress := crypto.PubkeyToAddress(feeKey.PublicKey)
       authKey, _ := crypto.HexToECDSA("")
       authAddress := crypto.PubkeyToAddress(authKey.PublicKey)
    
       authNonce, err := client.NonceAt(context.Background(), authAddress, nil)
       noErr(err)
       feeNonce, err := client.NonceAt(context.Background(), feeAddress, nil)
       noErr(err)
    
       fmt.Println("fee", feeAddress.String(), feeNonce)
       fmt.Println("auth", authAddress.String(), authNonce)
    
       chainId := uint256.NewInt(97)
       authList, err := types.SignSetCode(authKey, types.SetCodeAuthorization{
          ChainID: *chainId,
          //Address: common.HexToAddress("0x51510973ba7c1cc5a5e48e180c68b2ea4b9ec7df"),
          Address: common.Address{},
          Nonce:   authNonce,
       })
       //fmt.Println(authList, err)
    
       //authList.Address = common.HexToAddress("0x51510973ba8c1cc5a5e48e180c68b2ea4b9ec7df")
       //authList.Nonce = 123
    
       data, err := hex.DecodeString("79f2447e000000000000000000000000ec5dcb5dbf4b114c9d0f65bccab49ec54f6a0867000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000eeae6c472be3ced42094b0f5a4f49380d0cfbccc000000000000000000000000eeae6c472be3ced42094b0f5a4f49380d0cfbccc0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000012c")
       noErr(err)
    
       tx := &types.SetCodeTx{
          ChainID:    chainId,
          Nonce:      feeNonce,
          GasTipCap:  uint256.NewInt(1e9),
          GasFeeCap:  uint256.NewInt(1e9),
          Gas:        1000000,
          To:         authAddress,
          Value:      nil,
          Data:       data,
          AccessList: nil,
          AuthList:   []types.SetCodeAuthorization{authList},
       }
       //fmt.Println(authList)
       //
       signedTx, err := types.SignTx(types.NewTx(tx), types.NewPragueSigner(big.NewInt(97)), feeKey)
       noErr(err)
       fmt.Println(signedTx.Type())
    
       //tx := &types.LegacyTx{
       // Nonce:    feeNonce,
       // GasPrice: big.NewInt(1e9),
       // Gas:      100000,
       // To:       &authAddress,
       // Value:    nil,
       // Data:     data,
       //}
       //signedTx, err := types.SignTx(types.NewTx(tx), types.NewEIP155Signer(big.NewInt(97)), feeKey)
       //noErr(err)
       fmt.Println(signedTx.Type())
       err = client.SendTransaction(ctx, signedTx)
       noErr(err)
    
       fmt.Println(signedTx.Hash())
    }

---

*Originally published on [zoie](https://paragraph.com/@zoie/eip-7702)*
