如何判断以太坊地址类型?

devtools/2025/1/24 4:04:49/

如何判断以太坊地址类型?

一、账户类型解释

2.1 以太坊外部账户(Externally Owned Account,EOA)

外部账户(EOA)是由私钥控制的账户,在以太坊网络中用来发送交易和执行其他操作。EOA 不是智能合约,它没有合约代码,只能用于发送交易(例如转账以太币)。其主要特点是:

  • 没有合约代码:外部账户没有部署智能合约的代码。
  • 拥有私钥:外部账户由用户的私钥控制,只有拥有该私钥的人才能签署交易。
  • 交易类型:EOA 发起的交易仅限于转账以太币或调用合约。

外部账户的特征:

  • 账户的地址通常是 0x 开头的 40 个十六进制字符。
  • 它不会有与之相关联的合约代码。

2.2 以太坊合约地址(Contract Account)

合约地址是由智能合约创建的账户,它由合约的代码控制。智能合约是部署在以太坊网络上的可编程代码,可以接受交易、存储数据、执行逻辑等。合约地址的主要特点是:

  • 拥有合约代码:合约地址包含了可执行的合约代码,它允许外部地址调用合约的函数。
  • 由合约代码控制:合约地址不能直接由私钥控制,它由智能合约的代码决定。
  • 交易类型:合约地址通过调用合约函数来执行操作,而不仅仅是进行转账。智能合约可以接收来自外部账户的交易并做出响应。

合约地址的特征:

  • 也以 0x 开头,并且长度为 40 个十六进制字符。
  • 它会与特定的智能合约代码相关联。

关于以太坊账户的详细解读,可以查看:以太坊账户详解

二、地址判断

2.1 判断方法

  1. 检查地址的代码是否存在

在以太坊中,合约地址是有部署在区块链上的智能合约代码的,而普通地址(外部账户)没有代码。

步骤:

  • 使用 eth_getCode RPC 调用来检查一个地址的代码。如果返回的是 0x,表示该地址是一个外部账户(普通地址)。如果返回的是非空的代码,表示该地址是一个合约地址。
curl https://eth.com/ \-X POST \-H "Content-Type: application/json" \--data '{"method":"eth_getCode","params":["0xdac17f958d2ee523a2206206994597c13d831ec7","latest"],"id":1,"jsonrpc":"2.0"}'

例如,若返回 "0x",则说明该地址是一个普通地址。如果返回一个非零的代码(例如:0x606060405260043610610196576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde031461019b5780630753c30c14610229578063095ea7b3146102625780630e136b191),表示这是一个合约地址。

  1. 使用 Etherscan 等区块链浏览器

如果你有地址的相关信息,可以通过访问区块链浏览器(如 Etherscan)查看该地址是否被标记为合约地址。Etherscan 会显示“合约”标签,指示该地址是智能合约地址。

  1. 通过交易历史
  • 普通地址通常只会执行简单的交易(例如转账以太币)。
  • 合约地址会有部署合约或调用合约函数的交易记录。如果通过交易记录可以看到该地址与智能合约的交互(如调用 transfermint 等合约函数),则可以确定该地址是合约地址。
  1. 检查地址的活动

合约地址通常会有复杂的交易活动,如调用函数、创建交易等。而普通地址只是参与普通的转账交易,通常没有其他的复杂行为。可以通过查看该地址的交易历史来做一个大致的判断。

通过这些方法,你可以有效地判断一个以太坊地址是普通地址还是合约地址。

2.2 代码示例

我们将使用第一种检查地址的代码是否存在的方法进行判断,并且使用golang的方法进行测试,以下是我们的代码示例

代码示例

package mainimport ("context""encoding/hex""fmt""github.com/ethereum/go-ethereum/common""github.com/ethereum/go-ethereum/ethclient""log"
)func main() {// 连接到以太坊节点client, err := ethclient.Dial("https://rpc.ankr.com/eth")if err != nil {log.Fatalf("Failed to connect to the Ethereum client: %v", err)}// 需要检查的以太坊地址address := common.HexToAddress("0xdac17f958d2ee523a2206206994597c13d831ec7")// 调用 eth_getCode 来检查地址是否是合约地址code, err := client.CodeAt(context.Background(), address, nil) // 第二个参数为 nil,表示查询最新的状态if err != nil {log.Fatalf("Failed to get code at address: %v", err)}// 将字节码转换为十六进制字符串,并以 '0x' 开头codeHex := "0x" + hex.EncodeToString(code)fmt.Println("code is:", codeHex)// 如果返回的代码为空,说明是普通地址;如果有代码,说明是合约地址if len(code) == 0 {fmt.Println("The address is a regular wallet address (EOA).")} else {fmt.Println("The address is a smart contract address.")}
}

2.3 拓展:数据库中查询

通常,我们可能需要从数据库中获取地址,然后并发判断地址类型,以下是我们的golang代码示例

