<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: YD</title>
    <description>The latest articles on DEV Community by YD (@x777).</description>
    <link>https://dev.to/x777</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F313940%2F3b832cd4-2296-40b9-8d0b-9d04f25c5b1c.png</url>
      <title>DEV Community: YD</title>
      <link>https://dev.to/x777</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/x777"/>
    <language>en</language>
    <item>
      <title>Хакатон Nervos - Broaden the Spectrum. Трансформация.</title>
      <dc:creator>YD</dc:creator>
      <pubDate>Sat, 21 Aug 2021 01:37:19 +0000</pubDate>
      <link>https://dev.to/x777/nervos-broaden-the-spectrum-m4p</link>
      <guid>https://dev.to/x777/nervos-broaden-the-spectrum-m4p</guid>
      <description>&lt;p&gt;В рамках Хакатона на площадке Gitcoin, нужно описать процедуру трансформации или переноса Ethereum приложения на Nervos Network Layer 2.&lt;/p&gt;

&lt;p&gt;В одном посте вряд-ли получится описать процесс в деталях, особенно, если он похож на матрешку. Поэтому, настоятельно рекомендую ориентироваться по данному репозиторию:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Kuzirashi/gw-gitcoin-instruction"&gt;https://github.com/Kuzirashi/gw-gitcoin-instruction&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Я не буду сразу переходить к процессу переносу, так как считаю нужным хотя бы чуток объяснить как происходит работа со смарт-контрактами на базе Nervos Network Layer 2.&lt;/p&gt;

&lt;p&gt;Первое, что нужно сделать это клонировать учебный репозиторий Хакатона, в котором лежат все нужные нам скрипты.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git clone https://github.com/kuzirashi/gw-gitcoin-instruction&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;И устанавливаем все нужные зависимости:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn install-all&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Работаем со смарт-контрактами - компиляция и развертывание на блокчейне Nervos Layer 2.
&lt;/h3&gt;

&lt;p&gt;Для начала, неможко гугл-транслейта документации:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Polyjuice - это среда исполнения, совместимая с Ethereum EVM, которая позволяет запускать смарт-контракты на основе Solidity на Nervos. Цель проекта - 100% совместимость, позволяющая всем контрактам Ethereum работать на Nervos без каких-либо изменений.&lt;/p&gt;

&lt;p&gt;Polyjuice разработан для использования со сводной структурой Godwoken Layer 2. Это позволяет Polyjuice полностью перенести выполнение смарт-контрактов с уровня 1 на уровень 2, обеспечивая масштабируемость, выходящую далеко за рамки того, на что сегодня способна основная сеть Ethereum.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Другими словами, команда Nervos Network постаралась(и дальше старается), сделать так, чтобы смарт-контракты Ethereum (Solidity) были 100% совместимы с их блокчейном. Так что, если у нас есть готовый смарт-контракт в файле .sol, поидее, не должно возникнуть проблем чтобы развернуть его на блокчейне Nervos. Чем мы дальше и займемся. &lt;/p&gt;

&lt;p&gt;Рабочая папка для нас сейчас это &lt;code&gt;src/examples/2-deploy-contract/&lt;/code&gt; клонированного выше репозитория. Там же, в папке &lt;code&gt;contracts&lt;/code&gt; и лежит файл с простейшим смарт-контрактом &lt;code&gt;SimpleStorage.sol&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma solidity &amp;gt;=0.8.0;

