Часть 2 - Подключение смарт-контракта к GraphQL
Вы можете найти часть 1 этого руководства здесь.
В этой части мы создадим сервер Express для нашего GraphQL API и подключим его к нашему смарт-контракту с помощью Web3 😎.
Настройка тестовых учетных записей
Перед настройкой нашего сервера установите ganache-cli и расширение для браузера Metamask.
Интерфейс командной строки Ganache является частью среды разработки Truffle для Ethereum и поможет нам создать несколько фиктивных учетных записей для тестирования нашего контракта.
Нам нужно расширение Metamask, потому что оно позволяет запускать приложения Ethereum dApps в обычном веб-браузере, а также действует как безопасное хранилище идентификационных данных.
Запустите команду ganache-cli
, чтобы сгенерировать несколько тестовых учетных записей и скопируйте данную нам фразу «Мнемоника» в терминал.
Теперь щелкните значок Metamask в своем браузере и подключите расширение Metamask к сети Localhost 8545.
Затем выберите «восстановить из исходной фразы», вставьте фразу, которую вы скопировали с терминала, и создайте новый пароль.
Если все пойдет хорошо, мы увидим, что у нас есть 100 эфиров, которые мы можем потратить!
Затем мы подключим Remix IDE, которую мы будем использовать для написания и тестирования нашего смарт-контракта, с Metamask, выбрав параметр Injected Web3
на вкладке RUN.
Чтобы развернуть разработанный контракт, добавьте строку приза и нажмите «Развернуть».
Настройка GraphQL API
Теперь мы создадим базовый сервер GraphQL с помощью Apollo. Давайте начнем. Создайте новый каталог для своего проекта и запустите в нем npm init -y
.
Теперь мы настроим базовую файловую структуру нашего сервера GraphQL. Создайте папки и файлы, как показано на изображении ниже:
donationContract.js
- это место, где будет жить JavaScript-версия нашего скомпилированного смарт-контракта. Внутри этого файла нам понадобятся две части информации из Remix: адрес развернутого контракта в тестовой цепочке блоков и нечто, называемое ABI.
Что такое ABI? Узнайте больше здесь.
Окончательный файл будет выглядеть примерно так:
const contractAddress = "0xd0a6E6C54DbC68Db5db3A091B171A77407Ff7ccf" const contractABI = [{ // Your ABI here... }]
Теперь, когда у нас есть адрес нашего недавно развернутого смарт-контракта Ethereum, нам нужен способ взаимодействия с ним нашего приложения JavaScript. К счастью, для этого у нас есть библиотека web.js! Установите web3.js, запустив:
npm install --save-dev web3
Откройте getContract.js
файл в src/contract
и добавьте этот код:
const Web3 = require("web3"); // Importing contract details const {address, ABI} = require("../../constants/donationContract"); const getWeb3 = () => { return (web3 = new Web3( new Web3.providers.HttpProvider("http://localhost:8545") )); }; const getContract = new Promise(function(resolve, reject) { const web3 = getWeb3(); // Create a JavaScript versions of your Smart Contract's interface // using the contract address and ABI we got from the Remix const donationContract = new web3.eth.Contract(ABI, address); if (donationContract) resolve(donationContract); else reject(); }); const getCoinbase = async () => { const web3 = getWeb3(); return await web3.eth.getCoinbase(); }; module.exports = { getContract, getCoinbase };
Вы можете узнать больше о coinbase и Web3 здесь и здесь.
Создание схемы GraphQL
Добавьте следующие определения типов GraphQL в файл yourschema.js
:
const typeDefs = `type Contract { address: String!, balance: Int } type DonationReceipt { transactionHash: String!, blockHash: String!, blockNumber: Int!, gasUsed: Int!, status: Boolean! } type Query { contract: Contract } type Mutation { donate(amount: Int!): DonationReceipt } `; module.exports = typeDefs;
Эта схема поможет нам проверить детали нашего контракта (баланс и адрес) и сделать пожертвования (в ответ мы получим квитанцию о пожертвовании).
Наконец, напишем наш сервер. Откройте свой терминал и установите эти пакеты в свой проект:
npm install --save-dev apollo-server-express body-parser express graphql graphql-tools
Добавьте следующий код в начало index.js
:
const express = require("express"); const bodyParser = require("body-parser"); const { graphqlExpress, graphiqlExpress } = require("apollo-server-express"); const { makeExecutableSchema } = require("graphql-tools"); const typeDefs = require("./schema/schema"); const { getContract, getCoinbase } = require("./contract/getContract");
Наш следующий шаг - создать сервер Express и написать преобразователь GraphQL, чтобы получить баланс нашего контракта.
Продолжить редактирование index.js
:
let contractInstance = {}; function getBalance() { return contractInstance.methods.checkContractBalance().call(); } const resolvers = { Query: { async contract() { let balance = 0; await getBalance() .then(bal => (balance = bal / 1000000000000000000)) // Convert to ether from wei .catch(err => console.log(err)); return { address: contractInstance._address, balance }; } } }; // Create the schema const schema = makeExecutableSchema({ typeDefs, resolvers }); // Initialize the app const app = express(); const PORT = 4000; // The GraphQL endpoint app.use("/graphql", bodyParser.json(), graphqlExpress({ schema })); // GraphiQL, a visual editor for graphql queries app.use("/graphiql", graphiqlExpress({ endpointURL: "/graphql" })); // Start the server and get contract instance app.listen(PORT, () => { console.log(`Go to http://localhost:${PORT}/graphiql to explore the GraphQL API!`); // get our contract as soon as the server starts getContract .then(res => (contractInstance = res)) .catch(err => console.log(err)); });
Если вы работали с GraphQL и Node.js, это должно быть вам знакомо.
Мы только что написали простой преобразователь с _11 _ / _ 12_, который возвращает адрес нашего развернутого смарт-контракта и его баланс!
Посетите http: // localhost: 4000 / graphiql, чтобы протестировать свой сервер GraphQL.
Если вы видите следующее, поздравляем! 🙌
А как насчет вызова? Давайте закончим написанием преобразователя GraphQL, чтобы мы могли делать пожертвования в наш новый контракт, используя мутацию GraphQL.
Для этого добавьте в index.js
следующую функцию:
// after our query resolver put this code Mutation: { async function makeDonation(amount) { let response; let coinbase; let error; await getCoinbase() .then(res => (coinbase = res)) .then(() => contractInstance.methods .make_donation("sending some either") .send({ gas: 300000, value: amount * 1000000000000000000, // wei conversion from: coinbase }) .then(res => (response = res)) ) .catch(err => (error = err)); return response; } } // Mutation end
Помните, что мы импортировали getCoinbase
из нашего getContract.js
файла. Мы используем его сейчас, чтобы получить Coinbase, а затем сделать пожертвование.
Теперь мы добавим этот код в наш преобразователь под нашей contract
функцией:
async donate(obj, args, context) { let transactionHash = ""; let blockHash = ""; let blockNumber = 0; let gasUsed = 0; let status = false; const response = makeDonation(args.amount); await response .then(res => { transactionHash = res.transactionHash; blockHash = res.blockHash; blockNumber = res.blockNumber; gasUsed = res.gasUsed; status = res.status; }) .catch(err => console.log(err)); return { transactionHash, blockHash, blockNumber, gasUsed, status }; }
Большой!
Вот файл finished index.js
на случай, если что-то не так.
Давайте проверим, работает ли новый резолвер. Перезагрузите сервер и запустите эту мутацию, чтобы сделать пожертвование. Вы должны увидеть квитанцию о пожертвовании следующим образом:
Чтобы убедиться, что все работает, проверьте баланс своего аккаунта в Metamask и баланс контракта через Remix. Все значения следует поменять!
Вы можете проверить это репозиторий GitHub, чтобы увидеть этот готовый проект.
Большое спасибо за кодирование вместе со мной в этом приложении GraphQL и смарт-контрактах. Надеюсь, вы кое-чему научились. Удачного кодирования!
Если вам понравился этот пост и вы хотите продолжить изучение разработки приложений и веб-приложений с помощью RED Academy, обязательно подпишитесь на нас на Medium или посетите наш веб-сайт.