O recurso está a ser carregado... Carregamento...

Comece com o desenvolvimento web3 facilmente baseado no Ethereum usando FMZ

Autora:FMZ~Lydia, Criado: 2023-06-25 09:17:53, Atualizado: 2023-09-18 20:28:19

[TOC]

img

Comece com o desenvolvimento web3 facilmente baseado no Ethereum usando FMZ

EtherEaseWithFMZ Tutorial

Comece com o desenvolvimento web3 baseado no Ethereum usando o FMZ facilmente

Ethereum é uma plataforma de contrato inteligente baseada na tecnologia blockchain, que fornece uma maneira descentralizada de escrever e implantar contratos inteligentes.

Plataforma de negociação quântica FMZ (FMZ.COMA plataforma fornece uma API fácil de usar, permitindo que os desenvolvedores interajam mais facilmente com o blockchain Ethereum e seu ecossistema.

Neste tutorial, os exemplos são escritos emJavaScriptlinguagem, o ambiente de teste utiliza ambos osRede principal EthereumeRede de ensaio de GoerliE você também pode ver as interfaces API e descrições relacionadas, exemplos de código utilizados no tutorial na documentação API da plataforma FMZ.


Começo do FMZ

Antes de aprender a usar a Plataforma de Negociação Quant FMZ, precisamos nos familiarizar com alguns conceitos básicos:

1. Arquitetura da plataforma de negociação quântica FMZ

Após registar-se e entrar no site oficial da Plataforma de Negociação Quant da FMZ (https://www.fmz.comO programa de software docker pode ser implantado em vários dispositivos, como servidores, computadores, etc. Quando um usuário escreve um programa e cria uma instância em execução no site do FMZ, a plataforma FMZ se comunica com o docker e inicia uma instância de programa nele.

2. Docker

Se você quiser executar uma instância de programa, você deve implantar um docker. A implantação do docker também é muito simples, e há tutoriais de implantação na plataforma. Você também pode usar o One-click Deployment fornecido pela FMZ para implantar automaticamente em servidores alugados em nome da FMZ.

  • Implementar docker em dispositivos pessoais

Você pode implantar e executar o programa docker em servidores, computadores pessoais e outros dispositivos, desde que a rede seja normal (precisa ser capaz de acessar o alvo correspondente, como uma determinada interface de troca, endereço de nó, etc.).

  1. Login ou abrir o dispositivo onde o programa docker deve ser implantado, tais comoLogar em um servidorouligando um computador para entrar no sistema operacional.
  2. Baixe a versão correspondente do programa docker (dependendo do sistema operacional do dispositivo), página de download:https://www.fmz.com/m/add-node

img

  1. O que baixaste é um pacote comprimido, precisa ser descomprimido.
  2. Executar o programa docker, o programa docker é um arquivo executável chamadorobotConfigure o endereço de comunicação do docker, que é exclusivo para cada conta FMZ, depois de entrar no FMZ, você pode ver seu próprio endereço emhttps://www.fmz.com/m/add-nodepágina (ou seja,./robot -s node.fmz.com/xxxxxesta cadeia de endereços, onde o conteúdo noxxxxxA posição é diferente para cada conta FMZ). Finalmente, você precisa inserir a senha da sua conta FMZ. Depois de configurar essas configurações, execute o programa docker.
  • Utilize a função One-Click Deployment da plataforma FMZ

    Adicionar uma página docker na plataforma FMZ, endereço:https://www.fmz.com/m/add-node

    img

3. Ferramenta de depuração

FMZ Quant Trading Platform fornece uma ferramenta de depuração gratuita que suportaJavaScript, TypeScript, e a página é:https://www.fmz.com/m/debug. Porque a criação de instâncias para executar é cobrada. Durante o período inicial de aprendizagem, você pode usar essa ferramenta de depuração para testes e aprendizado. Exceto pelo limite máximo de tempo de execução de 3 minutos, não há diferença entre usar a ferramenta de depuração e criar uma instância para executar.

Quando utilizar oTypeScriptlinguagem, você precisa escrever// @ts-checkna primeira linha de código para mudar paraTypeScriptmodo; se não for comutado, o padrão éJavaScript language.

4. Plataformas

No FMZ, Platform é um conceito geral. Para as exchanges CEX, refere-se a uma configuração específica de conta de troca. Para o web3, esta troca refere-se a uma informação de configuração que inclui endereço de nó e configuração de chave privada.

No estado de login da plataforma FMZ, emhttps://www.fmz.com/m/add-platformpágina, você pode configurar informações de troca, onde a troca é um conceito geral.

img

SelecionarWeb3, configure endereço de nó RPC, configure chave privada, você pode clicar no canto inferior direito Informações sensíveis serão armazenadas criptografadas para visualizar o mecanismo de segurança.

Os nós podem ser nós auto-construídos ou fornecidos por provedores de serviços de nós.Infura. Após o registro, você pode ver o endereço do nó da sua própria conta. Tanto a mainnet quanto a testnet estão disponíveis, o que é bastante conveniente. Configure este endereço de nó noRpc AddressO rótulo pode ser nomeado por si mesmo para distinguir entre os objetos de troca configurados.

img

Na foto,https://mainnet.infura.io/v3/xxxxxxxxxxxxxé o endereço do nó RPC privado da rede principal Infura ETH.


Interagir com Ethereum usando FMZ

Depois de implantar o programa docker e configurar o objeto de troca, você pode usarFMZ.COMs Debugging Tool para testes. Chame os métodos Ethereum RPC e interaja com o Ethereum, além dos vários métodos RPC listados e introduzidos neste capítulo, outros métodos RPC podem ser encontrados consultando materiais, como:https://www.quicknode.com/docs.

Para várias linguagens e ferramentas, existem maneiras de acessar o web3, como mostrado na imagem:

img

No FMZ, as chamadas de método RPC também são encapsuladas e essas funções são encapsuladas na função FMZ APIexchange.IOO método de chamada é:exchange.IO("api", "eth", ...). O primeiro parâmetro é fixado em"api", o segundo parâmetro é fixado em"eth", e outros parâmetros dependem do método RPC específico a ser chamado.

Para a informação de saída, utilizaremos oLogA função da plataforma FMZLogA função pode aceitar vários parâmetros e, em seguida, produzi-los na área de log da página Debug Tool ou Bot na plataforma FMZ. A página Debug Tool será nossa principal ferramenta de teste.

eth_getBalance

Oeth_getBalancemétodo de Ethereum é usado para consultar o saldo ETH de um endereço no Ethereum, e este método requer dois parâmetros.

  • Endereço a consultar.
  • Na etiqueta, usamos geralmente ultimato. Vamos verificar o fundador do EthereumVitalik ButerinO endereço da carteira da ETH, o endereço conhecido é:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045.
function main() {
    let ethBalance = exchange.IO("api", "eth", "eth_getBalance", "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest")
    Log("ethBalance:", ethBalance)
}

Já implantado o docker (na imagem: linux/amd64...) e configurado o objeto de troca (na imagem: Web3 teste), teste de código na ferramenta de depuração:

img

Clique no botão Execute para executar o código e exibir os resultados:

EthBalance: 0x117296558f185bbc4c6

OlogA função imprime oethBalanceValor da variável:0x117296558f185bbc4c6, que é um tipo de string.o valor hexadecimal do saldo do ETHemweiunidades, com1e18 weisendo igual a 1ETHPortanto, precisa ser convertido para se tornar um saldo decimal de ETH legível.

ConversãoethBalanceem dados legíveis:

function main() {
    let ethBalance = exchange.IO("api", "eth", "eth_getBalance", "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest")
    Log("ethBalance:", ethBalance)
    
    // Converting ethBalance into readable data
    let vitalikEthBalance = parseInt(ethBalance.substring(2), 16) / 1e18
    Log("vitalikEthBalance:", vitalikEthBalance)
}

Procure emhttps://etherscan.io/:

img

No entanto, devido ao problema de precisão da própria linguagem, haverá desvios nesta forma de processamento.

  • BigInt: Converte cadeia hexadecimal para objeto BigInt.
  • BigDecimal: Converte objetos de tipo numérico em objetos BigDecimal computáveis.

Ajusta o código outra vez:

function main() {
    let ethBalance = exchange.IO("api", "eth", "eth_getBalance", "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest")

    // The precision unit of ETH is 1e18
    let ethDecimal = 18
    Log("vitalikEthBalance:", Number((BigDecimal(BigInt(ethBalance)) / BigDecimal(Math.pow(10, ethDecimal))).toString()))
}

VitalEthBalanço: 5149.6244846875215

Eth_chainId

eth_chainIdenet_versionAmbas as funções retornam a ID do blockchain ao qual o nó RPC atual está conectado, com a diferença de quenet_versionRetorna um Id decimal eeth_chainIdRetorna um ID hexadecimal.

Nome da rede correspondente ao chainId

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

Teste com a rede de teste Ethereum configuradagoerliNodo:

function main() {
    let netVersionId = exchange.IO("api", "eth", "net_version")
    let ethChainId = exchange.IO("api", "eth", "eth_chainId")

    Log("netVersionId:", netVersionId)
    Log("ethChainId:", ethChainId, " , conversion:", parseInt(ethChainId.substring(2), 16))
}

Eth_gasPrice

Ligue para oeth_gasPriceMétodo para consultar a correntegas pricena cadeia.

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, " , conversion:", toAmount(gasPrice, 0))
}

Aqui escrevemos uma função para converter a cadeia hexadecimal em um valor numérico legível:toAmountAlém disso, note que a unidade degasPriceéwei, então passar o valor 0 para o parâmetro real correspondente ao parâmetro formaldecimals.

Eth_block Número

"eth_blockNumberé utilizado para consultar a altura do bloco.

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

Execute a ferramenta de depuração:

img

Procure emhttps://etherscan.io/:

img

eth_getBlockByNumber

Informações do bloco de consulta.

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)
    
    // Due to the excessive content of Log output, it will be truncated automatically, so traverse each field of the returned block information and print them one by one
    for (let key in blockMsg) {
        Log("key:", key, ", val:", blockMsg[key])
    }
}

Execução na Debugging Tool pode obter as seguintes informações:

img

Leia as informações do contrato

Muitos aplicativos de contratos inteligentes são executados no Ethereum, eENSé um deles.ENS, ou Ethereum Name Service, é um serviço descentralizado de resolução de nomes de domínio baseado no blockchain Ethereum. Você se lembra do exemplo no tutorial onde verificamos o saldo da carteira do fundador do Ethereum, Vitalik Buterin?0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045Como é que sabemos o endereço?ENScontrato inteligente usando um nome intuitivovitalik.eth.

O seguinte conteúdo neste capítulo utiliza o ambiente da rede principal Ethereum, de acordo com oENSdocumentação,Hashing Namessão necessários para consultar nomes de domínio Ethereum. Use o seguinte código para processarvitalik.eth.

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

No exemplo de código acima, vimos outra função desconhecidaEncodeEsta função é uma função API da plataforma FMZ e é especificamente usada para codificar operações na plataforma FMZ. A função suporta vários métodos de codificação e vários algoritmos de hash.

Encode(algo, inputFormat, outputFormat, data, keyFormat, key string)

De acordo com a descrição do documento ENS, utilize osha3.keccak256Algoritmo para processar dados.

Ligue para onameHashfunção, por exemplo:Log(nameHash("vitalik.eth")), pode obter:ee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835, e você precisa adicionar o prefixo 0x.0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835é utilizado como parâmetro doresolvermétodo no contrato inteligente do ENS.

let ensNode = "0x" + nameHash("vitalik.eth")    // Prepare the parameters ensNode for calling the resolver method

De acordo com a documentação ENS, o endereço do contrato para as aplicações de contratos inteligentes ENS é:0x00000000000C2E074eC69A0dFb2997BA6C7d2e1eAntes de ligar para oresolverO processo de criação de um contrato inteligente, também precisamos preparar oABIdo contrato.

Registro ABI

Após saberem isto, podem perguntar:ABIde um contrato inteligente?

ABI, or Application Binary Interface, is the interface standard for smart contracts to communicate with the external world.
The ABI of a smart contract defines the contract's function interfaces, parameter types, return values, and other information, as well as specifications for calling the contract and passing parameters.

The ABI of a smart contract is usually stored in JSON format and contains the following information:

Contract function interfaces: function names, parameter lists, return values, etc.
Function parameter types: such as uint256, bool, string etc.
Encoding methods for input and output parameters of functions: Smart contracts use an encoding method called Solidity ABI to encode input and output parameters of functions so that they can interact with Ethereum network.
In Ethereum network ,the ABI of a smart contract is used to call its functions. When you need to call a contract function, you need to provide the name of the function, its parameters, and bytecode encoded according to ABI encoding method.
Ethereum nodes will package this information into transactions and send them out on Ethereum network for execution.

In Solidity language,the keyword 'interface' can be used define ABIs for smart contracts. Ethereum development tools like Remix IDE ,Truffle also provide editing & generation tools making it easier developers create & use ABIs.

Extrair oresolverO método pode ser executado através de uma parte do ABI do ENS, ou pode utilizar o ABI completo.https://etherscan.io/ou obter o ABI através de outros canais (por exemplo, documentação do projecto relevante).

img

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"}]`

Aqui vamos aprender um novo método de invocação na plataforma FMZ,exchange.IO("abi", address, abiContent), que é utilizado para registar o ABI.addressParâmetro é o endereço do contrato inteligente e oabiContentParâmetro é o contrato inteligente correspondente ABI (string).

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 is the address of the ENS smart contract deployed on the Ethereum mainnet

Métodos de chamada de contratos inteligentes

Em seguida, pode ligar para oresolvermétodo do contrato inteligente ENS, que retorna o endereço doENS: Public Resolver contract.

img

let resolverAddress = exchange.IO("api", "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", "resolver", ensNode)

Utilize oENS: Public ResolverContratosaddrPara obter o endereço da carteira de Vitalik Buterin.ENS: Public ResolverA informação do ABI para este contrato inteligente ainda pode ser obtida a partir dehttps://etherscan.io/.

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)

img

Finalmente, chame oENS: Public ResolverContratosaddrmétodo, com o parâmetro ainda sendoensNode.

let vitalikAddress = exchange.IO("api", resolverAddress, "addr", ensNode)
Log("vitalikAddress:", vitalikAddress)

Resultado da função de registo:

vitalikAddress: 0xd8da6bf26964af9d7eed9e03e53415d37aa96045

Chame o código completo do ENS

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() {
    // Calculate the name
    let ensNode = "0x" + nameHash("vitalik.eth")    

    // Register ENS contract
    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)
    
    // Register ENS Public Resolver contract
    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)
}

Enviar ETH

Nos capítulos anteriores do curso, aprendemos como configurar chaves privadas. Como sabemos o endereço da carteira correspondente a esta chave privada para o objeto de troca configurado?exchange.IO("address")função para obter o endereço da carteira correspondente à chave privada configurada.

O seguinte conteúdo neste capítulo usa o ambiente Goerli testnet, então o nó que estou usando é:https://goerli.infura.io/v3/*******, e o Infura atribui endereços de nó diferentes para cada utilizador registado.*******Esconde conteúdo específico.

function main() {
    let walletAddress = exchange.IO("address")
    Log("Testnet goerli wallet address:", walletAddress)
}

Depois de saber o endereço da sua carteira, você pode usar o método RPC do Ethereumeth_getTransactionCountEm Ethereum, esta contagem é muito comum, e é na verdade ononceNo Ethereum, o nonce é um número único usado para garantir que cada transação seja única. É um número crescente e aumentará automaticamente a cada vez que uma nova transação for enviada. Portanto, quando você envia uma transação para um contrato inteligente, você precisa fornecer um nonce para garantir que a transação seja única e na ordem correta.

https://goethereumbook.org/en/

img

Aqui, oPendingNonceAtfunção na biblioteca Ethereum da linguagem Go está realmente chamando oeth_getTransactionCountEm cursos anteriores, nós também aprendemos como chamar métodos RPC.exchange.IO("api", "eth", ...)funcionam novamente.

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}

function main() {
    let walletAddress = exchange.IO("address")
    Log("Testnet goerli wallet address:", 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: string = exchange.IO("api", "eth", "eth_getTransactionCount", walletAddress, "pending")
    Log("wallet address:", walletAddress, "current nonce:", nonce, ", convert to decimal:", toAmount(nonce, 0))
}

Antes de explicar a operação de transferência, vamos entender brevemente alguns conceitos. Ao transferir no Ethereum, uma certa quantidade de tokens ETH será consumida (como taxas de gás). A taxa de gás é determinada por dois parâmetros:

  • GasPrice

    No entanto, as taxas de gás na rede Ethereum sempre flutuam de acordo com a demanda do mercado e as taxas que os usuários estão dispostos a pagar, então escrever uma taxa fixa de gás no código às vezes não é uma escolha ideal.eth_gasPricemétodo que aprendemos antes, que pode obter o preço médio de gás.

  • GasLimit

    Uma transferência de Ether padrão tem um limite de gás de 21.000 unidades.

Após compreender os conceitos denonce, gasPrice, egasLimitFMZ fornece uma função de transferência muito simples e fácil de usar.

exchange.IO("api", "eth", "send", toAddress, toAmount)

Quando é utilizado para transferências, o terceiro parâmetro deexchange.IOé fixado como enviar, otoAddressParâmetro é o endereço que recebe ETH durante a transferência, etoAmounté o montante de ETH transferido.

Os parâmetrosnonce, gasPrice, egasLimitpodem todos utilizar os valores padrão do sistema obtidos automaticamente na FMZ.

exchange.IO("api", "eth", "send", toAddress, toAmount, {gasPrice: 5000000000, gasLimit: 21000, nonce: 100})

Em seguida, vamos transferir uma certa quantidade de ETH para um endereço específico na rede de teste goerli:

function toInnerAmount(s, decimals) {
    return (BigDecimal(s)*BigDecimal(Math.pow(10, decimals))).toFixed(0)
}

function main() {
    let walletAddress = exchange.IO("address")
    Log("Testnet goerli wallet address:", walletAddress)

    let ret = exchange.IO("api", "eth", "send", "0x4D75a08E870674E68cAE611f329A27f446A66813", toInnerAmount(0.01, 18))
    return ret // return Transaction Hash : 0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e
}

Porque a unidade de quantidade de transferência Ethereum éwei, uma função personalizadatoInnerAmountO valor de um valor de referência deve ser utilizado para processar o valor dewei units.

Hash de transação de consulta:0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734eemhttps://etherscan.io/.

img

Você também pode escrever código para consulta transferência hash0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e, utilizando oeth_getTransactionReceiptmétodo para consultas.

function main() {
    let transHash = "0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e"
    let info = exchange.IO("api", "eth", "eth_getTransactionReceipt", transHash)
    return info
}

Resultado da 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": []
}

Descrição correspondente a cada campo

blockHash - The hash value of the block where the transaction is located.
blockNumber - The block number of the block where the transaction is located, encoded in hexadecimal.
contractAddress - If it's a contract creation, the address of the contract; otherwise null.
cumulativeGasUsed - The total gas used when executing this transaction in the block.
effectiveGasPrice - Total base fee plus tip per unit of gas.
from - Sender's address.
gasUsed - Gas used by this specific transaction.
logs - Array of log objects generated by this transaction.
  address - Address that generated this log.
  topics - Data array with 0 to 4 indexed log parameters, each with 32 bytes. In Solidity, first topic is event signature hash (e.g., Deposit(address,bytes32,uint256)), unless you declare an event using anonymous specifier.
  data - Non-indexed parameters for logs with length of 32 bytes.
  blockNumber - The block number of the block where this log is located.
  transactionHash - Transaction hash at time when log was created. Null if pending state.
  transactionIndex - Index position during creation. Null if pending state.
  blockHash - The hash value for containing block.
  logIndex - Hexadecimal-encoded integer index position within containing block. Null if pending state.
  removed - True if deleted due to chain reorganization; false for valid logs.
logsBloom - Bloom filter for retrieving related logs.
status - Hexadecimal-encoded value either being '1' (success) or '0' (failure).
to - Receiving party's address; null for contract creation transactions.
transactionHash - The hash value associated with given transaction.
transactionIndex - Hexadecimal-encoded index position within its respective containing-block.
type - Type value.

Chame o Ethereum Smart Contract

No capítulo sobre Reading Contract Information, usamos um exemplo completo para chamar o método de contrato ENS implantado no Ethereum para obter o endereço da carteira de Vitalik Buterin.Readmétodos, e chamando estes métodos não requergas(lembrem-se do que falámos sobre gás antes?).Writemétodos de contratos inteligentes no Ethereum e pagar porgasEstas operações serão verificadas por cada nó e minerador em toda a rede e mudar o estado blockchain.

ERC20

Para o contrato ERC20 (contrato de token ERC20), a plataforma FMZ lista o ABI do contrato ERC20 ABI como um ABI comum integrado diretamente no sistema, eliminando a etapa de registro do ABI.

Para entender melhor o ABI, você pode verificá-lo antes de usá-lo.

[{"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":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"tran

Relacionados

Mais.