contract SimpleStorage {
  uint storedData;

  constructor() payable {
    storedData = 123;
  }

  function set(uint x) public payable {
    storedData = x;
  }

  function get() public view returns (uint) {
    return storedData;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Чтобы скомпилировать этот файл, запускаем:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn compile&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;В результате, у нас должен появиться в папке &lt;code&gt;build/contracts/&lt;/code&gt; новый файл &lt;code&gt;SimpleStorage.json&lt;/code&gt; скомпилированный truffle в EVM байткод (тут я не буду вдаваться в детали). &lt;/p&gt;

&lt;p&gt;Для того, чтобы развернуть смарт-контракт на новом блокчейне находим в текущей рабочей папке файл &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { existsSync } = require('fs');
const Web3 = require('web3');
const { PolyjuiceHttpProvider, PolyjuiceAccounts } = require("@polyjuice-provider/web3");

const contractName = process.argv.slice(2)[0];

if (!contractName) {
    throw new Error(`No compiled contract specified to deploy. Please put it in "src/examples/2-deploy-contract/build/contracts" directory and provide its name as an argument to this program, eg.: "node index.js SimpleStorage.json"`);
}

let compiledContractArtifact = null;
const filenames = [`./build/contracts/${contractName}`, `./${contractName}`];
for(const filename of filenames)
{
    if(existsSync(filename))
    {
        console.log(`Found file: ${filename}`);
        compiledContractArtifact = require(filename);
        break;
    }
    else
        console.log(`Checking for file: ${filename}`);
}

if(compiledContractArtifact === null)
    throw new Error(`Unable to find contract file: ${contractName}`);

const DEPLOYER_PRIVATE_KEY = 'ETHEREUM PRIVATE KEY'; // Replace this with your Ethereum private key with funds on Layer 2.

const GODWOKEN_RPC_URL = 'http://godwoken-testnet-web3-rpc.ckbapp.dev';

const polyjuiceConfig = {
    rollupTypeHash: '0x4cc2e6526204ae6a2e8fcf12f7ad472f41a1606d5b9624beebd215d780809f6a',
    ethAccountLockCodeHash: '0xdeec13a7b8e100579541384ccaf4b5223733e4a5483c3aec95ddc4c1d5ea5b22',
    web3Url: GODWOKEN_RPC_URL
};

const provider = new PolyjuiceHttpProvider(
    GODWOKEN_RPC_URL,
    polyjuiceConfig,
);

const web3 = new Web3(provider);

web3.eth.accounts = new PolyjuiceAccounts(polyjuiceConfig);
const deployerAccount = web3.eth.accounts.wallet.add(DEPLOYER_PRIVATE_KEY);
web3.eth.Contract.setProvider(provider, web3.eth.accounts);

(async () =&amp;gt; {
    const balance = BigInt(await web3.eth.getBalance(deployerAccount.address));

    if (balance === 0n) {
        console.log(`Insufficient balance. Can't deploy contract. Please deposit funds to your Ethereum address: ${deployerAccount.address}`);
        return;
    }

    console.log(`Deploying contract...`);

    const deployTx = new web3.eth.Contract(compiledContractArtifact.abi).deploy({
        data: getBytecodeFromArtifact(compiledContractArtifact),
        arguments: []
    }).send({
        from: deployerAccount.address,
        to: '0x' + new Array(40).fill(0).join(''),
        gas: 6000000,
        gasPrice: '0',
    });

    deployTx.on('transactionHash', hash =&amp;gt; console.log(`Transaction hash: ${hash}`));

    const receipt = await deployTx;

    console.log(`Deployed contract address: ${receipt.contractAddress}`);
})();

function getBytecodeFromArtifact(contractArtifact) {
    return contractArtifact.bytecode || contractArtifact.data?.bytecode?.object
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Этот скрипт принимает в качестве параметра название файла смарт-контракта, в нашем случае это &lt;code&gt;SimpleStorage.json&lt;/code&gt;. Также, импортирует все необходимые библиотеки для работы с Web3 и Polyjuice.&lt;/p&gt;

&lt;p&gt;Ну и самое основное, это нужно указать PRIVATE KEY вашего Ethereum аккаунта, от имени которого мы хотим развернуть наш смарт-контракт:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const DEPLOYER_PRIVATE_KEY = 'ETHEREUM PRIVATE KEY'; // Replace this with your Ethereum private key with funds on Layer 2.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Как экспортировать ключ от своего Ethereum аккаунта можно почитать &lt;a href="https://github.com/Kuzirashi/gw-gitcoin-instruction/blob/master/src/component-tutorials/5.extract.ethereum.private.key.md"&gt;здесь&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Теперь кульминация. Запускаем:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;node index.js SimpleStorage.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;В результате мы получаем &lt;code&gt;Deployed contract address&lt;/code&gt;, который  мы можем использовать вместе с файлом скомпилированного ранее контракта. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Важный момент - чтобы развернуть приложение на блокчейне нужен Godwoken Layer 2 аккаунт. Как его сделать и пополнить почитать можно &lt;a href="https://github.com/Kuzirashi/gw-gitcoin-instruction/blob/master/src/component-tutorials/4.layer2.deposit.md"&gt;здесь&lt;/a&gt;.&lt;br&gt;
Я специально упускаю эти шаги, так как мне пришлось бы описывать весь Хакатон...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Итак, самый важный вывод - таким способом мы можем использовать любой Ethereum смарт-контракт. Но это еще не все - теперь нужно научиться использовать этот смарт-контракт в нашем приложении.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Использование смарт-контракта
&lt;/h4&gt;

&lt;p&gt;Теперь переходим в нашу новую рабочую папку &lt;code&gt;gw-gitcoin-instruction/src/examples/3-call-contract&lt;/code&gt;, в которой лежит &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Web3 = require('web3');
const { PolyjuiceHttpProvider, PolyjuiceAccounts } = require("@polyjuice-provider/web3");

const ACCOUNT_PRIVATE_KEY = ''; // Replace this with your Ethereum private key with funds on Layer 2.

const CONTRACT_ABI = [
    {
      "inputs": [],
      "stateMutability": "payable",
      "type": "constructor"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "x",
          "type": "uint256"
        }
      ],
      "name": "set",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "get",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    }
  ]; // this should be an Array []

const CONTRACT_ADDRESS = ''; // Deployed contract address

const GODWOKEN_RPC_URL = 'http://godwoken-testnet-web3-rpc.ckbapp.dev';

const polyjuiceConfig = {
    rollupTypeHash: '0x4cc2e6526204ae6a2e8fcf12f7ad472f41a1606d5b9624beebd215d780809f6a',
    ethAccountLockCodeHash: '0xdeec13a7b8e100579541384ccaf4b5223733e4a5483c3aec95ddc4c1d5ea5b22',
    web3Url: GODWOKEN_RPC_URL
};

const provider = new PolyjuiceHttpProvider(
    GODWOKEN_RPC_URL,
    polyjuiceConfig,
);

const web3 = new Web3(provider);

web3.eth.accounts = new PolyjuiceAccounts(polyjuiceConfig);
const account = web3.eth.accounts.wallet.add(ACCOUNT_PRIVATE_KEY);
web3.eth.Contract.setProvider(provider, web3.eth.accounts);

async function readCall() {
    const contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);

    const callResult = await contract.methods.get().call({
        from: account.address
    });

    console.log(`Read call result: ${callResult}`);
}

