TheGraph的使用

关于TheGraph


TheGraph 是一个去中心化的查询协议,在 Web3 领域扮演着非常重要的角色。它主要用于索引和查询区块链数据。具体来说,TheGraph 可以帮助你做以下几件事:

  1. 索引区块链数据:

    • TheGraph 会收集和处理区块链上产生的各种数据,包括交易记录、合约事件等。
    • 通过定义 GraphQL 查询接口,开发者可以高效地查询和获取所需的数据。
  2. 为 dApp 提供数据查询服务:

    • dApp 开发者可以利用 TheGraph 提供的索引数据,构建出更丰富的用户体验。
    • 不需要自己处理复杂的区块链数据查询逻辑,大大降低了开发难度。
  3. 构建去中心化的数据市场:

    • TheGraph 网络由一群索引者(Indexers)组成,他们负责处理数据并提供查询服务。
    • Indexers可以获得来自 dApp 开发者的查询费用,形成一个去中心化的数据市场。
  4. 提高数据可靠性和安全性:

    • 由于 TheGraph 是建立在区块链之上的,数据查询过程是透明、可验证的。
    • 这大大提高了数据的可靠性和安全性,避免了单点故障问题。

总的来说,TheGraph 可以说是 Web3 应用开发的基础设施之一,极大地提高了区块链数据的可访问性和可用性。它正在推动 Web3 生态的进一步发展。



有个服务用这个来查询ENS信息 (即以太坊域名…把钱包地址映射为一个比较可读的字符串)


`curl –location –request POST ‘https://api.thegraph.com/subgraphs/name/ensdomains/ens‘ \

–header ‘Cookie: __cf_bm=9JeLfwGh.x9KQK4Ed34C2r6BewB1t680S1ERkyJ2KOk-1718939887-1.0.1.1-yeJ1LNmr84NjsqVX2jgewHgbWuFaVSDtoORBrIHJXbSp_EuI1CVrZM_81TIT6mvX0xAFk0Art_9de33biXT6pA’`

返回:

{“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"}


关于ENS


ENS 全称是 Ethereum Name Service,它是一个建立在以太坊区块链上的去中心化域名系统。ENS 在 Web3 领域发挥着重要作用,主要有以下几个方面:

  1. 可读性更好的地址:

    • ENS 允许用户将复杂的以太坊地址(如 0x12345…) 映射为更简单易记的域名。
    • 这极大地提高了用户体验,让区块链的使用更加友好。
  2. 统一的身份标识:

    • ENS 域名可以用作用户在 Web3 生态中的唯一标识。
    • 用户可以将自己的钱包地址、social media账号等信息关联到ENS域名上。
  3. 去中心化的域名系统:

    • ENS 是建立在以太坊区块链之上的,不受任何中心化机构的控制。
    • 域名所有权通过区块链交易来管理,保证了所有权的去中心化。
  4. 支持多种应用场景:

    • ENS 域名可以用于支付、登录、数据存储等各种 Web3 应用场景。
    • 开发者可以在自己的 dApp 中集成 ENS 功能。
  5. 可编程性和可扩展性:

    • 作为一个基础设施,ENS 提供了丰富的API和SDK,方便开发者集成和扩展。
    • 未来 ENS 还可能支持更多类型的域名资源,如数据存储、身份认证等。

总的来说, ENS 是 Web3 生态中重要的一环,它在提高用户体验、统一身份标识等方面发挥着关键作用。随着 Web3 技术的进一步发展,ENS 也必将扮演愈发重要的角色。

可以在 https://app.ens.domains/ 注册

折算后大概几十块人民币,和普通域名价格差不多


页面信息可以在这里查到: https://app.ens.domains/vitalik.eth

关于ENS(以太坊域名服务): 可以认为是web3的DNS…DNS是将域名和IP地址做一个映射,而ENS是把域名和比IP地址更复杂冗长的钱包地址做一个映射

什么是以太坊域名服务 (ENS)?

比较著名的一个批量注册工具的ENS 成交榜,看得出比较好的”靓号”价格不菲..


创建API key


如何使用the graph查询ENS信息?


当然,我很乐意为您提供一个使用Go语言编写的示例。以下是使用Go语言通过The Graph查询ENS信息的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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程序执行以下操作:

  1. 定义了必要的结构体来表示GraphQL请求和响应。

  2. 实现了queryENS函数,该函数构造GraphQL查询,发送HTTP POST请求到The Graph的API端点,并解析响应。

  3. main函数中,我们使用”vitalik.eth”作为示例域名进行查询。

  4. 程序打印出查询结果,包括域名、所有者地址、解析地址(如果设置了的话)和创建时间。

要运行这个程序,你需要先安装Go,然后将代码保存为.go文件(例如ens_query.go),最后在命令行中运行:

1
go run ens_query.go

这个示例展示了如何使用Go语言查询基本的ENS信息。你可以根据需要修改GraphQL查询来获取更多或不同的ENS数据。


请求次数稍微一多,就会限流:

1
{"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"}

我理解了问题所在,非常感谢您提供这个错误信息。这个错误表明我们遇到了 The Graph 的速率限制。这是因为我们使用的是公共的 API 端点,它有使用限制。让我解释一下情况并提供几个解决方案:

  1. 速率限制:
    The Graph 为公共访问设置了速率限制,以防止过度使用。

  2. 解决方案:

    a. 等待一段时间再试:
    最简单的方法是等待一段时间(可能几分钟到几小时),然后再次尝试查询。

    b. 创建自己的 API 密钥:
    如错误消息所示,你可以在 https://thegraph.com/studio 创建自己的 API 密钥。这将给你更高的查询限制。

    c. 使用 Graph Network:
    错误消息提供了 ENS 子图在 Graph Network 上的链接。你可以考虑直接使用这个网络版本的子图。

  3. 代码修改:
    如果你决定创建自己的 API 密钥,你需要修改代码以包含这个密钥。这通常涉及在 HTTP 请求头中添加认证信息。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func queryENS(domainName string) (*GraphQLResponse, error) {
// ... [其他代码保持不变]

req, err := http.NewRequest("POST", GraphQLEndpoint, bytes.NewBuffer(requestBody))
if err != nil {
return nil, err
}

req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer YOUR_API_KEY_HERE")

client := &http.Client{}
resp, err := client.Do(req)
// ... [后续代码]
}
  1. 使用 Graph Network:
    如果你想使用 Graph Network 版本的子图,你需要更新 GraphQLEndpoint 常量:
1
const GraphQLEndpoint = "https://gateway.thegraph.com/api/[API_KEY]/subgraphs/id/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH"

注意:你需要将 [API_KEY] 替换为你从 The Graph 获得的实际 API 密钥。

  1. 错误处理:
    考虑添加特定的错误处理来捕获速率限制错误:
1
2
3
if resp.StatusCode == 429 {
return nil, fmt.Errorf("rate limit exceeded, please try again later")
}

建议:

  1. 首先尝试等待一段时间后再次运行你的程序。
  2. 如果你计划频繁使用这个 API,强烈建议你创建自己的 API 密钥。
  3. 在开发过程中,考虑实现一些缓存机制来减少 API 调用次数。

如果你决定创建 API 密钥或使用 Graph Network,我可以帮你更详细地修改代码。请告诉我你打算如何处理,我会提供进一步的指导。



上面说的第一种方式,即

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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)) // 需要添加 API key,否则很容易被限流

req, err := http.NewRequest("POST", GraphQLEndpoint, bytes.NewBuffer(requestBody))
if err != nil {
return nil, err
}

req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer xxxxx")

client := &http.Client{}
resp, err := client.Do(req)

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)
createdAt, _ := time.Parse(time.RFC3339, domain.CreatedAt)

fmt.Printf("Created At: %s\n", createdAt.Format(time.RFC3339))
} else {
fmt.Println("Domain not found")
}
}

我申请了一个 xxxxx

但发现按这种方式,也总是429…冷却期太长了


尝试使用第二种:

根据文档,似乎也要用这种方式 https://thegraph.com/docs/en/querying/querying-the-graph/

https://thegraph.com/explorer/subgraphs/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH

这种方式可以~



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package main

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"time"
)

// const GraphQLEndpoint = "https://api.thegraph.com/subgraphs/name/ensdomains/ens"
const GraphQLEndpoint = "https://gateway-arbitrum.network.thegraph.com/api/xxxxx/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)) // 需要添加 API key,否则很容易被限流

//req, err := http.NewRequest("POST", GraphQLEndpoint, bytes.NewBuffer(requestBody))
//if err != nil {
// return nil, err
//}
//
//req.Header.Set("Content-Type", "application/json")
//req.Header.Set("Authorization", "Bearer xxxxx")
//
//client := &http.Client{}
//resp, err := client.Do(req)

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")
}
}

返回值为:

1
2
3
4
5
6
7
8
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