[TOC]

Utilisez FMZ pour démarrer facilement avec le développement Web3 basé sur Ethereum
Ethereum est une plateforme de contrats intelligents basée sur la technologie blockchain, qui fournit un moyen décentralisé d’écrire et de déployer des contrats intelligents. Un contrat intelligent est un programme informatique spécial qui peut être exécuté automatiquement sur la blockchain et peut mettre en œuvre diverses logiques commerciales sans avoir besoin de faire confiance à un tiers.
La plateforme de trading quantitative Inventor (FMZ.COM) fournit une API facile à utiliser qui permet aux développeurs d’interagir plus facilement avec la blockchain Ethereum et son écosystème. Réalisez des fonctions telles que l’accès aux échanges décentralisés (DEX), l’obtention de données en chaîne et l’envoi de transactions.
Les exemples de ce tutoriel utilisentJavaScriptRédaction de langage, utilisation d’environnement de testRéseau principal Ethereum、Réseau de test Goerli. Vous pouvez également consulter les interfaces API utilisées dans le didacticiel ainsi que les descriptions et exemples de code associés dans la documentation API de la plateforme FMZ.
Avant d’apprendre à utiliser la plateforme de trading quantitative FMZ, nous devons nous familiariser avec plusieurs concepts de base :
Après vous être inscrit et connecté au site officiel de la plateforme de trading quantitatif FMZ (https://www.fmz.com), vous pouvez utiliser les différentes fonctions de la plateforme. Le site Web FMZ est l’extrémité de gestion de l’ensemble du système, et les programmes écrits par les utilisateurs sont réellement exécutés sur l’hôte. L’hôte est un logiciel qui peut être déployé sur divers appareils, tels que des serveurs, des ordinateurs, etc. Lorsqu’un utilisateur écrit un programme et crée une instance en cours d’exécution sur le site Web FMZ, la plate-forme FMZ communique avec l’hôte et démarre une instance de programme sur l’hôte.

Si vous souhaitez exécuter une instance de programme, vous devez déployer un hôte. Le déploiement de l’hôte est également très simple et il existe un didacticiel de déploiement sur la plateforme. Vous pouvez également utiliser « l’hôte de déploiement en un clic » fourni sur FMZ pour déployer automatiquement à l’aide du serveur loué par FMZ.
Le programme de garde peut être déployé et exécuté sur des serveurs, des ordinateurs personnels et d’autres appareils, à condition que le réseau soit normal (la cible correspondante doit être accessible, comme une interface d’échange, une adresse de nœud, etc.). Les principales étapes du déploiement sont :

robotfichier exécutable. Configurez l’adresse de communication du dépositaire. Cette adresse de communication est unique pour chaque compte FMZ. Après vous être connecté à FMZ,https://www.fmz.com/m/add-nodeLa page peut afficher sa propre adresse (c’est-à-dire./robot -s node.fmz.com/xxxxxCette chaîne d’adresses, icixxxxxLe contenu de l’emplacement est affiché différemment pour chaque compte FMZ). Enfin, vous devez saisir le mot de passe du compte FMZ. Après avoir configuré ces derniers, exécutez le programme hôte.Ajoutez une page dépositaire sur la plateforme FMZ, adresse :https://www.fmz.com/m/add-node

La plateforme de trading quantitative FMZ fournit un outil de débogage gratuit qui prend en chargeJavaScript,TypeScriptLa page est : https://www.fmz.com/m/debug, car la création d’une instance et son exécution sont facturées. Cet outil de débogage peut être utilisé pour tester et apprendre pendant la période d’apprentissage initiale. L’outil de débogage n’est pas différent de la création d’une instance, sauf que le temps d’exécution est limité à 3 minutes.
utiliserTypeScriptLorsque vous utilisez le langage, vous devez l’écrire dans la première ligne de code// @ts-checkPour passer àTypeScriptMode, s’il n’est pas commuté, la valeur par défaut estJavaScriptlangue.
Sur FMZ, le terme « échange » est un concept général. Pour un échange CEX, il fait référence à une configuration de compte d’échange spécifique. Pour le Web3, cet échange fait référence à une information de configuration, notamment l’adresse du nœud et la configuration de la clé privée.
Une fois connecté à la plateforme FMZ,https://www.fmz.com/m/add-platformPage, vous pouvez configurer les informations d’échange, où l’échange est un concept général.

choisirWeb3, configurez l’adresse du nœud RPC et la clé privée. Vous pouvez cliquer sur « Les informations sensibles sont cryptées et enregistrées à l’aide d’une clé privée indépendante » dans le coin inférieur droit pour afficher le mécanisme de sécurité.
Les nœuds peuvent être auto-construits ou fournis par des fournisseurs de services de nœuds. Il existe de nombreux fournisseurs de services de nœuds, tels que :Infura. Après inscription, vous pouvez visualiser l’adresse du nœud de votre compte. Il existe à la fois un réseau principal et un réseau de test, ce qui est plus pratique. Configurez cette adresse de nœud dans la figure ci-dessus.Rpc Addressdans les commandes. Les balises peuvent être nommées par elles-mêmes pour distinguer les objets d’échange configurés.

Dans la figurehttps://mainnet.infura.io/v3/xxxxxxxxxxxxxIl s’agit de l’adresse du nœud RPC du réseau principal privé Infura ETH.
Après avoir déployé le programme de conservation et configuré l’objet d’échange, vous pouvez utiliser « l’outil de débogage » de FMZ.COM pour les tests. Appelez les méthodes RPC d’Ethereum pour interagir avec Ethereum. En plus des nombreuses méthodes RPC répertoriées dans ce chapitre, vous pouvez vous référer à la documentation d’autres méthodes RPC, telles quehttps://www.quicknode.com/docs。
Prenons quelques exemples simples et commençons par les bases. Il existe des moyens d’accéder à Web3 pour différentes langues et outils, comme le montre la figure :

Les appels de méthode RPC sont également encapsulés sur FMZ. Ces fonctions sont encapsulées dans la fonction API FMZexchange.IOmilieu. La méthode d’appel estexchange.IO("api", "eth", ...). Le premier paramètre est fixe."api", le deuxième paramètre est fixe"eth", d’autres paramètres dépendent de la méthode RPC spécifique appelée.
Pour la sortie des informations, nous utilisons la plateforme FMZLogfonction,LogLa fonction peut transmettre plusieurs paramètres puis les afficher dans la zone de journal de la page « Debug Tool » ou « Real Trading » de la plateforme FMZ. La page « Debug Tool » sera l’outil principal pour nos tests.
Ethereumeth_getBalanceCette méthode permet d’interroger le solde ETH d’une adresse sur Ethereum. Cette méthode nécessite la transmission de deux paramètres.
Vérifions le fondateur d’EthereumV神Adresse du portefeuille ETH, les adresses connues sont :0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045。
function main() {
let ethBalance = exchange.IO("api", "eth", "eth_getBalance", "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest")
Log("ethBalance:", ethBalance)
}
Le dépositaire a été déployé (dans la figure : linux/amd64 …) et l’objet d’échange a été configuré (dans la figure : test Web3). Testez le code dans l’outil de débogage :

Cliquez sur le bouton « Exécuter » pour exécuter ce code et afficher les résultats :
ethBalance: 0x117296558f185bbc4c6
LogLa fonction imprimeethBalanceLes valeurs des variables sont :0x117296558f185bbc4c6, qui est un type de chaîne. OuiSolde ETH en valeur hexadécimale,parweiEn tant qu’unité,1e18 weiest 1ETH. Il doit donc être converti en un solde ETH décimal lisible.
VolontéethBalanceConvertir en données lisibles :
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 hauthttps://etherscan.io/Requête:

Cependant, ce traitement comportera des écarts dus à la précision de la langue elle-même, c’est pourquoi la plateforme FMZ dispose de deux fonctions intégrées pour le traitement des données :
Ajustez à nouveau le code :
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_chainIdetnet_versionLes usages sont similaires, nous les avons donc testés ensemble. Les deux fonctions renvoient l’ID de la blockchain à laquelle le nœud RPC actuel est connecté. La différence estnet_versionRenvoie l’ID décimal.eth_chainIdRenvoie l’ID hexadécimal.
Le nom du réseau correspondant à l’ID de la chaîne
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

Utiliser le réseau de test Ethereum configurégoerliTest de nœud :
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))
}