async function writeCall() {
    const contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);

    const tx = contract.methods.set(10).send(
        {
            from: account.address,
            to: '0x' + new Array(40).fill(0).join(''),
            gas: 6000000,
            gasPrice: '100',
        }
    );

    tx.on('transactionHash', hash =&amp;gt; console.log(`Write call transaction hash: ${hash}`));

    const receipt = await tx;

    console.log('Write call transaction receipt: ', receipt);
}

(async () =&amp;gt; {
    const balance = BigInt(await web3.eth.getBalance(account.address));

    if (balance === 0n) {
        console.log(`Insufficient balance. Can't issue a smart contract call. Please deposit funds to your Ethereum address: ${account.address}`);
        return;
    }

    console.log('Calling contract...');

    // Check smart contract state before state change.
    await readCall();

    // Change smart contract state.
    await writeCall();

    // Check smart contract state after state change.
    await readCall();
})();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В этом скрипте вначале мы также указываем ключ нашего Ethereum аккаунта. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;const CONTRACT_ABI = []&lt;/code&gt; - это объект &lt;code&gt;ABI&lt;/code&gt; нашего смарт-контракта. Мы его просто копируем с файла .json, и вставляем как именно массив.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const CONTRACT_ADDRESS = ''&lt;/code&gt; - это адресс смарт-контракта развернутого в предыдущем шаге.&lt;/p&gt;

