使用 Godot 引擎实现以太坊仪表盘 🛠️✨

今天我打算分享一个如何使用 Godot 引擎实现以太坊仪表盘的项目。Godot 引擎是一款类 Python 语言开发工具,所见即所得的开发过程非常方便和高效。

开发中的仪表盘界面展示

本项目的数据获取采用了 Etherscan API 和 JSON-RPC 接口。以下是详细的实现过程和相关代码。

Etherscan 的数据获取 📊

原先我使用 Etherscan API 获取账户数据。Etherscan 提供的 API 非常简单,只需要提供地址和 API Token,就能返回所需的数据,数据以 JSON 格式展示:

request_url = """https://api.etherscan.io/api
?module=account
&action=balance
&address=0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae
&tag=latest
&apikey=YourApiKeyToken"""

返回的 JSON 数据如下所示,其中 result 字段为钱包中的 ETH 数量,单位为 wei:

{
"status":"1",
"message":"OK",
"result":"40891626854930000000000"
}

为什么需要更多选择?

Etherscan API 对于免费用户每秒只能执行 5 次请求。由于我计划中的仪表盘需要查询的数据量较大,这样的限制使我们无法完全依赖 Etherscan API。为了确保数据的实时性和准确性,我们需要寻找其他数据获取方式。

使用 JSON-RPC 获取数据 🖥️

好在区块链提供了公共 RPC 节点进行调用。我们可以通过这些节点与区块链交互。由于 Godot 中没有 web3.jsweb3.py 等替代代码库,因此需要使用 JSON-RPC 请求获取数据。在此项目中,我们使用 Infura 提供的 RPC 节点和 INFURA_PROJECT_ID 进行数据请求。

以下是具体实现:

const INFURA_PROJECT_ID = "YOUR_INFURA_PROJECT_ID"
const INFURA_JSONRPC = "https://mainnet.infura.io/v3/" + INFURA_PROJECT_ID

var headers = ["Content-Type: application/json"]
var payload = {
"jsonrpc": 2.0,
"method": "",
"params": [],
"id": 1
}

func get_eth_balance(address: String):
# 获取以太坊主币金额
var data = Utils.deep_copy(payload)
data.method = "eth_getBalance"
data.params = [address, "latest"]
var payload = JSON.stringify(data)
get_eth_balance_request.request_completed.connect(_on_get_eth_balance_request_done)
get_eth_balance_request.request(INFURA_JSONRPC, headers, HTTPClient.METHOD_POST, payload)

返回的结果如下,其中 result 字段是余额,但格式为十六进制:

{
"jsonrpc":"2.0",
"id":1,
"result":"0x417dc47f925afc21b1c3"
}

为什么需要自己写转换器?

由于 Godot 原生不支持超大数值计算(超出 2^64-1),我们需要将十六进制格式的余额转换成十进制。

实现十六进制转十进制 🔄

为了处理十六进制格式的余额,我们需要一个转换器,将十六进制字符串转换成十进制:

## 将十六进制字符串转换成十进制,会丢失第 17 位之后的精度
func hex_to_decimal(hex_str: String) -> float:
# 移除字符串中的 0x 前缀
if hex_str.begins_with("0x"):
hex_str = hex_str.substr(2)
# 初始化结果
var result := 0.0
# 从十六进制字符串的最低位(最右边)开始遍历
for i in range(len(hex_str) - 1, -1, -1):
# 获取当前十六进制字符对应的十进制值,并使用浮点数来避免整数溢出问题(牺牲精度)
var decimal_value = float(hex_str[i].hex_to_int())
# 计算当前位的十进制值乘以16的相应幂次
var loc = len(hex_str) - i - 1
var value = decimal_value * pow(16, loc)
# 将计算结果相加
result = result + value
return result

通过这种方式,我们可以得到十进制格式的余额。虽然在精度上有些牺牲,但足以满足需求。

赠品 🎁

感谢您阅读到这里,下面附送两个额外的实用函数:

实现 round 函数,到 n 位小数

func round_to_decimals(x: float, digit: int) -> float:
return round(x * pow(10.0, digit)) / pow(10.0, digit)

格式化输出可读性更高的数值

## 将字符串进行格式化输出
## 输入:8372914982598237298347 18, 6
## 输出:8,372.91
func format_number(x: float, max_reserve_decimal: int = 6) -> String:
# 计算小数点前面有多少位
var digits = len(str(int(x)))
# 最少预留两个小数,最多预留 6 个小数。
var reserve_decimal = max(max_reserve_decimal - digits + 1, 2)
x = round_to_decimals(x, reserve_decimal)
# 格式化输出,加上逗号
var number_string = str(x)
var parts = number_string.split(".")
var num: String = parts[0]
var dec: String = "." + parts[1]
var reversed_str = num.reverse()
var formatted_str = ""
# 每三位插入逗号
for i in range(reversed_str.length()):
if i > 0 and i % 3 == 0:
formatted_str += ","
formatted_str += reversed_str[i]
# 再次反转字符串以得到正确顺序
formatted_str = formatted_str.reverse()
# 拼接小数部分
formatted_str += dec
return formatted_str

总结 🌟

通过本文,我们展示了如何使用 Godot 引擎和 Etherscan API 以及 JSON-RPC 接口来实现一个以太坊仪表盘。希望这些代码和方法对您有所帮助,并激发您在区块链开发中的更多灵感和创意。