Appeleth_gasPriceMéthode pour interroger la chaîne actuellegas 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))
}
Ici, nous écrivons une fonction pour convertir une chaîne hexadécimale en une valeur lisible :toAmount. Une autre chose à noter est que l’unité du prix du gaz estwei, donc le paramètredecimalsLe paramètre réel correspondant peut être transmis comme valeur 0.
eth_blockNumbeUtilisé pour interroger la hauteur du bloc.
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))
}
Exécuter dans le débogueur :

https://etherscan.io/Sur demande :

Informations sur le bloc de requête.
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])
}
}
Les informations suivantes peuvent être obtenues en exécutant l’outil de débogage :

Il existe un grand nombre d’applications de contrats intelligents exécutées sur Ethereum.ENSest l’un d’entre eux.ENS, à savoir Ethereum Name Service, est un service décentralisé de résolution de noms de domaine basé sur la blockchain Ethereum.
Vous vous souvenez de l’exemple du tutoriel où nous avons interrogé le solde du portefeuille du fondateur d’Ethereum, Vitalik Buterin ? L’une des adresses de portefeuille de Vitalik est :0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045. Alors comment connaissons-nous cette adresse ? En fait, à traversENSContrat intelligent, utilisant un nom intuitifvitalik.eth(vitalik est le nom de Vitalik) pour mener la requête.
Le contenu suivant de ce chapitre utilise l’environnement du réseau principal Ethereum.ENSLe document montre que le nom de domaine Ethereum à interroger doit êtreHashing Names, utilisez le code suivant pourvitalik.ethLe nom est traité.
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))
}
}
Dans l’exemple de code ci-dessus, nous voyons une autre fonction inconnueEncodeCette fonction est la fonction API de la plateforme FMZ, qui est spécifiquement utilisée pour effectuer des opérations d’encodage sur la plateforme FMZ. Cette fonction prend en charge plusieurs méthodes d’encodage et plusieurs algorithmes de hachage.
Encode(algo, inputFormat, outputFormat, data, keyFormat, key string)
Selon la documentation ENS, utilisezsha3.keccak256Les algorithmes traitent les données.
AppelnameHashFonctions, par exemple :Log(nameHash("vitalik.eth")), nous pouvons obtenir :ee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835, vous devez ajouter le préfixe « 0x ».0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835En tant que contrat intelligent ENSresolverLes paramètres de la méthode.
let ensNode = "0x" + nameHash("vitalik.eth") // 准备好调用resolver方法的参数ensNode
Selon le document ENS, l’adresse du contrat de l’application de contrat intelligent ENS est :0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e. En appelant le contrat intelligentresolverAvant de continuer, nous devons préparer le contratABI。
Après avoir appris cela, je ne peux pas m’empêcher de demander : qu’est-ce qu’un contrat intelligent ?ABIÉtoffe de laine?
ABI,即应用程序二进制接口(Application Binary Interface),是智能合约与外部世界进行通信的接口标准。
智能合约的 ABI 定义了合约的函数接口、参数类型、返回值等信息,以及调用合约的方式和参数传递方式等规范。
智能合约的 ABI 通常以 JSON 格式存储,包含以下信息:
合约的函数接口:函数名、参数列表、返回值等信息。
函数参数类型:如 uint256、bool、string 等。
函数的输入参数和输出参数的编码方式:智能合约使用一种称为 Solidity ABI 的编码方式来编码函数的输入参数和输出参数,
以便与以太坊网络进行交互。
在以太坊网络中,使用智能合约的 ABI 来调用合约的函数。当需要调用合约函数时,需要提供函数名和函数参数,以及将函数参数按照 ABI 编码方式编码后的字节码。
以太坊节点会将这些信息打包成一笔交易,并将交易发送到以太坊网络中执行。
智能合约的 ABI 在 Solidity 语言中可以通过 interface 关键字来定义。以太坊开发工具如 Remix IDE、Truffle 等也提供了 ABI 编辑和生成工具,
使得开发者可以方便地创建和使用智能合约的 ABI。
Extraire les éléments suivants de l’ENS ABI :resolverL’ABI complet peut également être utilisé dans lehttps://etherscan.io/Vous pouvez interroger l’ABI du contrat sur GitHub ou obtenir l’ABI par d’autres moyens (par exemple, des documents de projet pertinents).

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"}]`
Ici, nous devons apprendre une nouvelle méthode d’appel sur la plateforme FMZ.exchange.IO("abi", address, abiContent), utilisez cette méthode pour enregistrer ABI,addressLe paramètre est l’adresse du contrat intelligent.abiContentLe paramètre est l’ABI du contrat intelligent correspondant (chaîne).
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智能合约的地址
Ensuite, vous pouvez appeler le contrat intelligent ENSresolverméthode, qui renvoieENS: Public ResolverL’adresse du contrat.