&lt;p&gt;Следующий блок кода создает PolyjuiceHttpProvider с константными параметрами, который потом мы передаем Web3. Именно здесь начинается магия и приложение начинает работать с Nervos Layer 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const provider = new PolyjuiceHttpProvider(
    GODWOKEN_RPC_URL,
    polyjuiceConfig,
);
const web3 = new Web3(provider);
web3.eth.accounts = new PolyjuiceAccounts(polyjuiceConfig);
const account = web3.eth.accounts.wallet.add(ACCOUNT_PRIVATE_KEY);
web3.eth.Contract.setProvider(provider, web3.eth.accounts);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ну, а теперь давайте наконец-то поработаем с нашим смарт-контрактом. Для этого нам нужно вызвать те функции, которые в нем прописаны (если забыли, просьба вернуться к исходному коду нашего контракта):&lt;/p&gt;

&lt;p&gt;Следующий блок кода вызывает функцию &lt;code&gt;get()&lt;/code&gt; смарт-контракта, которая читает данные:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function readCall() {
    const contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);

    const callResult = await contract.methods.get().call({
        from: account.address
    });

    console.log(`Read call result: ${callResult}`);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;А здесь мы вызываем функцию set(), которая принимает параметр (смотреть исходный код контракта), а значит мы записываем данные. Для этого в конце, вместо &lt;code&gt;call()&lt;/code&gt; уже используется &lt;code&gt;send()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function writeCall() {
    const contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);

    const tx = contract.methods.set(10).send(
        {
            from: account.address,
            to: '0x' + new Array(40).fill(0).join(''),
            gas: 6000000,
            gasPrice: '100',
        }
    );

    tx.on('transactionHash', hash =&amp;gt; console.log(`Write call transaction hash: ${hash}`));

    const receipt = await tx;

    console.log('Write call transaction receipt: ', receipt);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Запускам &lt;code&gt;node index.js&lt;/code&gt; и результат:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GWyAGbS2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8q98bc1x19bo7m00e7b9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GWyAGbS2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8q98bc1x19bo7m00e7b9.png" alt="Call contract"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Вот теперь, мы научились использовать наш Ethereum смарт-контракт, который был развернут на Layer 2 Nervos Network c помощью Polyjuice&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Перенос Ethereum dapp приложения на Polyjuice
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;То, ради чего этот пост был написан.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;И так, первое что нужно сделать это выбрать любое Ethereum приложение. Я выбрал &lt;a href="https://github.com/AndrewJBateman/blockchain-ethereum-contract"&gt;"Список задач" с этого репозитория&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Клонируем:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git clone https://github.com/AndrewJBateman/blockchain-ethereum-contract&lt;/code&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Добавление новой сети в MetaMask.
&lt;/h5&gt;

&lt;p&gt;Для того, чтобы наше приложение могло общаться с новым блокчейном, нам необходима специальная сеть. &lt;br&gt;
Чтобы добавить новую сеть, нажимаем на значок расширения, дальше во избежания исчезнования окошка, нажимите на вертикальное троиточие и выбирете Expand View, чтобы открыть его в отдельном окне. Дальше, нажимаем на список Networks, и выбираем Custom RPC.  &lt;/p&gt;

&lt;p&gt;Вводим необходимые параметры для нашей сети:&lt;/p&gt;

&lt;p&gt;Network Name: &lt;code&gt;Godwoken Test Network&lt;/code&gt;&lt;br&gt;
New RPC URL: &lt;code&gt;https://godwoken-testnet-web3-rpc.ckbapp.dev/&lt;/code&gt;&lt;br&gt;
Chain ID: &lt;code&gt;71393&lt;/code&gt;&lt;br&gt;
Currency Symbol (optional): &lt;code&gt;N/A&lt;/code&gt;&lt;br&gt;
Block Explorer URL (optional): &lt;code&gt;N/A&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Нажимаем Save. Выглядеть это должно вот так:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5CbTujnq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f6xgc2946twiypqqu7nx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5CbTujnq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f6xgc2946twiypqqu7nx.png" alt="Godwoken Test Network"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Развертывание смарт-контракта
&lt;/h5&gt;

&lt;p&gt;Выбранное приложение имеет один смарт-контракт - 'TasksContract.sol', который необходимо скомпилировать и развернуть. &lt;/p&gt;

&lt;p&gt;Здесь есть 3 варианта:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Первый. Использование нашего учебного репозитория. Просто копируем в папку &lt;code&gt;contracts&lt;/code&gt; &lt;code&gt;.json&lt;/code&gt; файл контракта и запускаем те же команды описанные выше. На выходе нам нужно получить адрес по которому был развернут смарт-контракт, а также забрать &lt;code&gt;.json&lt;/code&gt; файл с папки &lt;code&gt;build/contracts/&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Второй. Использование команды &lt;code&gt;truffle migrate --network godwoken&lt;/code&gt;. &lt;br&gt;
Файл настроек сети и Polyjuice провайдера лежит тут -  &lt;a href="https://github.com/x777/todo-dapp-polyjuice/blob/main/truffle-config.js"&gt;&lt;code&gt;truffle-config.js&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
Этот config файл использует файл &lt;code&gt;.env&lt;/code&gt; с такими параметрами:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRIVATE_KEY='' - Ethereum ключ
WEB3_PROVIDER_URL='https://godwoken-testnet-web3-rpc.ckbapp.dev'
ROLLUP_TYPE_HASH='0x4cc2e6526204ae6a2e8fcf12f7ad472f41a1606d5b9624beebd215d780809f6a'
ETH_ACCOUNT_LOCK_CODE_HASH='0xdeec13a7b8e100579541384ccaf4b5223733e4a5483c3aec95ddc4c1d5ea5b22'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Замечение: этот способ требует знание truffle и неможко танцев с бубном...&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Третий. Это развертывание смарт-контракта внутри самого приложения - просто добавляется проверка адреса и если по этому адресу контракт не найден, тогда скрипт выполняет код, который выполняет этот процесс показанный в примере.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;
  
  
  Добавляем Polyjuice провайдер
&lt;/h5&gt;

&lt;p&gt;Так как выбранное мною приложение не использует никакой фронтенд фреймворк, подключаем в &lt;code&gt;client/index.html&lt;/code&gt; нужные с библиотеки помощью &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="/libs/web3/dist/web3.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src="/libs/@truffle/contract/dist/truffle-contract.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src="./nervos-godwoken-integration.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src="./polyjuice.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Не знаю почему, но мой Nodejs отказался работать с require(), поэтому я использовал утилиту &lt;a href="https://browserify.org/"&gt;&lt;code&gt;browserify&lt;/code&gt;&lt;/a&gt;, чтобы "запаковать" главный файл &lt;code&gt;client/app.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Следующим шагом, для удобства, создаем файл &lt;a href="https://github.com/x777/todo-dapp-polyjuice/blob/main/client/config.js"&gt;&lt;code&gt;client/config.js&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const CONFIG = {
    WEB3_PROVIDER_URL: 'https://godwoken-testnet-web3-rpc.ckbapp.dev',
    ROLLUP_TYPE_HASH: '0x4cc2e6526204ae6a2e8fcf12f7ad472f41a1606d5b9624beebd215d780809f6a',
    ETH_ACCOUNT_LOCK_CODE_HASH: '0xdeec13a7b8e100579541384ccaf4b5223733e4a5483c3aec95ddc4c1d5ea5b22',
    DEFAULT_SEND_OPTIONS: {
        gas: 6000000
    },
    CONTRACT_ADDRESS: '0x57a4d44de477A569766Ab934Fff38281d034f3EF',
    SUDT_ERC20_PROXY_ADDRESS: '0xD5A6a78E967cd70C6791d5289B3E4b1D5D55eC27'
};