package mainimport ("context""database/sql""fmt""log""sync""github.com/ethereum/go-ethereum/common""github.com/ethereum/go-ethereum/ethclient"_ "github.com/go-sql-driver/mysql"
)// 检查地址是否为合约地址的函数
func checkIfContract(client *ethclient.Client, address string, wg *sync.WaitGroup, results chan<- string) {defer wg.Done()// 将地址转换为eth的地址格式ethAddress := common.HexToAddress(address)// 查询地址的代码code, err := client.CodeAt(context.Background(), ethAddress, nil)if err != nil {results <- fmt.Sprintf("Error checking address %s: %v", address, err)return}// 判断代码是否为空if len(code) == 0 {results <- fmt.Sprintf("Address %s is a regular wallet address (EOA).", address)} else {results <- fmt.Sprintf("Address %s is a smart contract address.", address)}
}// 从数据库中获取以太坊地址
func getEthereumAddresses(db *sql.DB) ([]string, error) {var addresses []string// 执行查询(假设表名为 'eth_addresses',列名为 'address')rows, err := db.Query("SELECT address FROM eth_addresses")if err != nil {return nil, fmt.Errorf("failed to execute query: %w", err)}defer rows.Close()// 遍历查询结果并将地址加入到切片中for rows.Next() {var address stringif err := rows.Scan(&address); err != nil {return nil, fmt.Errorf("failed to scan row: %w", err)}addresses = append(addresses, address)}// 检查是否有错误发生if err := rows.Err(); err != nil {return nil, fmt.Errorf("error while iterating rows: %w", err)}return addresses, nil
}func main() {// 连接到MySQL数据库,使用连接池dsn := "username:password@tcp(localhost:3306)/your_database_name"db, err := sql.Open("mysql", dsn)if err != nil {log.Fatalf("Failed to connect to the database: %v", err)}defer db.Close()// 检查数据库连接是否可用if err := db.Ping(); err != nil {log.Fatalf("Failed to ping the database: %v", err)}// 获取所有以太坊地址addresses, err := getEthereumAddresses(db)if err != nil {log.Fatalf("Failed to get Ethereum addresses from database: %v", err)}// 连接到以太坊节点(使用Infura或Geth节点)client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID")if err != nil {log.Fatalf("Failed to connect to the Ethereum client: %v", err)}// 并发查询每个地址var wg sync.WaitGroupresults := make(chan string, len(addresses)) // 设置一个缓冲 channel 来存储结果// 启动多个 goroutine 并发查询for _, address := range addresses {wg.Add(1)go checkIfContract(client, address, &wg, results)}// 等待所有查询完成wg.Wait()close(results)// 打印所有结果for result := range results {fmt.Println(result)}
}

以上代码的优化点;

主要优化点:

  1. 数据库连接池使用:通过 sql.Open 创建数据库连接并使用连接池(db.Ping() 用于确保数据库连接可用)。不需要每次查询时都打开新连接。
  2. 错误处理改进:通过 fmt.Errorf 包装错误信息,使其更具可读性,并提供详细的上下文。
  3. sync.WaitGroupchan 并发控制:这些部分没有变化,但确保 goroutines 的执行是并发的且高效的。
  4. 日志:改进了错误日志,提供更清晰的错误上下文。
  5. CodeAt 查询上下文:确保查询时使用 context.Background(),而不是 nil,保持一致性。
  6. db.Ping():用于检查数据库连接是否正常,防止在查询时出现连接问题。

性能考虑:

  • 使用缓冲通道(results)来存储查询结果,避免了在多个 goroutine 中频繁地发生阻塞。
  • sync.WaitGroup 确保所有的并发任务在程序退出之前都已完成。

这种优化方式使得代码更健壮,易于维护,并提高了处理并发任务时的性能。


http://www.ppmy.cn/devtools/153038.html

相关文章

满足不同场景的需求的智慧物流开源了

智慧物流视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。构建基于Ai技术的…

WPS按双字段拆分工作表到独立工作簿-Excel易用宝

我们老板真是事多&#xff0c;他说要把这个工作表以月份和支付方式的维度&#xff0c;以这两个字段进行拆分工作表&#xff0c;而且拆分出来的表格要保存一个新的工作簿。 啥事都交给我&#xff0c;他还以为我有三头六臂呢&#xff0c;还好我有易用宝&#xff0c;可以轻松拆分…

Ruby语言的数据库编程

Ruby语言的数据库编程 引言 在现代软件开发中&#xff0c;数据库几乎是每个应用程序的重要组成部分。无论是简单的Web应用还是复杂的企业系统&#xff0c;数据存储和管理都是不可或缺的环节。Ruby是一种动态、面向对象的编程语言&#xff0c;因其优雅的语法和丰富的库支持而受…

基于微信小程序的安心陪诊管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

vscode环境中用仓颉语言开发时调出覆盖率的方法

在vscode中仓颉语言想得到在idea中利用junit和jacoco的覆盖率&#xff0c;需要如下几个步骤&#xff1a; 1.在vscode中搭建仓颉语言开发环境&#xff1b; 2.在源代码中右键运行[cangjie]coverage. 思路1&#xff1a;编写了测试代码的情况&#xff08;包管理工具&#xff09; …

4.flask-SQLAlchemy,表Model定义、增删查改操作

介绍 SQLAlchemy是对数据库的一个抽象 开发者不用直接与SQL语句打交道 Python对象来操作数据库 SQLAlchemy是一个关系型数据库 安装 flask中SQLAlchemy的配置 from flask import Flask from demo.user_oper import userdef create_app():app Flask(__name__)# 使用sessi…

Grafana系列之Dashboard:新增仪表板、新增变量、过滤变量、变量查询、导入仪表板、变量联动、Grafana Alert

概述 关于Prometheus和Grafana的安装&#xff0c;略过。 写在前面 Dashboard&#xff1a;仪表板&#xff0c;可包含多个PanelPanel&#xff1a;面板&#xff0c;Dashboard中的组件 如有写得不对的地方&#xff0c;烦请指出。 新增仪表板 点击右上角的 选择New dashboard…

数字图像处理:实验二

任务一&#xff1a; 将不同像素&#xff08;32、64和256&#xff09;的原图像放大为像素大 小为1024*1024的图像&#xff08;图像自选&#xff09; 要求&#xff1a;1&#xff09;输出一幅图&#xff0c;该图包含六幅子图&#xff0c;第一排是原图&#xff0c;第 二排是对应放大…