let resolverAddress = exchange.IO("api", "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", "resolver", ensNode)
utiliserENS: Public ResolverContractueladdrMéthode pour obtenir l’adresse du portefeuille de Vitalik. Pour appelerENS: Public ResolverLe contrat doit encore d’abord enregistrer l’ABI. Les informations ABI de ce contrat intelligent peuvent toujours être trouvées à l’adressehttps://etherscan.io/Obtenir.
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)

Dernier appelENS: Public ResolverContractueladdrMéthode, les paramètres sont toujoursensNode。
let vitalikAddress = exchange.IO("api", resolverAddress, "addr", ensNode)
Log("vitalikAddress:", vitalikAddress)
Sortie de la fonction log :

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)
}
Dans les chapitres précédents du cours, nous avons appris à configurer des clés privées. Pour l’objet d’échange configuré, comment connaissons-nous l’adresse du portefeuille correspondant à cette clé privée ? Disponible sur FMZexchange.IO("address")La fonction obtient l’adresse du portefeuille correspondant à la clé privée configurée.
Étant donné que le contenu suivant de ce chapitre utiliseGoerliEnvironnement réseau de test, donc le nœud que j’utilise est :https://goerli.infura.io/v3/*******Infura attribue des adresses de nœud différentes à chaque utilisateur enregistré.*******Le contenu spécifique est caché.
function main() {
let walletAddress = exchange.IO("address")
Log("测试网 goerli 钱包地址:", walletAddress)
}

