[TOC]

Utilice FMZ para comenzar fácilmente con el desarrollo web3 basado en Ethereum
Ethereum es una plataforma de contratos inteligentes basada en tecnología blockchain, que proporciona una forma descentralizada de escribir e implementar contratos inteligentes. Un contrato inteligente es un programa informático especial que se puede ejecutar automáticamente en la cadena de bloques y puede implementar diversas lógicas comerciales sin la necesidad de confiar en un tercero.
La plataforma de comercio cuantitativo Inventor (FMZ.COM) proporciona una API fácil de usar que facilita a los desarrolladores la interacción con la cadena de bloques Ethereum y su ecosistema. Realizar funciones como el acceso a intercambios descentralizados (DEX), la obtención de datos en cadena y el envío de transacciones.
Los ejemplos de este tutorial utilizanJavaScriptEscritura de idiomas, uso del entorno de pruebaRed principal de Ethereum、Red de pruebas de Goerli. También puede ver las interfaces API utilizadas en el tutorial, así como descripciones relacionadas y ejemplos de código en la documentación API de la plataforma FMZ.
Antes de aprender a utilizar la plataforma de trading cuantitativo FMZ, debemos familiarizarnos con varios conceptos básicos:
Después de registrarse e iniciar sesión en el sitio web oficial de la plataforma de comercio cuantitativo FMZ (https://www.fmz.com), puede utilizar las diversas funciones de la plataforma. El sitio web de FMZ es el extremo administrativo de todo el sistema, y los programas escritos por los usuarios se ejecutan realmente en el host. El host es un programa de software que se puede implementar en varios dispositivos, como servidores, computadoras, etc. Cuando un usuario escribe un programa y crea una instancia en ejecución en el sitio web de FMZ, la plataforma FMZ se comunicará con el host e iniciará una instancia de programa en el host.

Si desea ejecutar una instancia de programa, debe implementar un host. La implementación del host también es muy sencilla y existe un tutorial de implementación en la plataforma. También puede utilizar el “host de implementación con un solo clic” proporcionado en FMZ para implementar automáticamente utilizando el servidor alquilado por FMZ.
El programa custodio se puede implementar y ejecutar en servidores, computadoras personales y otros dispositivos, siempre que la red sea normal (el objetivo correspondiente debe ser accesible, como una interfaz de intercambio, una dirección de nodo, etc.). Los principales pasos de la implementación son:

robotarchivo ejecutable. Configure la dirección de comunicación del custodio. Esta dirección de comunicación es única para cada cuenta de FMZ. Después de iniciar sesión en FMZ,https://www.fmz.com/m/add-nodeLa página puede ver su propia dirección (es decir,./robot -s node.fmz.com/xxxxxEsta cadena de direcciones, aquíxxxxxEl contenido de la ubicación se muestra de forma diferente para cada cuenta FMZ). Por último, deberá introducir la contraseña de la cuenta FMZ. Después de configurarla, ejecute el programa host.Añadir una página de custodia en la plataforma FMZ, dirección:https://www.fmz.com/m/add-node

La plataforma de comercio cuantitativo FMZ proporciona una herramienta de depuración gratuita que admiteJavaScript,TypeScriptLa página es: https://www.fmz.com/m/debug, porque crear una instancia y ejecutarla tiene costo. Esta herramienta de depuración se puede utilizar para realizar pruebas y aprender durante el período de aprendizaje inicial. La herramienta de depuración no es diferente de la creación de una instancia, excepto que el tiempo de ejecución está limitado a 3 minutos.
usarTypeScriptAl utilizar el lenguaje, debes escribirlo en la primera línea de código.// @ts-checkPara cambiar aTypeScriptModo, si no se cambia, el valor predeterminado esJavaScriptidioma.
En FMZ, “intercambio” es un concepto general. En el caso de un intercambio CEX, se refiere a una configuración de cuenta de intercambio específica. Para web3, este intercambio se refiere a información de configuración, incluida la dirección del nodo y la configuración de la clave privada.
Al iniciar sesión en la plataforma FMZ,https://www.fmz.com/m/add-platformPágina, puede configurar la información de intercambio, donde el intercambio es un concepto general.

elegirWeb3, configure la dirección del nodo RPC y la clave privada. Puede hacer clic en “La información confidencial se cifra y se guarda utilizando una clave privada independiente” en la esquina inferior derecha para ver el mecanismo de seguridad.
Los nodos pueden ser autoconstruidos o proporcionados por proveedores de servicios de nodos. Existen muchos proveedores de servicios de nodo, como por ejemplo:Infura. Después del registro, podrá ver la dirección de nodo de su cuenta. Existen redes principales y redes de prueba, que son más convenientes. Configure esta dirección de nodo en la figura anterior.Rpc Addressen los controles. Las etiquetas pueden tener su propio nombre para distinguir los objetos de intercambio configurados.

En la figurahttps://mainnet.infura.io/v3/xxxxxxxxxxxxxEs la dirección del nodo RPC de la red principal privada de Infura ETH.
Después de implementar el programa custodio y configurar el objeto de intercambio, puede utilizar la “herramienta de depuración” de FMZ.COM para realizar pruebas. Llama a los métodos RPC de Ethereum para interactuar con Ethereum. Además de los diversos métodos RPC que se enumeran en este capítulo, puedes consultar la documentación de otros métodos RPC, comohttps://www.quicknode.com/docs。
Tomemos algunos ejemplos sencillos y comencemos con lo básico. Existen formas de acceder a web3 para varios lenguajes y herramientas, como se muestra en la figura:

Las llamadas a métodos RPC también están encapsuladas en FMZ. Estas funciones están encapsuladas en la función API de FMZexchange.IOmedio. El método de llamada esexchange.IO("api", "eth", ...). El primer parámetro es fijo."api", el segundo parámetro es fijo"eth", otros parámetros dependen del método RPC específico llamado.
Para la salida de información utilizamos la plataforma FMZLogfunción,LogLa función puede pasar múltiples parámetros y luego mostrarlos en el área de registro de la página “Herramienta de depuración” o “Operaciones reales” de la plataforma FMZ. La página “Herramienta de depuración” será la herramienta principal para nuestras pruebas.
Ethereumeth_getBalanceEl método se utiliza para consultar el saldo de ETH de una dirección en Ethereum. Este método requiere que se ingresen dos parámetros.
Veamos quién es el fundador de EthereumV神Dirección de billetera ETH, las direcciones conocidas son:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045。
function main() {
let ethBalance = exchange.IO("api", "eth", "eth_getBalance", "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest")
Log("ethBalance:", ethBalance)
}
Se ha implementado el custodio (en la figura: linux/amd64 …) y se ha configurado el objeto de intercambio (en la figura: prueba Web3). Pruebe el código en la herramienta de depuración:

Haga clic en el botón “Ejecutar” para ejecutar este código y mostrar los resultados:
ethBalance: 0x117296558f185bbc4c6
LogLa función imprimeethBalanceLos valores de las variables son:0x117296558f185bbc4c6, que es un tipo de cadena. SíSaldo de ETH en valor hexadecimal,porweiComo unidad,1e18 weies 1ETH. Por lo tanto, es necesario convertirlo a un saldo ETH decimal legible.
VoluntadethBalanceConvertir a datos legibles:
function main() {
let ethBalance = exchange.IO("api", "eth", "eth_getBalance", "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest")
Log("ethBalance:", ethBalance)
// 将ethBalance转换为可读的数据
let vitalikEthBalance = parseInt(ethBalance.substring(2), 16) / 1e18
Log("vitalikEthBalance:", vitalikEthBalance)
}

En la parte superiorhttps://etherscan.io/Consulta:

Sin embargo, este procesamiento tendrá desviaciones debido a la precisión del propio lenguaje, por lo que la plataforma FMZ tiene dos funciones integradas para el procesamiento de datos:
Ajuste el código nuevamente:
function main() {
let ethBalance = exchange.IO("api", "eth", "eth_getBalance", "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest")
// ETH的精度单位为1e18
let ethDecimal = 18
Log("vitalikEthBalance:", Number((BigDecimal(BigInt(ethBalance)) / BigDecimal(Math.pow(10, ethDecimal))).toString()))
}
vitalikEthBalance: 5149.6244846875215
eth_chainIdynet_versionLos usos son similares, por lo que los probamos juntos. Ambas funciones devuelven el ID de la cadena de bloques a la que está conectado el nodo RPC actual. La diferencia esnet_versionDevuelve el Id decimal.eth_chainIdDevuelve el ID hexadecimal.
El nombre de la red correspondiente al ID de la cadena
1 - ethereum mainnet
2 - morden testnet (deprecated)
3 - ropsten testnet
4 - rinkeby testnet
5 - goerli testnet
11155111 - sepolia testnet
10 - optimism mainnet
69 - optimism kovan testnet
42 - kovan testnet
137 - matic/polygon mainnet
80001 - matic/polygon mumbai testnet
250 - fantom mainnet
100 - xdai mainnet
56 - bsc mainnet

Utilice la red de prueba Ethereum configuradagoerliPrueba de nodo:
function main() {
let netVersionId = exchange.IO("api", "eth", "net_version")
let ethChainId = exchange.IO("api", "eth", "eth_chainId")
Log("netVersionId:", netVersionId)
Log("ethChainId:", ethChainId, " ,转换:", parseInt(ethChainId.substring(2), 16))
}

Llamareth_gasPriceMétodo para consultar la cadena actualgas price。
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function main() {
let gasPrice = exchange.IO("api", "eth", "eth_gasPrice")
Log("gasPrice:", gasPrice, " ,转换:", toAmount(gasPrice, 0))
}
Aquí escribimos una función para convertir una cadena hexadecimal en un valor legible:toAmount. Otra cosa a tener en cuenta es que la unidad de gasPrice eswei, por lo que el parámetrodecimalsEl parámetro real correspondiente se puede pasar como 0.
eth_blockNumbeSe utiliza para consultar la altura del bloque.
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function main() {
let blockNumber = exchange.IO("api", "eth", "eth_blockNumber")
Log(toAmount(blockNumber, 0))
}
Ejecutar en el depurador:

https://etherscan.io/En consulta:

Consultar información del bloque.
function main() {
let blockNumber = exchange.IO("api", "eth", "eth_blockNumber")
Log(blockNumber)
let blockMsg = exchange.IO("api", "eth", "eth_getBlockByNumber", blockNumber, true)
Log(typeof(blockMsg), blockMsg)
// 由于Log输出的内容过多,会自动截断,所以遍历返回的区块信息各个字段,逐个打印
for (let key in blockMsg) {
Log("key:", key, ", val:", blockMsg[key])
}
}
La siguiente información se puede obtener ejecutando en la “Herramienta de depuración”:

Hay una gran cantidad de aplicaciones de contratos inteligentes que se ejecutan en Ethereum.ENSEs uno de ellos.ENS, es decir, Ethereum Name Service, es un servicio de resolución de nombres de dominio descentralizado basado en la cadena de bloques Ethereum.
¿Recuerdas el ejemplo del tutorial donde consultamos el saldo de la billetera del fundador de Ethereum, Vitalik Buterin? Una de las direcciones de billetera de Vitalik es:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045. Entonces ¿cómo sabemos esta dirección? De hecho, a través deENSContrato inteligente, con un nombre intuitivovitalik.eth(vitalik es el nombre de Vitalik) para realizar la consulta.
Los siguientes contenidos de este capítulo utilizan el entorno de red principal de Ethereum.ENSEl documento muestra que el nombre de dominio de Ethereum que se va a consultar debe serHashing Names, utilice el siguiente código paravitalik.ethEl nombre está procesado.
function nameHash(name) {
if (name == "") {
return "0000000000000000000000000000000000000000000000000000000000000000"
} else {
let arr = name.split(".")
let label = arr[0]
arr.shift()
let remainder = arr.join(".")
return Encode("sha3.keccak256", "hex", "hex", nameHash(remainder) + Encode("sha3.keccak256", "raw", "hex", label))
}
}
En el ejemplo de código anterior, vemos otra función desconocidaEncodeEsta función es la función API de la plataforma FMZ, que se utiliza específicamente para realizar operaciones de codificación en la plataforma FMZ. Esta función admite múltiples métodos de codificación y múltiples algoritmos hash.
Encode(algo, inputFormat, outputFormat, data, keyFormat, key string)
Según la documentación de ENS, utilicesha3.keccak256Los algoritmos procesan datos.
LlamarnameHashFunciones, por ejemplo:Log(nameHash("vitalik.eth")), podemos obtener:ee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835, debes agregar el prefijo “0x”.0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835Como contrato inteligente ENSresolverLos parámetros del método.
let ensNode = "0x" + nameHash("vitalik.eth") // 准备好调用resolver方法的参数ensNode
Según el documento ENS, la dirección del contrato de la aplicación de contrato inteligente ENS es:0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e. Al llamar al contrato inteligenteresolverAntes de continuar, necesitamos preparar el contrato.ABI。
Después de aprender esto, no puedo evitar preguntar: ¿qué es un contrato inteligente?ABI¿Paño de lana?
ABI,即应用程序二进制接口(Application Binary Interface),是智能合约与外部世界进行通信的接口标准。
智能合约的 ABI 定义了合约的函数接口、参数类型、返回值等信息,以及调用合约的方式和参数传递方式等规范。
智能合约的 ABI 通常以 JSON 格式存储,包含以下信息:
合约的函数接口:函数名、参数列表、返回值等信息。
函数参数类型:如 uint256、bool、string 等。
函数的输入参数和输出参数的编码方式:智能合约使用一种称为 Solidity ABI 的编码方式来编码函数的输入参数和输出参数,
以便与以太坊网络进行交互。
在以太坊网络中,使用智能合约的 ABI 来调用合约的函数。当需要调用合约函数时,需要提供函数名和函数参数,以及将函数参数按照 ABI 编码方式编码后的字节码。
以太坊节点会将这些信息打包成一笔交易,并将交易发送到以太坊网络中执行。
智能合约的 ABI 在 Solidity 语言中可以通过 interface 关键字来定义。以太坊开发工具如 Remix IDE、Truffle 等也提供了 ABI 编辑和生成工具,
使得开发者可以方便地创建和使用智能合约的 ABI。
Extraiga lo siguiente de ENS ABI:resolverEl ABI completo también se puede utilizar en elhttps://etherscan.io/Puede consultar el ABI del contrato en GitHub u obtenerlo a través de otros medios (por ejemplo, documentos relevantes del proyecto).

let abiENS_resolver = `[{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}]`
Aquí tenemos que aprender un nuevo método de llamada en la plataforma FMZ.exchange.IO("abi", address, abiContent), utilice este método para registrar ABI,addressEl parámetro es la dirección del contrato inteligente.abiContentEl parámetro es la ABI del contrato inteligente correspondiente (cadena).
let abiENS_resolver = `[{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}]`
exchange.IO("abi", "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", abiENS_resolver) // 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e 是在以太坊主网上部署的ENS智能合约的地址
A continuación, puedes llamar al contrato inteligente ENSresolvermétodo, que devuelveENS: Public ResolverLa dirección del contrato.

let resolverAddress = exchange.IO("api", "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", "resolver", ensNode)
usarENS: Public ResolverContractualaddrMétodo para obtener la dirección de billetera de Vitalik. Para llamarENS: Public ResolverEl contrato todavía necesita registrarse primero en ABI. La información ABI de este contrato inteligente todavía se puede encontrar enhttps://etherscan.io/Conseguir.
let abiENSPublicResolver = `[{"inputs":[{"internalType":"contract ENS","name":"_ens","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"contentType","type":"uint256"}],"name":"ABIChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"address","name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"coinType","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"newAddress","type":"bytes"}],"name":"AddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bool","name":"isAuthorised","type":"bool"}],"name":"AuthorisationChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"hash","type":"bytes"}],"name":"ContenthashChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"name","type":"bytes"},{"indexed":false,"internalType":"uint16","name":"resource","type":"uint16"},{"indexed":false,"internalType":"bytes","name":"record","type":"bytes"}],"name":"DNSRecordChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"name","type":"bytes"},{"indexed":false,"internalType":"uint16","name":"resource","type":"uint16"}],"name":"DNSRecordDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"DNSZoneCleared","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"bytes4","name":"interfaceID","type":"bytes4"},{"indexed":false,"internalType":"address","name":"implementer","type":"address"}],"name":"InterfaceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"NameChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"x","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"y","type":"bytes32"}],"name":"PubkeyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"string","name":"indexedKey","type":"string"},{"indexed":false,"internalType":"string","name":"key","type":"string"}],"name":"TextChanged","type":"event"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint256","name":"contentTypes","type":"uint256"}],"name":"ABI","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"addr","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint256","name":"coinType","type":"uint256"}],"name":"addr","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"authorisations","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"clearDNSZone","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"contenthash","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"name","type":"bytes32"},{"internalType":"uint16","name":"resource","type":"uint16"}],"name":"dnsRecord","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"name","type":"bytes32"}],"name":"hasDNSRecords","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes4","name":"interfaceID","type":"bytes4"}],"name":"interfaceImplementer","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"pubkey","outputs":[{"internalType":"bytes32","name":"x","type":"bytes32"},{"internalType":"bytes32","name":"y","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint256","name":"contentType","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setABI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint256","name":"coinType","type":"uint256"},{"internalType":"bytes","name":"a","type":"bytes"}],"name":"setAddr","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"a","type":"address"}],"name":"setAddr","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"isAuthorised","type":"bool"}],"name":"setAuthorisation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes","name":"hash","type":"bytes"}],"name":"setContenthash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setDNSRecords","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes4","name":"interfaceID","type":"bytes4"},{"internalType":"address","name":"implementer","type":"address"}],"name":"setInterface","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"string","name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"x","type":"bytes32"},{"internalType":"bytes32","name":"y","type":"bytes32"}],"name":"setPubkey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"setText","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes4","name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"string","name":"key","type":"string"}],"name":"text","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]`
exchange.IO("abi", resolverAddress, abiENSPublicResolver)

Última llamadaENS: Public ResolverContractualaddrMétodo, los parámetros siguen siendoensNode。
let vitalikAddress = exchange.IO("api", resolverAddress, "addr", ensNode)
Log("vitalikAddress:", vitalikAddress)
Salida de la función de registro:

vitalikAddress: 0xd8da6bf26964af9d7eed9e03e53415d37aa96045
function nameHash(name) {
if (name == "") {
return "0000000000000000000000000000000000000000000000000000000000000000"
} else {
let arr = name.split(".")
let label = arr[0]
arr.shift()
let remainder = arr.join(".")
return Encode("sha3.keccak256", "hex", "hex", nameHash(remainder) + Encode("sha3.keccak256", "raw", "hex", label))
}
}
function main() {
// 计算名称
let ensNode = "0x" + nameHash("vitalik.eth")
// 注册ENS合约
let abiENS_resolver = `[{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}]`
exchange.IO("abi", "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", abiENS_resolver)
let resolverAddress = exchange.IO("api", "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", "resolver", ensNode)
// 注册ENS Public Resolver合约
let abiENSPublicResolver = `[{"inputs":[{"internalType":"contract ENS","name":"_ens","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"contentType","type":"uint256"}],"name":"ABIChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"address","name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"coinType","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"newAddress","type":"bytes"}],"name":"AddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bool","name":"isAuthorised","type":"bool"}],"name":"AuthorisationChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"hash","type":"bytes"}],"name":"ContenthashChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"name","type":"bytes"},{"indexed":false,"internalType":"uint16","name":"resource","type":"uint16"},{"indexed":false,"internalType":"bytes","name":"record","type":"bytes"}],"name":"DNSRecordChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"name","type":"bytes"},{"indexed":false,"internalType":"uint16","name":"resource","type":"uint16"}],"name":"DNSRecordDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"DNSZoneCleared","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"bytes4","name":"interfaceID","type":"bytes4"},{"indexed":false,"internalType":"address","name":"implementer","type":"address"}],"name":"InterfaceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"NameChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"x","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"y","type":"bytes32"}],"name":"PubkeyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"string","name":"indexedKey","type":"string"},{"indexed":false,"internalType":"string","name":"key","type":"string"}],"name":"TextChanged","type":"event"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint256","name":"contentTypes","type":"uint256"}],"name":"ABI","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"addr","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint256","name":"coinType","type":"uint256"}],"name":"addr","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"authorisations","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"clearDNSZone","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"contenthash","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"name","type":"bytes32"},{"internalType":"uint16","name":"resource","type":"uint16"}],"name":"dnsRecord","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"name","type":"bytes32"}],"name":"hasDNSRecords","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes4","name":"interfaceID","type":"bytes4"}],"name":"interfaceImplementer","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"pubkey","outputs":[{"internalType":"bytes32","name":"x","type":"bytes32"},{"internalType":"bytes32","name":"y","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint256","name":"contentType","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setABI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint256","name":"coinType","type":"uint256"},{"internalType":"bytes","name":"a","type":"bytes"}],"name":"setAddr","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"a","type":"address"}],"name":"setAddr","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"isAuthorised","type":"bool"}],"name":"setAuthorisation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes","name":"hash","type":"bytes"}],"name":"setContenthash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setDNSRecords","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes4","name":"interfaceID","type":"bytes4"},{"internalType":"address","name":"implementer","type":"address"}],"name":"setInterface","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"string","name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"x","type":"bytes32"},{"internalType":"bytes32","name":"y","type":"bytes32"}],"name":"setPubkey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"setText","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes4","name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"string","name":"key","type":"string"}],"name":"text","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]`
exchange.IO("abi", resolverAddress, abiENSPublicResolver)
let vitalikAddress = exchange.IO("api", resolverAddress, "addr", ensNode)
Log("vitalikAddress:", vitalikAddress)
}
En los capítulos anteriores del curso, hemos aprendido a configurar claves privadas. Para el objeto de intercambio configurado, ¿cómo sabemos la dirección de billetera correspondiente a esta clave privada? Disponible en FMZexchange.IO("address")La función obtiene la dirección de la billetera correspondiente a la clave privada configurada.
Dado que los siguientes contenidos de este capítulo utilizanGoerliEntorno de red de prueba, por lo que el nodo que uso es:https://goerli.infura.io/v3/*******Infura asigna diferentes direcciones de nodo a cada usuario registrado.*******El contenido específico está oculto.
function main() {
let walletAddress = exchange.IO("address")
Log("测试网 goerli 钱包地址:", walletAddress)
}

Una vez que conozca la dirección de su billetera, puede utilizar el método RPC de Ethereumeth_getTransactionCountConsultar el recuento de transacciones de una dirección de billetera. Este recuento se utiliza con mucha frecuencia en Ethereum. De hecho, es lo que se debe pasar al transferir dinero.nonceParámetros: En Ethereum, nonce es un número utilizado para garantizar que cada transacción sea única. Es un número creciente que se incrementa automáticamente cada vez que se envía una nueva transacción. Por lo tanto, cuando envías una transacción a un contrato inteligente, debes proporcionar un nonce para garantizar que la transacción sea única y esté en el orden correcto. En algunos datos y documentos podemos encontrar:

Aquí está la biblioteca Ethereum en lenguaje GoPendingNonceAtLa función realmente llamaeth_getTransactionCountmétodo. En el curso anterior, también aprendimos a llamar a métodos RPC. Lo usaremos nuevamente aquí.exchange.IO("api", "eth", ...)función.
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function main() {
let walletAddress = exchange.IO("address")
Log("测试网 goerli 钱包地址:", walletAddress)
/**
* eth_getTransactionCount
* @param address - string - The address from which the transaction count to be checked.
* @param blockNumber - string - The block number as a string in hexadecimal format or tags.
* @returns The integer of the number of transactions sent from an address encoded as hexadecimal.
*/
let nonce = exchange.IO("api", "eth", "eth_getTransactionCount", walletAddress, "pending")
Log("钱包地址:", walletAddress, "当前的 nonce:", nonce, ",转换为10进制:", toAmount(nonce, 0))
}
Antes de explicar la operación de transferencia, entendamos brevemente algunos conceptos. Al transferir dinero en Ethereum, se consumirá una cierta cantidad de tokens ETH (como tarifas de gas). La tarifa del gas se determina en función de dos parámetros:
Sin embargo, las tarifas de gas en la red Ethereum siempre fluctúan según la demanda del mercado y lo que los usuarios están dispuestos a pagar, por lo que escribir una tarifa de gas fija en el código a veces no es ideal. Podemos usar lo que aprendimos anteseth_gasPriceMétodo mediante el cual se puede obtener el precio medio del gas.
El límite de gas para una transferencia de éter estándar es de 21.000 unidades.
Entiendononce,gasPrice,gasLimitCon estos conceptos podrás probar la transferencia. En FMZ se encapsula una función de transferencia muy simple y fácil de utilizar.
exchange.IO("api", "eth", "send", toAddress, toAmount)
Cuando se utiliza para transferencia,exchange.IOEl tercer parámetro es siempre “enviar”.toAddressEl parámetro es la dirección que recibe ETH durante la transferencia.toAmountLa cantidad de ETH transferida.
nonce,gasPrice,gasLimitEstos parámetros pueden utilizar los valores obtenidos automáticamente por el sistema por defecto en FMZ. También puede especificar:
exchange.IO("api", "eth", "send", toAddress, toAmount, {gasPrice: 5000000000, gasLimit: 21000, nonce: 100})
A continuación, transferimos una cierta cantidad de ETH a una determinada dirección en la red de prueba goerli:
function toInnerAmount(s, decimals) {
return (BigDecimal(s)*BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function main() {
let walletAddress = exchange.IO("address")
Log("测试网 goerli 钱包地址:", walletAddress)
let ret = exchange.IO("api", "eth", "send", "0x4D75a08E870674E68cAE611f329A27f446A66813", toInnerAmount(0.01, 18))
return ret // 返回Transaction Hash : 0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e
}
Porque la unidad del monto de transferencia de Ethereum eswei, necesitas usar una función personalizadatoInnerAmountProcesado comoweiEl valor de la unidad.
existirhttps://etherscan.io/Consulta de hash de transacción:0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e。

También puedes escribir código para consultar el hash de transferencia.0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e,usareth_getTransactionReceiptMétodo para realizar la consulta.
function main() {
let transHash = "0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e"
let info = exchange.IO("api", "eth", "eth_getTransactionReceipt", transHash)
return info
}
Resultados de la consulta:
{
"cumulativeGasUsed": "0x200850",
"effectiveGasPrice": "0x1748774421",
"transactionHash": "0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e",
"type": "0x0",
"blockHash": "0x6bdde8b0f0453ecd24eecf7c634d65306f05511e0e8f09f9ed3f59eee2d06ac7",
"contractAddress": null,
"blockNumber": "0x868a50",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasUsed": "0x5208",
"to": "0x4d75a08e870674e68cae611f329a27f446a66813",
"status": "0x1",
"transactionIndex": "0x23",
"from": "0x6b3f11d807809b0b1e5e3243df04a280d9f94bf4",
"logs": []
}
Descripción de cada campo:
blockHash - 该交易所在区块的哈希值
blockNumber - 以十六进制编码的该交易所在区块的块号
contractAddress - 如果是合约创建,该合约的地址;否则为null
cumulativeGasUsed - 该交易在区块中执行时使用的总燃气量
effectiveGasPrice - 每单位燃气的总基础费用加小费
from - 发送者的地址
gasUsed - 该特定交易使用的燃气量
logs - 生成该交易的日志对象数组
address - 生成该日志的地址
topics - 0到4个32字节索引日志参数的数据数组。在Solidity中,第一个主题是事件签名的哈希值(例如Deposit(address,bytes32,uint256)),除非你使用匿名说明符声明该事件
data - 日志的32字节非索引参数
blockNumber - 该日志所在区块的块号
transactionHash - 该日志创建时的交易哈希值。如果该日志处于待定状态,则为null
transactionIndex - 该日志创建时的交易索引位置。如果该日志处于待定状态,则为null
blockHash - 该日志所在区块的哈希值
logIndex - 该日志在区块中的索引位置,以十六进制编码的整数。如果该日志处于待定状态,则为null
removed - 如果该日志已被删除,则为true,由于链重组而被删除;如果是有效的日志,则为false
logsBloom - 用于检索相关日志的布隆过滤器
status - 以十六进制编码的值,它要么是1(成功),要么是0(失败)
to - 接收者的地址。如果是合约创建交易,则为null
transactionHash - 该交易的哈希值
transactionIndex - 以十六进制编码的该交易在区块中的索引位置
type - 值的类型
SomosLeer la información del contratoEsta sección utiliza un ejemplo completo para llamar al método de contrato ENS implementado en Ethereum para obtener la dirección de billetera de Vitalik. Estos métodos pertenecen aReadMétodos, no es necesario llamar a estos métodos.gas(¿Recuerdas que hablamos sobre el gas antes?) En esta sección llamaremos a algunos contratos inteligentes en EthereumWriteMétodo y pagogas. Estas operaciones serán verificadas por cada nodo y minero en toda la red y cambiarán el estado de la cadena de bloques.
Para los contratos ERC20 (contratos de token ERC20), la plataforma FMZ enumera el ABI del contrato ERC20 como un ABI comúnmente utilizado y lo incorpora directamente al sistema, eliminando el paso de registrar el ABI. También aprendimos sobre ABI en el tutorial anterior. Cuando llamamos al método de contrato ENS, primero registramos la ABI del contrato ENS.
Para comprender mejor el ABI, puede consultarlo antes de usarlo. A continuación, se muestra el ABI del contrato ERC20:
”`javascript [{“constant”:true,“inputs”:[],“name”:“name”,“outputs”:[{“name”:“”,“type”:“string”}],“payable”:false,“stateMutability”:“view”,“type”:“function”},{“constant”:false,“inputs”:[{“name”:“guy”,“type”:“address”},{“name”:“wad”,“type”:“uint256”}],“name”:“approve”,“outputs”:[{“name”:“”,“type”:“bool”}],“payable”:false,“stateMutability”:“nonpayable”,“type”:“function”},{“constant”:true,“inputs”:[],“name”:“totalSupply”,“outputs”:[{“name”:“”,“type”:“uint256”}],“payable”:false,“stateMutability”:“view”,“type”:“function”},{“constant”:false,“inputs”:[{“name”:“src”,“type”:“address”},{“name”:“dst”,“type”:“address”},{“name”:“wad”,“type”:“uint256”}],“name”:“transferFrom”,“outputs”: