关于ENS (以太坊域名服务)
ENS 全称是 Ethereum Name Service
,它是一个建立在以太坊区块链上的去中心化域名系统。
ENS 在 Web3 领域发挥着重要作用,主要有以下几个方面:
-
可读性更好的地址:
- ENS 允许用户将复杂的以太坊地址(如 0x12345…) 映射为更简单易记的域名。
- 这极大地提高了用户体验,让区块链的使用更加友好。
-
统一的身份标识:
- ENS 域名可以用作用户在 Web3 生态中的唯一标识。
- 用户可以将自己的钱包地址、social media账号等信息关联到ENS域名上。
-
去中心化的域名系统:
- ENS 是建立在以太坊区块链之上的,不受任何中心化机构的控制。
- 域名所有权通过区块链交易来管理,保证了所有权的去中心化。
-
支持多种应用场景:
- ENS 域名可以用于支付、登录、数据存储等各种 Web3 应用场景。
- 开发者可以在自己的 dApp 中集成 ENS 功能。
-
可编程性和可扩展性:
- 作为一个基础设施,ENS 提供了丰富的API和SDK,方便开发者集成和扩展。
- 未来 ENS 还可能支持更多类型的域名资源,如数据存储、身份认证等。
总ENS 是 Web3 生态中重要的一环,它在提高用户体验、统一身份标识等方面发挥着关键作用。
ENS(以太坊域名服务)可以认为是web3的DNS…DNS是将域名和IP地址做一个映射,而ENS是把域名和比IP地址更复杂冗长的钱包地址做一个映射
可以在 https://app.ens.domains/ 注册
折算后大概几十块人民币,和普通域名价格差不多
页面信息可以在这里查到: https://app.ens.domains/vitalik.eth
什么是以太坊域名服务 (ENS)?
比较著名的一个批量注册工具的ENS 成交榜,看得出比较好的"靓号"价格不菲…
关于TheGraph
TheGraph 可以说是 Web3 应用开发的基础设施之一,极大地提高了区块链数据的可访问性和可用性。
TheGraph 是一个去中心化的查询协议,在 Web3 领域扮演着非常重要的角色。主要用于索引和查询区块链数据。TheGraph 可以帮助用户做以下几件事:
-
索引区块链数据:
- TheGraph 会收集和处理区块链上产生的各种数据,包括交易记录、合约事件等。
- 通过定义 GraphQL 查询接口,开发者可以高效地查询和获取所需的数据。
-
为 dApp 提供数据查询服务:
- dApp 开发者可以利用 TheGraph 提供的索引数据,构建出更丰富的用户体验。
- 不需要自己处理复杂的区块链数据查询逻辑,大大降低了开发难度。
-
构建去中心化的数据市场:
- TheGraph 网络由一群索引者(Indexers)组成,他们负责处理数据并提供查询服务。
- Indexers可以获得来自 dApp 开发者的查询费用,形成一个去中心化的数据市场。
-
提高数据可靠性和安全性:
- 由于 TheGraph 是建立在区块链之上的,数据查询过程是透明、可验证的。
- 这大大提高了数据的可靠性和安全性,避免了单点故障问题。
如何使用TheGraph查询ENS信息?
以下是使用Go语言通过The Graph查询ENS信息的代码:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
)
const GraphQLEndpoint = "https://api.thegraph.com/subgraphs/name/ensdomains/ens"
type GraphQLRequest struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables"`
}
type GraphQLResponse struct {
Data struct {
Domains []struct {
ID string `json:"id"`
Name string `json:"name"`
LabelName string `json:"labelName"`
ResolvedAddress struct {
ID string `json:"id"`
} `json:"resolvedAddress"`
Owner struct {
ID string `json:"id"`
} `json:"owner"`
Resolver struct {
Address string `json:"address"`
} `json:"resolver"`
TTL string `json:"ttl"`
CreatedAt string `json:"createdAt"`
} `json:"domains"`
} `json:"data"`
}
func queryENS(domainName string) (*GraphQLResponse, error) {
query := `
query ($name: String!) {
domains(where: { name: $name }) {
id
name
labelName
resolvedAddress { id }
owner { id }
resolver { address }
ttl
createdAt
}
}
`
variables := map[string]interface{}{
"name": domainName,
}
requestBody, err := json.Marshal(GraphQLRequest{Query: query, Variables: variables})
if err != nil {
return nil, err
}
resp, err := http.Post(GraphQLEndpoint, "application/json", bytes.NewBuffer(requestBody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var graphQLResponse GraphQLResponse
err = json.Unmarshal(body, &graphQLResponse)
if err != nil {
return nil, err
}
return &graphQLResponse, nil
}
func main() {
domainName := "vitalik.eth"
result, err := queryENS(domainName)
if err != nil {
fmt.Printf("Error querying ENS: %v\n", err)
return
}
fmt.Println()
fmt.Println("result.Data.Domains 长度为:", len(result.Data.Domains))
if len(result.Data.Domains) > 0 {
domain := result.Data.Domains[0]
fmt.Printf("Domain Name: %s\n", domain.Name)
fmt.Printf("Owner: %s\n", domain.Owner.ID)
if domain.ResolvedAddress.ID != "" {
fmt.Printf("Resolved Address: %s\n", domain.ResolvedAddress.ID)
} else {
fmt.Println("Resolved Address: Not set")
}
createdAt, _ := time.Parse(time.RFC3339, domain.CreatedAt)
fmt.Printf("Created At: %s\n", createdAt.Format(time.RFC3339))
} else {
fmt.Println("Domain not found")
}
}
这个Go程序执行以下操作:
-
定义了必要的结构体来表示GraphQL请求和响应。
-
实现了
queryENS
函数,该函数构造GraphQL查询,发送HTTP POST请求到The Graph的API端点,并解析响应。 -
在
main
函数中,我们使用"vitalik.eth"作为示例域名进行查询。 -
程序打印出查询结果,包括域名、所有者地址、解析地址(如果设置了的话)和创建时间。
可以查到数据
但请求次数稍微一多,就会限流:
{"message":"Rate-limit on ensdomains/ens community key exceeded. Try again later or go to https://thegraph.com/studio to create your own API key. Find the ensdomains/ens Graph Network Subgraph here: https://thegraph.com/explorer/subgraphs/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH"}
解决方案:
创建自己的 API 密钥:
-
- 如错误消息所示,可以在 https://thegraph.com/studio 创建自己的 API 密钥。这将有更高的查询频率限制 (免费的API key,每个月可以进行10万次查询)
-
- 使用 Graph Network:错误消息提供了 ENS 子图在 Graph Network 上的链接。可以直接使用这个网络版本的子图。
我申请了一个API key,类似 e65d654167ba349f029f0exxxxxxxxxx
根据文档,以及该子图主页的提示,修改代码如下:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"time"
)
const GraphQLEndpoint = "https://gateway-arbitrum.network.thegraph.com/api/e65d654167ba349f029f0exxxxxxxxxx/subgraphs/id/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH"
type GraphQLRequest struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables"`
}
type GraphQLResponse struct {
Data struct {
Domains []struct {
ID string `json:"id"`
Name string `json:"name"`
LabelName string `json:"labelName"`
ResolvedAddress struct {
ID string `json:"id"`
} `json:"resolvedAddress"`
Owner struct {
ID string `json:"id"`
} `json:"owner"`
Resolver struct {
Address string `json:"address"`
} `json:"resolver"`
TTL string `json:"ttl"`
CreatedAt string `json:"createdAt"`
} `json:"domains"`
} `json:"data"`
}
func queryENS(domainName string) (*GraphQLResponse, error) {
query := `
query ($name: String!) {
domains(where: { name: $name }) {
id
name
labelName
resolvedAddress { id }
owner { id }
resolver { address }
ttl
createdAt
}
}
`
variables := map[string]interface{}{
"name": domainName,
}
requestBody, err := json.Marshal(GraphQLRequest{Query: query, Variables: variables})
if err != nil {
return nil, err
}
resp, err := http.Post(GraphQLEndpoint, "application/json", bytes.NewBuffer(requestBody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// 打印原始响应
fmt.Println("Raw response:", string(body))
var graphQLResponse GraphQLResponse
err = json.Unmarshal(body, &graphQLResponse)
if err != nil {
return nil, err
}
return &graphQLResponse, nil
}
func main() {
domainName := "vitalik.eth"
result, err := queryENS(domainName)
if err != nil {
fmt.Printf("Error querying ENS: %v\n", err)
return
}
fmt.Println()
fmt.Println("result.Data.Domains 长度为:", len(result.Data.Domains))
if len(result.Data.Domains) > 0 {
domain := result.Data.Domains[0]
fmt.Printf("Domain Name: %s\n", domain.Name)
fmt.Printf("Owner: %s\n", domain.Owner.ID)
if domain.ResolvedAddress.ID != "" {
fmt.Printf("Resolved Address: %s\n", domain.ResolvedAddress.ID)
} else {
fmt.Println("Resolved Address: Not set")
}
fmt.Println("domain.CreatedAt is:", domain.CreatedAt)
intCreateAt, _ := strconv.Atoi(domain.CreatedAt)
createdAt := time.Unix(int64(intCreateAt), 0).Format(time.DateTime)
fmt.Printf("Created At: %s\n", createdAt)
} else {
fmt.Println("Domain not found")
}
}
返回值为:
Raw response: {"data":{"domains":[{"createdAt":"1497775154","id":"0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835","labelName":"vitalik","name":"vitalik.eth","owner":{"id":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045"},"resolvedAddress":{"id":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045"},"resolver":{"address":"0x231b0ee14048e9dccd1d247744d114a4eb5e8e63"},"ttl":null}]}}
result.Data.Domains 长度为: 1
Domain Name: vitalik.eth
Owner: 0xd8da6bf26964af9d7eed9e03e53415d37aa96045
Resolved Address: 0xd8da6bf26964af9d7eed9e03e53415d37aa96045
domain.CreatedAt is: 1497775154
Created At: 2017-06-18 16:39:14