В процессе загрузки ресурсов... загрузка...

Начните с разработки web3 на базе Ethereum с помощью FMZ

Автор:FMZ~Lydia, Создано: 2023-06-25 09:17:53, Обновлено: 2024-11-11 22:34:49

[TOC]

img

Начните с разработки web3 на базе Ethereum с помощью FMZ

Урок EtherEaseWithFMZ

Начните с разработки web3 на базе Ethereum с помощью FMZ легко

Ethereum - это платформа для смарт-контрактов, основанная на технологии блокчейн, которая обеспечивает децентрализованный способ написания и развертывания смарт-контрактов.

Платформа FMZ Quant Trading (FMZ.COMОн обеспечивает доступ к децентрализованным биржам (DEX), получает данные по цепочке, отправляет транзакции и другие функции.

В этом уроке примеры написаныJavaScriptязык, среда тестирования использует иОсновная сеть EthereumиПробная сеть Гёрли. И вы также можете просматривать интерфейсы API и связанные с ними описания, примеры кода, используемые в учебном пособии в документации API платформы FMZ.


Начало FMZ

Прежде чем научиться пользоваться платформой FMZ Quant Trading, мы должны ознакомиться с несколькими основными понятиями:

1. Архитектура квантовой торговой платформы FMZ

После регистрации и входа на официальный сайт FMZ Quant Trading Platform (https://www.fmz.comФМЗ-сайт является управляющим концом всей системы, и программы, написанные пользователем, фактически работают на докере. Программное обеспечение докера может быть развернуто на различных устройствах, таких как серверы, компьютеры и т. Д. Когда пользователь пишет программу и создает запущенный экземпляр на веб-сайте FMZ, платформа FMZ будет общаться с докером и запускать экземпляр программы на нем.

2. Докер

Если вы хотите запустить экземпляр программы, вы должны развернуть докер. Развертывание докера также очень просто, и на платформе есть учебники по развертыванию. Вы также можете использовать One-click Deployment, предоставленный FMZ, для автоматического развертывания на серверах, арендованных от имени FMZ.

  • Развернуть докер на персональных устройствах

Вы можете развернуть и запустить программу Docker на серверах, персональных компьютерах и других устройствах, если сеть работает нормально (необходимо иметь доступ к соответствующей цели, такой как определенный интерфейс обмена, адрес узла и т. Д.).

  1. Входите или откройте устройство, на котором будет развернута программа докера, например:вход на сервериливключение компьютера для входа в операционную систему.
  2. Скачать соответствующую версию программы Docker (в зависимости от операционной системы устройства), страницу загрузки:https://www.fmz.com/m/add-node

img

  1. То, что вы загрузили, это сжатый пакет, нужно распечатать.
  2. Запустите программу Docker, программа Docker - это исполняемый файл под названиемrobot. Конфигурируйте адрес связи докера, который является уникальным для каждой учетной записи FMZ, после входа в FMZ, вы можете просмотреть свой собственный адрес наhttps://www.fmz.com/m/add-nodeстраницы (т.е../robot -s node.fmz.com/xxxxxЭта последовательность адресов, где содержание наxxxxxПосле настройки этих настроек, запустите программу Docker.
  • Использовать функцию One-Click Deployment платформы FMZ

    Добавить докерную страницу на платформе FMZ, адрес:https://www.fmz.com/m/add-node

    img

3. Инструмент отладки ошибок

FMZ Quant Trading Platform предоставляет бесплатный инструмент отладки, который поддерживаетJavaScript, TypeScript, а страница:https://www.fmz.com/m/debug. Поскольку создание экземпляров для запуска взимается. В течение первоначального периода обучения вы можете использовать этот инструмент отладки для тестирования и обучения. За исключением максимального ограничения времени запуска 3 минуты, нет никакой разницы между использованием инструмента отладки и созданием экземпляра для запуска.

При использованииTypeScriptязык, вы должны писать// @ts-checkна первой строке кода перейти наTypeScriptрежим; если не переключен, по умолчаниюJavaScript language.

4. Платформы

В FMZ Platform является общим понятием. Для бирж CEX он относится к конкретной конфигурации учетной записи биржи. Для web3 этот обмен относится к конфигурационной информации, которая включает в себя адрес узла и конфигурацию частного ключа.

В состоянии входа на платформу FMZ, наhttps://www.fmz.com/m/add-platformстраницы, вы можете настроить обмен информацией, где обмен является общим понятием.

img

ВыберитеWeb3, настроить адрес узла RPC, настроить частный ключ, вы можете нажать в правом нижнем углу Серьезная информация будет храниться зашифрована для просмотра механизма безопасности.

Узлы могут быть самостоятельно построенными узлами или узлами, предоставляемыми поставщиками услуг узлов.Инфура. После регистрации вы можете просмотреть адрес узла вашего собственного аккаунта. И майннет, и тестнет доступны, что довольно удобно.Rpc AddressЭтикетка может быть названа самостоятельно, чтобы отличить между конфигурированными объектами обмена.

img

На фотографии,https://mainnet.infura.io/v3/xxxxxxxxxxxxxявляется частным адресом узла RPC майннета Infura ETH.


Взаимодействие с Ethereum с помощью FMZ

После развертывания программы Docker и настройки обмена объекта, вы можете использоватьFMZ.COMs Debugging Tool для тестирования. Назовите методы Ethereum RPC и взаимодействуйте с Ethereum, в дополнение к нескольким методам RPC, перечисленным и представленным в этой главе, другие методы RPC можно найти, ознакомившись с материалами, такими как:https://www.quicknode.com/docs.

Для различных языков и инструментов есть способы доступа к web3, как показано на рисунке:

img

На FMZ, вызовы методов RPC также инкапсулированы, и эти функции инкапсулированы в функции FMZ APIexchange.IOСпособ вызова:exchange.IO("api", "eth", ...). Первый параметр фиксирован на"api", второй параметр фиксирован на"eth", и другие параметры зависят от конкретного метода RPC, который вызывается.

Для вывода информации мы будем использоватьLogФункция платформы FMZ.LogФункция может принимать несколько параметров, а затем выводить их в регистр страницы Debug Tool или Bot на платформе FMZ. Страница Debug Tool будет нашим основным инструментом тестирования.

eth_getBalance

Вeth_getBalanceметод Ethereum используется для запроса баланса ETH адреса на Ethereum, и этот метод требует двух параметров.

  • Адрес для запроса.
  • На этикетке мы обычно используем последний. Давайте проверим основателя EthereumVitalik ButerinАдрес кошелька ETH, известный адрес:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045.
function main() {
    let ethBalance = exchange.IO("api", "eth", "eth_getBalance", "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest")
    Log("ethBalance:", ethBalance)
}

Уже развернутый докер (на рисунке: linux/amd64...) и сконфигурирован обменный объект (на рисунке: Web3 тест), тестирование кода в инструменте отладки:

img

Нажмите кнопку Execute, чтобы запустить код и отобразить результаты:

EthБалканс: 0x117296558f185bbc4c6

ВlogФункция распечатываетethBalanceпеременное значение:0x117296558f185bbc4c6, который является типом строки.шестнадцатеричное значение баланса ETHвweiединицы, с1e18 weiравняется 1ETHСледовательно, он должен быть преобразован в читаемый десятичный баланс ETH.

ПреобразованиеethBalanceв читаемые данные:

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

Продолжайте поискhttps://etherscan.io/:

img

Однако из-за проблемы точности самого языка будут отклонения в этом способе обработки.

  • BigInt: Преобразует шестерную строку в объект BigInt.
  • BigDecimal: преобразует объекты числового типа в вычислимые объекты BigDecimal.

Повторить код:

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

ВиталикЭтБалкан: 5149.6244846875215

eth_chainId

eth_chainIdиnet_versionОбе эти функции возвращают ID блокчейна, к которому подключен текущий узел RPC, с той разницей, чтоnet_versionвозвращает десятичный ID иeth_chainIdвозвращает шестнадцатеричный ID.

Наименование сети, соответствующее 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

Испытание с конфигурированной тестовой сетью Ethereumgoerliузел:

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

Позвониeth_gasPriceметод запроса текущегоgas 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, " , conversion:", toAmount(gasPrice, 0))
}

Здесь мы запишем функцию для преобразования шестой десятичной строки в читаемое числовое значение:toAmountКроме того, следует отметить, что единицаgasPriceэтоwei, поэтому передать значение 0 на реальный параметр, соответствующий формальный параметрdecimals.

eth_blockNumber (число блока)

"eth_blockNumberиспользуется для запроса высоты блока.

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

Запустить инструмент отладки:

img

Продолжайте поискhttps://etherscan.io/:

img

eth_getBlockByNumber

Запросить информацию о блоке.

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

Используя Debugging Tool можно получить следующую информацию:

img

Читать информацию о контракте

Многие смарт-контрактные приложения работают на Ethereum, иENSЭто один из них.ENS, или Ethereum Name Service, является децентрализованной службой разрешения доменных имен, основанной на блокчейне Ethereum. Помните пример из учебника, где мы проверяли баланс кошелька основателя Ethereum Виталика Бутерина?0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045Так как мы узнаем адрес? На самом деле, он может быть запрошен черезENSумный контракт с интуитивным названиемvitalik.eth.

Следующий контент в этой главе использует среду Ethereum mainnet, согласноENSдокументация,Hashing Namesиспользуйте следующий код для обработкиvitalik.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))
    }
}

В приведенном выше примере кода, мы увидели еще одну незнакомую функциюEncode. Эта функция является API-функцией платформы FMZ и специально используется для кодирования операций на платформе FMZ. Функция поддерживает несколько методов кодирования и различные хэш-алгоритмы.

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

Согласно описанию в ENS-документе, используйтеsha3.keccak256алгоритм для обработки данных.

ПозвониnameHashфункция, например:Log(nameHash("vitalik.eth")), вы можете получить:ee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835, и вам нужно добавить префикс 0x.0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835используется в качестве параметраresolverМетод в интеллектуальном контракте ENS.

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

Согласно документации ENS, адрес контракта для приложений интеллектуальных контрактов ENS:0x00000000000C2E074eC69A0dFb2997BA6C7d2e1eПрежде чем позвонитьresolverВ то же время, мы должны подготовитьABIДоговора.

Регистрировать ABI

Узнав об этом, вы можете спросить:ABIУмного контракта?

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.

ВытащитьresolverВы можете запросить ABI контракта наhttps://etherscan.io/или получить АБИ по другим каналам (например, соответствующей проектной документации).

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

Здесь мы узнаем новый метод вызова на платформе FMZ,exchange.IO("abi", address, abiContent), который используется для регистрации ABI.addressПараметр - это адрес смарт-контракта иabiContentпараметр - соответствующий умный контракт ABI (строка).

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

Методы вызова умных контрактов

Далее, вы можете позвонитьresolverметод интеллектуального контракта ENS, который возвращает адресENS: Public Resolver contract.

img

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

ИспользуйтеENS: Public ResolverКонтрактыaddrЧтобы получить адрес кошелька Виталия Бутерина.ENS: Public ResolverЕсли вы не можете получить информацию об ABI для этого смарт-контракта, вы все равно должны зарегистрировать ABI.https://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

Наконец, позвонитеENS: Public ResolverКонтрактыaddrметод, при этом параметр все ещеensNode.

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

Выход функции лога:

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() {
    // 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)
}

Отправить ETH

В предыдущих главах курса мы узнали, как настроить частные ключи. Как мы узнаем адрес кошелька, соответствующий этому частному ключу для конфигурированного объекта обмена?exchange.IO("address")функция получения адреса кошелька, соответствующего конфигурированному частному ключу.

Следующий контент в этой главе использует среду тестирования Goerli, поэтому я использую узел:https://goerli.infura.io/v3/*******, и Infura присваивает разные адреса узлов для каждого зарегистрированного пользователя.*******скрывает конкретный контент.

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

После того, как вы узнаете адрес вашего кошелька, вы можете использовать метод RPC Ethereumeth_getTransactionCountВ Ethereum, это очень распространено, и это на самом делеnonceПараметр, который необходимо передать во время операций по передаче. В Ethereum nonce - это уникальный номер, используемый для обеспечения уникальности каждой транзакции. Это увеличивающееся число, и оно автоматически увеличивается каждый раз, когда отправляется новая транзакция. Поэтому, когда вы отправляете транзакцию в смарт-контракт, вам нужно предоставить nonce, чтобы гарантировать, что транзакция уникальна и в правильном порядке. Мы можем найти эту информацию в некоторых материалах и документах:

https://goethereumbook.org/en/

img

Вот,PendingNonceAtФункция в библиотеке Ethereum языка Go на самом деле называетeth_getTransactionCountВ предыдущих курсах мы также узнали, как называть методы RPC.exchange.IO("api", "eth", ...)снова функционировать.

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

Прежде чем объяснить операцию перевода, давайте кратко разберемся с некоторыми понятиями. При переводе на Ethereum будет потреблено определенное количество токенов ETH (в виде сборов за газ). Сбор за газ определяется двумя параметрами:

  • Газовый

    Тем не менее, сборы за газ в сети Ethereum всегда колеблются в зависимости от спроса на рынке и сборов, которые пользователи готовы платить, поэтому написание фиксированной сборы за газ в коде иногда не является идеальным выбором.eth_gasPriceМетод, который мы выучили раньше, который может получить среднюю цену на газ.

  • Газовой лимит

    Стандартная передача эфира имеет газовый лимит 21000 единиц.

После понимания понятийnonce, gasPrice, иgasLimitFMZ предоставляет очень простую и удобную в использовании функцию передачи.

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

Когда он используется для переводов, третий параметрexchange.IOфиксируется как посылать,toAddressпараметр - адрес, получающий ETH во время перевода, иtoAmountявляется суммой перечисленных ETH.

Параметрыnonce, gasPrice, иgasLimitмогут все использовать системные значения по умолчанию, автоматически получаемые на FMZ. Они также могут быть указаны:

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

Далее мы переведем определенное количество ETH на определенный адрес в тестовой сети 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
}

Потому что единицей суммы перевода Ethereum являетсяwei, функция по заказуtoInnerAmountнеобходимо использовать для обработки значения вwei units.

Хэш транзакции запроса:0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734eнаhttps://etherscan.io/.

img

Вы также можете написать код для запроса передачи хэша0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e, используяeth_getTransactionReceiptметод для запросов.

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

Результат запроса:

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

Описание, соответствующее каждому поле:

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.

Вызов Ethereum Smart Contract

В главе о чтении контрактной информации мы использовали полный пример для вызова метода контракта ENS, развернутого на Ethereum, для получения адреса кошелька Виталика Бутерина.Readметоды, и вызов этих методов не требуетgas(помнишь, что мы говорили о газе раньше?).Writeметоды интеллектуальных контрактов на Ethereum и платить заgasЭти операции будут проверены каждым узлом и майнером в всей сети и изменят состояние блокчейна.

ERC20

Для контракта ERC20 (токен-контракт ERC20) платформа FMZ перечисляет ABI контракта ERC20 ABI как общий ABI, встроенный непосредственно в систему, исключая шаг регистрации ABI.

Чтобы лучше понять ABI, вы можете проверить его перед использованием.

[{"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

Связанные

Больше