module.exports = {CONFIG}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Почти все параметры уже были использованны нами в учебном репозитории, это предустановленные настройки которые нужны для Polyjuice провайдера. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;CONTRACT_ADDRESS&lt;/code&gt; - это адрес нашего смарт-контракта, который был развернут в предыдущем шаге.&lt;br&gt;
&lt;code&gt;SUDT_ERC20_PROXY_ADDRESS&lt;/code&gt; - это адрес отдельного смарт-контракта, который используется для отображения нужного SUDT (токена). Более подробно о SUDT можно почитать &lt;a href="https://github.com/Kuzirashi/gw-gitcoin-instruction/blob/master/src/tasks/4.issue.sudt.deposit.md"&gt;тут&lt;/a&gt; и &lt;a href="https://github.com/Kuzirashi/gw-gitcoin-instruction/blob/master/src/tasks/6.use.force.bridge.to.deposit.md"&gt;тут&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Далее, переходим в файл &lt;a href="https://github.com/x777/todo-dapp-polyjuice/blob/main/client/app.js"&gt;&lt;code&gt;client/app.js&lt;/code&gt;&lt;/a&gt; и подключаем наш &lt;code&gt;config.js&lt;/code&gt; и добавляем Polyjuice провайдер:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loadWeb3: async () =&amp;gt; {
        if (web3) {
            // Polyjuice provider config
            const providerConfig = {
                rollupTypeHash: CONFIG.ROLLUP_TYPE_HASH,
                ethAccountLockCodeHash: CONFIG.ETH_ACCOUNT_LOCK_CODE_HASH,
                web3Url: CONFIG.WEB3_PROVIDER_URL
                };

            // Polyjuice provider
            App.web3Provider = new PolyjuiceHttpProvider(CONFIG.WEB3_PROVIDER_URL, providerConfig);
            web3 = new Web3(App.web3Provider);
        } else {
            alert("To use this DAPP you need setup godwoken network in your Metamask")
            console.log("No ethereum browser is installed. Try installing MetaMask");
        }

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Загружаем смарт-контракты:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// load already deployed contract by address, 
    // to deploy use: truffle migrate --network godwoken
    // 
    loadContract: async () =&amp;gt; {
        try {
            const res = await fetch("TasksContract.json");
            const tasksContractJSON = await res.json();
            // Use CONFIG for contracts address
            App.tasksContract = new web3.eth.Contract(tasksContractJSON.abi, CONFIG.CONTRACT_ADDRESS);


            console.log('Task contract loaded:', App.tasksContract)
        } catch (error) {
            alert('Contract not found. Please deploy before continue')
            console.error('Cannot find deployed contract:', error);
        }
    },

    loadSudtContract: async () =&amp;gt; {
        try {
            const res = await fetch("ERC20.json");
            const sudtContract = await res.json();
            // Use CONFIG for contracts address
            App.sudtContract = new web3.eth.Contract(sudtContract.abi, CONFIG.SUDT_ERC20_PROXY_ADDRESS);

            console.log('SUDT contract loaded:', App.sudtContract)
        } catch (error) {
            alert('Contract not found. Please deploy before continue')
            console.error('Cannot find deployed contract:', error);
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Отображение баланса аккаунтов:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // render account balance as inner text
    renderBalance: async () =&amp;gt; {
        const addressTranslator = new AddressTranslator();
        const polyjuiceAddress = addressTranslator.ethAddressToGodwokenShortAddress(App.account);
        const ckETHAddress = await addressTranslator.getLayer2DepositAddress(web3, App.account);

        const urlDeposit = "https://force-bridge-test.ckbapp.dev/bridge/Ethereum/Nervos?recipient=" + ckETHAddress.addressString

        const ckbShn = BigInt(await web3.eth.getBalance(App.account));
        const ckbBalance = parseInt(BigInt(ckbShn / 10n ** 8n));

        const ckEth = BigInt(await App.sudtContract.methods.balanceOf(polyjuiceAddress).call({
            from: App.account
        }));

        ckEthBalance = parseInt(BigInt(ckEth / 10n**8n));

        document.getElementById("account").innerText = App.account;
        document.getElementById("polyjuice-account").innerText = polyjuiceAddress;
        document.getElementById("ck-eth").innerText = ckETHAddress.addressString;
        // document.getElementById("ckb-balance").innerText = ckbBalance + " $CKB";
        document.getElementById("do-deposit").href = urlDeposit
        document.getElementById("ckb-balance").innerText = ckbBalance + " $CKB";
        document.getElementById("cketh-balance").innerText = ckEthBalance + " $ckETH";

        //   Copy address
        document.querySelector("#do-deposit").addEventListener("click", function copy() {
            var copyText = document.querySelector("#ck-eth");
            var elementText = copyText.textContent;
            navigator.clipboard.writeText(elementText);
        });

        // console.log(await App.sudtContract.methods.balanceOf(polyjuiceAddress).call({
        //  from: App.account
        // }));
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Вызываем функцию смарт-контракта, которая выдает список всех задач и проходимся по каждой из них:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// render tasks
    renderTasks: async () =&amp;gt; {
        const taskCounter = await App.tasksContract.methods.taskCounter().call(
            {
                from: App.account,
                ...CONFIG.DEFAULT_SEND_OPTIONS ,
            }
        );
        const taskCounterNumber = parseInt(taskCounter);

        let html = "";

        for (let i = 1; i &amp;lt;= taskCounterNumber; i++) {
            const task = await App.tasksContract.methods.tasks(i).call({
                from: App.account,
                ...CONFIG.DEFAULT_SEND_OPTIONS ,
            });
            const taskId = parseInt(task[0]);
            const taskTitle = task[1];
            const taskDescription = task[2];
            const taskDone = task[3];
            const taskCreatedAt = task[4];

            // Creating a task Card
            let taskElement = `
        &amp;lt;div class="card bg-light rounded-0 mb-2"&amp;gt;
          &amp;lt;div class="card-header d-flex justify-content-between align-items-center"&amp;gt;
            &amp;lt;span&amp;gt;${taskTitle}&amp;lt;/span&amp;gt;
            &amp;lt;div class="form-check form-switch"&amp;gt;
              &amp;lt;input class="form-check-input" data-id="${taskId}" type="checkbox" onchange="App.toggleDone(this)" ${
                taskDone === true &amp;amp;&amp;amp; "checked"
            }&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="card-body"&amp;gt;
            &amp;lt;span&amp;gt;${taskDescription}&amp;lt;/span&amp;gt;

            &amp;lt;p class="text-muted"&amp;gt;Task was created ${new Date(
                            taskCreatedAt * 1000
                        ).toLocaleString()}&amp;lt;/p&amp;gt;
            &amp;lt;/label&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      `;
            html += taskElement;
        }

        document.querySelector("#tasksList").innerHTML = html;
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Вызываем функцию смарт-контракта, которая создает новую задачу:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;createTask: async (title, description) =&amp;gt; {
        try {
            const result = await App.tasksContract.methods.createTask(title, description).send({
                ...CONFIG.DEFAULT_SEND_OPTIONS,
                from: App.account,
            });
            alert("Transaction completed, page will be reload.")
            window.location.reload();
        } catch (error) {
            console.error(error);
        }
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Здесь важный момент использования &lt;code&gt;send()&lt;/code&gt; вместо &lt;code&gt;call()&lt;/code&gt;, о котором я упоминал выше в учебном примере.&lt;/p&gt;

&lt;p&gt;Ну, и последняя функция смарт-контракта, которая меняет статус задачи:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;toggleDone: async (element) =&amp;gt; {
        const taskId = element.dataset.id;
        console.log(taskId)
        await App.tasksContract.methods.toggleDone(taskId).send({
            ...CONFIG.DEFAULT_SEND_OPTIONS,
            from: App.account,
        });
        alert("Transaction completed, page will be reload.")
        window.location.reload();
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Также, используется &lt;code&gt;send()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;На этом процесс портирование (перенос) Ethereum dapp приложения закончен. В моем случае осталось только подправить макет единственной страницы &lt;code&gt;index.html&lt;/code&gt; и все, можно запускать &lt;code&gt;npm run dev&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Видео работающего приложения на &lt;a href="https://www.youtube.com/watch?v=0HOUiy4MVj0"&gt;YouTube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Скриншоты:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--icWWffgu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j3r9zr2s73af6ongr0l9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--icWWffgu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j3r9zr2s73af6ongr0l9.png" alt="Polyjuice App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oq64Xbi5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trsjhezll0ecb2u5lqil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oq64Xbi5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trsjhezll0ecb2u5lqil.png" alt="Polyjuice App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Ссылки:
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://github.com/AndrewJBateman/blockchain-ethereum-contract"&gt;Github оригинального Ethereum приложения&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/x777/todo-dapp-polyjuice"&gt;Github трансформированного приложения на Polyjuice&lt;/a&gt;&lt;br&gt;
&lt;a href="https://gitcoin.co/hackathon/nervos?"&gt;Хакатон Nervos - Broaden The Spectrum&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://teletype.in/@vladislav_vl25/nervosguideru"&gt;Знакомство с Nervos Network для новичков&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>ethereum</category>
      <category>nervos</category>
      <category>polijuice</category>
    </item>
    <item>
      <title>It's possible to find a mentor on Dev.to?</title>
      <dc:creator>YD</dc:creator>
      <pubDate>Tue, 03 Mar 2020 14:21:02 +0000</pubDate>
      <link>https://dev.to/x777/it-s-possible-to-find-a-mentor-on-dev-to-1afn</link>
      <guid>https://dev.to/x777/it-s-possible-to-find-a-mentor-on-dev-to-1afn</guid>
      <description>&lt;p&gt;I am looking for an experienced mentor in Golang for practical exercises. &lt;br&gt;
How do you think, it's possible to find anybody on Dev.to? May be someone have such experience? &lt;br&gt;
I have basic Golang level now, but I want grow and ready to "work for food" if it needed. Just notice, I am not begginer on web dev and have experience with fronend and backed.&lt;/p&gt;

</description>
      <category>go</category>
      <category>beginners</category>
      <category>discuss</category>
      <category>mentor</category>
    </item>
    <item>
      <title>How to survive junior Golang developer?</title>
      <dc:creator>YD</dc:creator>
      <pubDate>Sun, 26 Jan 2020 20:35:52 +0000</pubDate>
      <link>https://dev.to/x777/how-to-survive-junior-golang-developer-5d4c</link>
      <guid>https://dev.to/x777/how-to-survive-junior-golang-developer-5d4c</guid>
      <description>&lt;p&gt;Hi, for Gophers and everyone else!&lt;br&gt;
Hope, I am not alone in such question. How to be in demand in industry if you like write in Golang, but haven't enough experience. What I mean.&lt;br&gt;
Golang is cool language that can be useful for big company because it's solve problems of productivity. But for big clients (if you're frelancer) and for big companies, who really needed in Golang you'are not interested if you'are a "junior" in this language because that parts of project where Golang can be used very important and need be experienced engenieer to be useful. Same for small clients - you'are also not interested, because why use "ferrari in village". &lt;br&gt;
Who have similar cycle's problem, how you've broken this? &lt;/p&gt;

</description>
      <category>go</category>
      <category>beginners</category>
      <category>career</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Where best to study "Angular"? Advice please!</title>
      <dc:creator>YD</dc:creator>
      <pubDate>Fri, 10 Jan 2020 20:14:12 +0000</pubDate>
      <link>https://dev.to/x777/where-best-to-study-angular-advice-please-4np6</link>
      <guid>https://dev.to/x777/where-best-to-study-angular-advice-please-4np6</guid>
      <description>&lt;p&gt;Hi, everyone! I am looking for good courses, books, blogs, etc, for teaching systematic Angular framework. Preferred language English and Russian. Thanks for any advice!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