Une fois que vous connaissez l’adresse de votre portefeuille, vous pouvez utiliser la méthode RPC d’Ethereumeth_getTransactionCountInterroger le nombre de transactions d’une adresse de portefeuille. Ce nombre est très couramment utilisé dans Ethereum. En fait, c’est ce qui doit être transmis lors d’un transfert d’argent.nonceParamètres, dans Ethereum, nonce est un nombre utilisé pour garantir que chaque transaction est unique. Il s’agit d’un nombre croissant qui est automatiquement incrémenté à chaque fois qu’une nouvelle transaction est envoyée. Par conséquent, lorsque vous envoyez une transaction à un contrat intelligent, vous devez fournir un nonce pour garantir que la transaction est unique et dans le bon ordre. Dans certaines données et documents, nous pouvons trouver :

Voici la bibliothèque Ethereum en langage GoPendingNonceAtLa fonction appelle en faiteth_getTransactionCountméthode. Dans le cours précédent, nous avons également appris à appeler des méthodes RPC. Nous l’utiliserons à nouveau ici.exchange.IO("api", "eth", ...)fonction.
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))
}
Avant d’expliquer le fonctionnement du transfert, comprenons brièvement quelques concepts. Lors du transfert d’argent sur Ethereum, une certaine quantité de jetons ETH sera consommée (en tant que frais de gaz). Le tarif du gaz est déterminé par deux paramètres :
Cependant, les frais de gaz sur le réseau Ethereum fluctuent toujours en fonction de la demande du marché et de ce que les utilisateurs sont prêts à payer, donc écrire des frais de gaz fixes dans le code n’est parfois pas idéal. Nous pouvons utiliser ce que nous avons appris auparavanteth_gasPriceMéthode permettant d’obtenir le prix moyen du gaz.
La limite de gaz pour un transfert d’éther standard est de 21 000 unités.
J’ai comprisnonce,gasPrice,gasLimitAvec ces concepts, vous pouvez tester le transfert. Une fonction de transfert très simple et facile à utiliser est encapsulée sur FMZ.
exchange.IO("api", "eth", "send", toAddress, toAmount)
Lorsqu’il est utilisé pour le transfert,exchange.IOLe troisième paramètre est toujours « envoyer ».toAddressLe paramètre est l’adresse qui reçoit l’ETH lors du transfert.toAmountLe montant d’ETH transféré.
nonce,gasPrice,gasLimitCes paramètres peuvent utiliser les valeurs obtenues automatiquement par le système par défaut sur FMZ. Vous pouvez également spécifier :
exchange.IO("api", "eth", "send", toAddress, toAmount, {gasPrice: 5000000000, gasLimit: 21000, nonce: 100})
Ensuite, nous transférons une certaine quantité d’ETH vers une certaine adresse sur le réseau de test 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
}
Parce que l’unité du montant du transfert Ethereum estwei, vous devez utiliser une fonction personnaliséetoInnerAmountTraité commeweiLa valeur de l’unité.
existerhttps://etherscan.io/Requête de hachage de transaction :0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e。

Vous pouvez également écrire du code pour interroger le hachage de transfert0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e,utilisereth_getTransactionReceiptMéthode à interroger.
function main() {
let transHash = "0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e"
let info = exchange.IO("api", "eth", "eth_getTransactionReceipt", transHash)
return info
}
Résultats de la requête :
{
"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": []
}
Description de chaque champ :
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 - 值的类型
Nous sommesLire les informations du contratCette section utilise un exemple complet pour appeler le contrat ENS déployé sur Ethereum pour obtenir l’adresse du portefeuille de Vitalik. Ces méthodes appartiennent àReadMéthodes, l’appel de ces méthodes n’est pas nécessairegas(Vous vous souvenez de ce dont nous avons parlé à propos du gaz avant ?). Dans cette section, nous appellerons certains contrats intelligents sur EthereumWriteMode et paiementgas. Ces opérations seront vérifiées par chaque nœud et mineur sur l’ensemble du réseau et modifieront l’état de la blockchain.
Pour les contrats ERC20 (contrats de jetons ERC20), la plateforme FMZ répertorie l’ABI du contrat ERC20 comme un ABI couramment utilisé et l’intègre directement dans le système, éliminant ainsi l’étape d’enregistrement de l’ABI. Nous avons également découvert l’ABI dans le tutoriel précédent. Lorsque nous appelons la méthode du contrat ENS, nous enregistrons d’abord l’ABI du contrat ENS.
Pour mieux comprendre l’ABI, vous pouvez le vérifier avant de l’utiliser. Voici l’ABI du contrat ERC20 :
”`javascript [{“constant”:true,“inputs”:[],“name”:“name”,“outputs”:[{“name”:“”,“type”:“string”}],“payable”:false,“stateMutability”:“view